diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..cc8b97a
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,206 @@
+# Remove the line below if you want to inherit .editorconfig settings from higher directories
+root = true
+
+# C# files
+[*.cs]
+
+#### Core EditorConfig Options ####
+
+# Indentation and spacing
+indent_size = 4
+indent_style = space
+tab_width = 4
+
+# New line preferences
+end_of_line = crlf
+insert_final_newline = false
+
+#### .NET Coding Conventions ####
+
+# Organize usings
+dotnet_separate_import_directive_groups = true
+dotnet_sort_system_directives_first = true
+
+# this. and Me. preferences
+dotnet_style_qualification_for_event = true:warning
+dotnet_style_qualification_for_field = true:warning
+dotnet_style_qualification_for_method = true:warning
+dotnet_style_qualification_for_property = true:warning
+
+# Language keywords vs BCL types preferences
+dotnet_style_predefined_type_for_locals_parameters_members = false:warning
+dotnet_style_predefined_type_for_member_access = false:warning
+
+# Parentheses preferences
+dotnet_style_parentheses_in_arithmetic_binary_operators = never_if_unnecessary:warning
+dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:warning
+dotnet_style_parentheses_in_other_operators = never_if_unnecessary:warning
+dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:warning
+
+# Modifier preferences
+dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent
+
+# Expression-level preferences
+csharp_style_deconstructed_variable_declaration = true:suggestion
+csharp_style_inlined_variable_declaration = true:warning
+csharp_style_throw_expression = true:warning
+dotnet_style_coalesce_expression = true:warning
+dotnet_style_collection_initializer = true:warning
+dotnet_style_explicit_tuple_names = true:warning
+dotnet_style_null_propagation = true:warning
+dotnet_style_object_initializer = false:suggestion
+dotnet_style_prefer_auto_properties = true:warning
+dotnet_style_prefer_compound_assignment = true:warning
+dotnet_style_prefer_conditional_expression_over_assignment = true:warning
+dotnet_style_prefer_conditional_expression_over_return = true:warning
+dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
+dotnet_style_prefer_inferred_tuple_names = true:suggestion
+dotnet_style_prefer_is_null_check_over_reference_equality_method = true:warning
+
+# Field preferences
+dotnet_style_readonly_field = true:warning
+
+# Parameter preferences
+dotnet_code_quality_unused_parameters = all:warning
+
+#### C# Coding Conventions ####
+
+# var preferences
+csharp_style_var_elsewhere = true:silent
+csharp_style_var_for_built_in_types = true:warning
+csharp_style_var_when_type_is_apparent = true:warning
+
+# Expression-bodied members
+csharp_style_expression_bodied_accessors = when_on_single_line:suggestion
+csharp_style_expression_bodied_constructors = when_on_single_line:suggestion
+csharp_style_expression_bodied_indexers = when_on_single_line:suggestion
+csharp_style_expression_bodied_lambdas = when_on_single_line:suggestion
+csharp_style_expression_bodied_local_functions = when_on_single_line:suggestion
+csharp_style_expression_bodied_methods = when_on_single_line:suggestion
+csharp_style_expression_bodied_operators = when_on_single_line:suggestion
+csharp_style_expression_bodied_properties = when_on_single_line:suggestion
+
+# Pattern matching preferences
+csharp_style_pattern_matching_over_as_with_null_check = true:warning
+csharp_style_pattern_matching_over_is_with_cast_check = true:warning
+
+# Null-checking preferences
+csharp_style_conditional_delegate_call = true:warning
+
+# Modifier preferences
+csharp_prefer_static_local_function = false:warning
+csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async
+
+# Code-block preferences
+csharp_prefer_braces = true:warning
+csharp_prefer_simple_using_statement = false:warning
+
+# Expression-level preferences
+csharp_prefer_simple_default_expression = true:warning
+csharp_style_pattern_local_over_anonymous_function = true:warning
+csharp_style_prefer_index_operator = true:suggestion
+csharp_style_prefer_range_operator = true:suggestion
+csharp_style_unused_value_assignment_preference = discard_variable:suggestion
+csharp_style_unused_value_expression_statement_preference = discard_variable:suggestion
+
+# 'using' directive preferences
+csharp_using_directive_placement = inside_namespace:warning
+
+#### C# Formatting Rules ####
+
+# New line preferences
+csharp_new_line_before_catch = true
+csharp_new_line_before_else = true
+csharp_new_line_before_finally = true
+csharp_new_line_before_members_in_anonymous_types = true
+csharp_new_line_before_members_in_object_initializers = true
+csharp_new_line_before_open_brace = all
+csharp_new_line_between_query_expression_clauses = true
+
+# Indentation preferences
+csharp_indent_block_contents = true
+csharp_indent_braces = false
+csharp_indent_case_contents = true
+csharp_indent_case_contents_when_block = false
+csharp_indent_labels = one_less_than_current
+csharp_indent_switch_labels = true
+
+# Space preferences
+csharp_space_after_cast = false
+csharp_space_after_colon_in_inheritance_clause = true
+csharp_space_after_comma = true
+csharp_space_after_dot = false
+csharp_space_after_keywords_in_control_flow_statements = true
+csharp_space_after_semicolon_in_for_statement = true
+csharp_space_around_binary_operators = before_and_after
+csharp_space_around_declaration_statements = false
+csharp_space_before_colon_in_inheritance_clause = true
+csharp_space_before_comma = false
+csharp_space_before_dot = false
+csharp_space_before_open_square_brackets = false
+csharp_space_before_semicolon_in_for_statement = false
+csharp_space_between_empty_square_brackets = false
+csharp_space_between_method_call_empty_parameter_list_parentheses = false
+csharp_space_between_method_call_name_and_opening_parenthesis = false
+csharp_space_between_method_call_parameter_list_parentheses = false
+csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
+csharp_space_between_method_declaration_name_and_open_parenthesis = false
+csharp_space_between_method_declaration_parameter_list_parentheses = false
+csharp_space_between_parentheses = false
+csharp_space_between_square_brackets = false
+
+# Wrapping preferences
+csharp_preserve_single_line_blocks = true
+csharp_preserve_single_line_statements = false
+
+#### Naming styles ####
+
+# Naming rules
+
+dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
+dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
+dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
+
+dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
+dotnet_naming_rule.types_should_be_pascal_case.symbols = types
+dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
+
+dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
+dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
+dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
+
+# Symbol specifications
+
+dotnet_naming_symbols.interface.applicable_kinds = interface
+dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal
+dotnet_naming_symbols.interface.required_modifiers =
+
+dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
+dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal
+dotnet_naming_symbols.types.required_modifiers =
+
+dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
+dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal
+dotnet_naming_symbols.non_field_members.required_modifiers =
+
+# Naming styles
+
+dotnet_naming_style.pascal_case.required_prefix =
+dotnet_naming_style.pascal_case.required_suffix =
+dotnet_naming_style.pascal_case.word_separator =
+dotnet_naming_style.pascal_case.capitalization = pascal_case
+
+dotnet_naming_style.begins_with_i.required_prefix = I
+dotnet_naming_style.begins_with_i.required_suffix =
+dotnet_naming_style.begins_with_i.word_separator =
+dotnet_naming_style.begins_with_i.capitalization = pascal_case
+
+#### Diagnostic configuration ####
+
+dotnet_diagnostic.CA1028.severity = none
+dotnet_diagnostic.CA1031.severity = none
+dotnet_diagnostic.CA1303.severity = none
+dotnet_diagnostic.CA1060.severity = none
+dotnet_diagnostic.CA2101.severity = none
+
+dotnet_diagnostic.IDE0058.severity = none
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..056734d
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,311 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+
+# User-specific files
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+
+# Visual Studio 2015 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUNIT
+*.VisualState.xml
+TestResult.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# DNX
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+*_i.c
+*_p.c
+*_i.h
+*.ilk
+*.meta
+*.obj
+*.pch
+*.pdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# JustCode is a .NET coding add-in
+.JustCode
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# TODO: Comment the next line if you want to checkin your web deploy settings
+# but database connection strings (with potential passwords) will be unencrypted
+#*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# The packages folder can be ignored because of Package Restore
+**/packages/*
+# except build/, which is used as an MSBuild target.
+!**/packages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/packages/repositories.config
+# NuGet v3's project.json files produces more ignoreable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+node_modules/
+orleans.codegen.cs
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+
+# SQL Server files
+*.mdf
+*.ldf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# JetBrains Rider
+.idea/
+*.sln.iml
+
+# CodeRush
+.cr/
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+
+#
+# ANGULAR gitignore begin section
+#
+
+# See http://help.github.com/ignore-files/ for more about ignoring files.
+
+# compiled output
+/dist
+/tmp
+/out-tsc
+
+# dependencies
+/node_modules
+
+# IDEs and editors
+/.idea
+.project
+.classpath
+.c9/
+*.launch
+.settings/
+*.sublime-workspace
+
+# IDE - VSCode
+.vscode/*
+!.vscode/settings.json
+!.vscode/tasks.json
+!.vscode/launch.json
+!.vscode/extensions.json
+
+# misc
+/.sass-cache
+/connect.lock
+/coverage
+/libpeerconnection.log
+npm-debug.log
+testem.log
+/typings
+
+# e2e
+/e2e/*.js
+/e2e/*.map
+
+# System Files
+*.xcuserstate
+*.xcactivitylog
+
+*.DotSettings
diff --git a/.versionrc.js b/.versionrc.js
new file mode 100644
index 0000000..4b9b623
--- /dev/null
+++ b/.versionrc.js
@@ -0,0 +1,22 @@
+// .versionrc.js
+const tracker = [
+ {
+ filename: './YTMDesktopPlugin/Properties/AssemblyInfo.cs',
+ updater: require('./standard-version-updater/AssemblyInfo.js')
+ },
+ {
+ filename: './package.json',
+ type: 'json'
+ },
+ {
+ filename: './LoupedeckPackage.yaml',
+ type: 'yaml'
+ }
+]
+
+module.exports = {
+ sign: true,
+ commitAll: true,
+ bumpFiles: tracker,
+ packageFiles: tracker
+}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..eb766e0
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,41 @@
+# Changelog
+
+All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
+
+## [1.4.0](https://github.com/XeroxDev/Loupedeck-plugin-YTMDesktop/compare/v1.3.0...v1.4.0) (2021-11-30)
+
+
+### Features
+
+* add plugin to all profiles ([411a62f](https://github.com/XeroxDev/Loupedeck-plugin-YTMDesktop/commit/411a62ff2cb0716aeb957e849e2cda7a533313a9))
+* add settings window ([18da42e](https://github.com/XeroxDev/Loupedeck-plugin-YTMDesktop/commit/18da42ecf8e9c002be294160dad5a79a5ef1faf0))
+* add visual bar to volume display ([22d1ee7](https://github.com/XeroxDev/Loupedeck-plugin-YTMDesktop/commit/22d1ee775fe5ad96c50370535343761cbb793d32))
+
+## [1.3.0](https://github.com/XeroxDev/Loupedeck-plugin-YTMDesktop/compare/v1.2.0...v1.3.0) (2021-11-19)
+
+
+### Features
+
+* add plugin icons ([f7d8b7d](https://github.com/XeroxDev/Loupedeck-plugin-YTMDesktop/commit/f7d8b7d81444e3ec0f732c6a74c60505140ee6a1))
+* show error messages and create link to wiki ([07b880f](https://github.com/XeroxDev/Loupedeck-plugin-YTMDesktop/commit/07b880f4d55679479e406bd7ff5140e22839d43d))
+
+## [1.2.0](https://github.com/XeroxDev/Loupedeck-plugin-YTMDesktop/compare/v1.1.0...v1.2.0) (2021-11-15)
+
+
+### Features
+
+* add thumbnail ([a155e1c](https://github.com/XeroxDev/Loupedeck-plugin-YTMDesktop/commit/a155e1c5a2997d0437050120727fb2999017ce45))
+
+
+### Bug Fixes
+
+* connection display issue ([d8e0d84](https://github.com/XeroxDev/Loupedeck-plugin-YTMDesktop/commit/d8e0d8428c9fda9fa48335e98d474964fb8c8d56))
+
+## [1.1.0](https://github.com/XeroxDev/Loupedeck-plugin-YTMDesktop/compare/v1.0.0...v1.1.0) (2021-09-02)
+
+
+### Features
+
+* show user the state of connection ([56b365d](https://github.com/XeroxDev/Loupedeck-plugin-YTMDesktop/commit/56b365dfb2e72061c9520ba41dd4462acd99e0dd)), closes [#1](https://github.com/XeroxDev/Loupedeck-plugin-YTMDesktop/issues/1)
+
+## 1.0.0 (2021-08-31)
diff --git a/Directory.Build.props b/Directory.Build.props
new file mode 100644
index 0000000..51879ab
--- /dev/null
+++ b/Directory.Build.props
@@ -0,0 +1,5 @@
+
+
+ 7.3
+
+
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..0bed292
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2021 Dominic "XeroxDev" Ris
+
+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/LoupedeckPackage.yaml b/LoupedeckPackage.yaml
new file mode 100644
index 0000000..3345f79
--- /dev/null
+++ b/LoupedeckPackage.yaml
@@ -0,0 +1,14 @@
+type: plugin4
+name: YTMDesktop
+displayName: YTMDesktop
+version: 1.0.0
+author: XeroxDev
+copyright: Copyright (c) 2021 Dominic Ris
+
+supportedDevices:
+ - LoupedeckCt
+ - LoupedeckLive
+
+pluginFileName: YTMDesktopPlugin.dll
+pluginFolderWin: bin/win/
+pluginFolderMac: bin/mac/
\ No newline at end of file
diff --git a/YTMDesktopPlugin.sln b/YTMDesktopPlugin.sln
new file mode 100644
index 0000000..53d1579
--- /dev/null
+++ b/YTMDesktopPlugin.sln
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.30128.74
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "YTMDesktopPlugin", "YTMDesktopPlugin\YTMDesktopPlugin.csproj", "{E8F03AF3-E7CA-4125-9196-E437B93BA224}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {E8F03AF3-E7CA-4125-9196-E437B93BA224}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E8F03AF3-E7CA-4125-9196-E437B93BA224}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E8F03AF3-E7CA-4125-9196-E437B93BA224}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E8F03AF3-E7CA-4125-9196-E437B93BA224}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {6ABAC073-F84C-4B7B-BE81-2946526F1FF1}
+ EndGlobalSection
+EndGlobal
diff --git a/YTMDesktopPlugin/App.config b/YTMDesktopPlugin/App.config
new file mode 100644
index 0000000..320ee0d
--- /dev/null
+++ b/YTMDesktopPlugin/App.config
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/YTMDesktopPlugin/Classes/EventArgs/OnConnectedEvent.cs b/YTMDesktopPlugin/Classes/EventArgs/OnConnectedEvent.cs
new file mode 100644
index 0000000..998e466
--- /dev/null
+++ b/YTMDesktopPlugin/Classes/EventArgs/OnConnectedEvent.cs
@@ -0,0 +1,18 @@
+namespace Loupedeck.YTMDesktopPlugin.Classes.EventArgs
+{
+ using System;
+
+ using H.Socket.IO.EventsArgs;
+
+ public sealed class OnConnectedEvent
+ {
+ public Object Sender { get; }
+ public SocketIoEventEventArgs EventArgs { get; }
+
+ public OnConnectedEvent(Object sender, SocketIoEventEventArgs eventArgs)
+ {
+ this.Sender = sender;
+ this.EventArgs = eventArgs;
+ }
+ }
+}
\ No newline at end of file
diff --git a/YTMDesktopPlugin/Classes/EventArgs/ServerEvents.cs b/YTMDesktopPlugin/Classes/EventArgs/ServerEvents.cs
new file mode 100644
index 0000000..6c00989
--- /dev/null
+++ b/YTMDesktopPlugin/Classes/EventArgs/ServerEvents.cs
@@ -0,0 +1,58 @@
+namespace Loupedeck.YTMDesktopPlugin.Classes.EventArgs
+{
+ using System;
+ using System.Text.Json.Serialization;
+
+ public class TrackAndPlayer
+ {
+ [JsonPropertyName("player")] public PlayerInfo Player { get; set; }
+ [JsonPropertyName("track")] public TrackInfo Track { get; set; }
+ }
+
+ public class PlayerInfo
+ {
+ [JsonPropertyName("hasSong")] public Boolean HasSong { get; set; }
+ [JsonPropertyName("isPaused")] public Boolean IsPaused { get; set; }
+ [JsonPropertyName("volumePercent")] public Double VolumePercent { get; set; }
+
+ [JsonPropertyName("seekbarCurrentPosition")]
+ public Double SeekbarCurrentPosition { get; set; }
+
+ [JsonPropertyName("seekbarCurrentPositionHuman")]
+ public String SeekbarCurrentPositionHuman { get; set; }
+
+ [JsonPropertyName("statePercent")] public Double StatePercent { get; set; }
+ [JsonPropertyName("likeStatus")] public String LikeStatus { get; set; }
+ [JsonPropertyName("repeatType")] public String RepeatType { get; set; }
+ }
+
+ public class TrackInfo
+ {
+ [JsonPropertyName("author")] public String Author { get; set; }
+ [JsonPropertyName("title")] public String Title { get; set; }
+ [JsonPropertyName("album")] public String Album { get; set; }
+ [JsonPropertyName("cover")] public String Cover { get; set; }
+ [JsonPropertyName("duration")] public Double Duration { get; set; }
+ [JsonPropertyName("durationHuman")] public String DurationHuman { get; set; }
+ [JsonPropertyName("url")] public String Url { get; set; }
+ [JsonPropertyName("id")] public String Id { get; set; }
+ [JsonPropertyName("isVideo")] public Boolean IsVideo { get; set; }
+ [JsonPropertyName("isAdvertisement")] public Boolean IsAdvertisement { get; set; }
+ [JsonPropertyName("inLibrary")] public Boolean InLibrary { get; set; }
+
+ public void Deconstruct(out String author, out String title, out String album, out String cover, out Double duration, out String durationHuman, out String url, out String id, out Boolean isVideo, out Boolean isAdvertisement, out Boolean inLibrary)
+ {
+ author = this.Author;
+ title = this.Title;
+ album = this.Album;
+ cover = this.Cover;
+ duration = this.Duration;
+ durationHuman = this.DurationHuman;
+ url = this.Url;
+ id = this.Id;
+ isVideo = this.IsVideo;
+ isAdvertisement = this.IsAdvertisement;
+ inLibrary = this.InLibrary;
+ }
+ }
+}
\ No newline at end of file
diff --git a/YTMDesktopPlugin/Commands/DislikeCommand.cs b/YTMDesktopPlugin/Commands/DislikeCommand.cs
new file mode 100644
index 0000000..37f219b
--- /dev/null
+++ b/YTMDesktopPlugin/Commands/DislikeCommand.cs
@@ -0,0 +1,46 @@
+namespace Loupedeck.YTMDesktopPlugin.Commands
+{
+ using System;
+ using System.Reactive.Linq;
+ using System.Reactive.Subjects;
+
+ using Services;
+
+ using static Helper.DrawingHelper;
+
+ public class DislikeCommand : PluginDynamicCommand
+ {
+ private SocketService SocketService { get; }
+ private Subject OnDestroy { get; } = new();
+
+ private Boolean Disliked { get; set; }
+
+ public DislikeCommand() : base("Dislike", "Dislikes track", "Track") =>
+ this.SocketService = SocketService.Instance;
+
+ protected override Boolean OnLoad()
+ {
+ this.SocketService.OnTick
+ .Select(response => response.Player.LikeStatus == "DISLIKE")
+ .DistinctUntilChanged(b => b == this.Disliked)
+ .TakeUntil(this.OnDestroy)
+ .Subscribe(liked =>
+ {
+ this.Disliked = liked;
+ this.ActionImageChanged();
+ });
+ return base.OnLoad();
+ }
+
+ protected override Boolean OnUnload()
+ {
+ this.OnDestroy.OnNext(true);
+ return base.OnUnload();
+ }
+
+ protected override async void RunCommand(String actionParameter) => await this.SocketService.TrackThumbsDown();
+
+ protected override BitmapImage GetCommandImage(String actionParameter, PluginImageSize imageSize) =>
+ LoadBitmapImage($"dislike-{(this.Disliked ? "on" : "off")}");
+ }
+}
\ No newline at end of file
diff --git a/YTMDesktopPlugin/Commands/LikeCommand.cs b/YTMDesktopPlugin/Commands/LikeCommand.cs
new file mode 100644
index 0000000..0e6b27d
--- /dev/null
+++ b/YTMDesktopPlugin/Commands/LikeCommand.cs
@@ -0,0 +1,46 @@
+namespace Loupedeck.YTMDesktopPlugin.Commands
+{
+ using System;
+ using System.Reactive.Linq;
+ using System.Reactive.Subjects;
+
+ using Services;
+
+ using static Helper.DrawingHelper;
+
+ public class LikeCommand : PluginDynamicCommand
+ {
+ private SocketService SocketService { get; }
+ private Subject OnDestroy { get; } = new();
+
+ private Boolean Liked { get; set; }
+
+ public LikeCommand() : base("Like", "Likes track", "Track") =>
+ this.SocketService = SocketService.Instance;
+
+ protected override Boolean OnLoad()
+ {
+ this.SocketService.OnTick
+ .Select(response => response.Player.LikeStatus == "LIKE")
+ .DistinctUntilChanged(b => b == this.Liked)
+ .TakeUntil(this.OnDestroy)
+ .Subscribe(liked =>
+ {
+ this.Liked = liked;
+ this.ActionImageChanged();
+ });
+ return base.OnLoad();
+ }
+
+ protected override Boolean OnUnload()
+ {
+ this.OnDestroy.OnNext(true);
+ return base.OnUnload();
+ }
+
+ protected override async void RunCommand(String actionParameter) => await this.SocketService.TrackThumbsUp();
+
+ protected override BitmapImage GetCommandImage(String actionParameter, PluginImageSize imageSize) =>
+ LoadBitmapImage($"like-{(this.Liked ? "on" : "off")}");
+ }
+}
\ No newline at end of file
diff --git a/YTMDesktopPlugin/Commands/NextTrackCommand.cs b/YTMDesktopPlugin/Commands/NextTrackCommand.cs
new file mode 100644
index 0000000..3719cda
--- /dev/null
+++ b/YTMDesktopPlugin/Commands/NextTrackCommand.cs
@@ -0,0 +1,21 @@
+namespace Loupedeck.YTMDesktopPlugin.Commands
+{
+ using System;
+
+ using Services;
+
+ using static Helper.DrawingHelper;
+
+ public class NextTrackCommand : PluginDynamicCommand
+ {
+ private SocketService SocketService { get; }
+
+ public NextTrackCommand() : base("Next track", "Goes to next track", "Track") =>
+ this.SocketService = SocketService.Instance;
+
+ protected override async void RunCommand(String actionParameter) => await this.SocketService.TrackNext();
+
+ protected override BitmapImage GetCommandImage(String actionParameter, PluginImageSize imageSize) =>
+ LoadBitmapImage("music-next");
+ }
+}
\ No newline at end of file
diff --git a/YTMDesktopPlugin/Commands/OpenSettingsCommand.cs b/YTMDesktopPlugin/Commands/OpenSettingsCommand.cs
new file mode 100644
index 0000000..09f1f1a
--- /dev/null
+++ b/YTMDesktopPlugin/Commands/OpenSettingsCommand.cs
@@ -0,0 +1,20 @@
+namespace Loupedeck.YTMDesktopPlugin.Commands
+{
+ using System;
+
+ using GUI;
+
+ using Services;
+
+ using static Helper.DrawingHelper;
+
+ public class OpenSettingsCommand : PluginDynamicCommand
+ {
+ public OpenSettingsCommand() : base("Open Settings", "Opens the settings", "General") {}
+
+ protected override void RunCommand(String actionParameter) => Settings.Open();
+
+ protected override BitmapImage GetCommandImage(String actionParameter, PluginImageSize imageSize) =>
+ LoadBitmapImage(text: "Open settings");
+ }
+}
\ No newline at end of file
diff --git a/YTMDesktopPlugin/Commands/PlayPauseCommand.cs b/YTMDesktopPlugin/Commands/PlayPauseCommand.cs
new file mode 100644
index 0000000..38da8e2
--- /dev/null
+++ b/YTMDesktopPlugin/Commands/PlayPauseCommand.cs
@@ -0,0 +1,83 @@
+namespace Loupedeck.YTMDesktopPlugin.Commands
+{
+ using System;
+ using System.Reactive.Linq;
+ using System.Reactive.Subjects;
+
+ using Enums;
+
+ using Extensions;
+
+ using Services;
+
+ using static Helper.DrawingHelper;
+
+ public class PlayPauseCommand : PluginDynamicCommand
+ {
+ private Boolean Playing { get; set; } = true;
+ private String Duration { get; set; } = "00:00";
+ private Subject OnDestroy { get; } = new();
+ private SocketService SocketService { get; }
+
+
+ public PlayPauseCommand() : base("Play/Pause", "Pauses or resumes music track", "Track") =>
+ this.SocketService = SocketService.Instance;
+
+ protected override Boolean OnLoad()
+ {
+ this.SocketService.OnTick
+ .DistinctUntilChanged()
+ .TakeUntil(this.OnDestroy)
+ .Subscribe(response =>
+ {
+ this.Duration = response.Player.SeekbarCurrentPositionHuman;
+ this.Playing = !response.Player.IsPaused;
+ this.ActionImageChanged();
+ });
+ this.SocketService.IsConnected
+ .DistinctUntilChanged()
+ .TakeUntil(this.OnDestroy)
+ .Subscribe(_ => this.ActionImageChanged());
+ return base.OnLoad();
+ }
+
+ protected override Boolean OnUnload()
+ {
+ this.OnDestroy.OnNext(true);
+ return base.OnUnload();
+ }
+
+ protected override async void RunCommand(String actionParameter)
+ {
+ this.Playing = !this.Playing;
+ await this.SocketService.TrackPlayPause();
+ this.ActionImageChanged(actionParameter);
+ }
+
+ protected override BitmapImage GetCommandImage(String actionParameter, PluginImageSize imageSize)
+ {
+ String text;
+ String imageName;
+ if (this.SocketService.IsConnected.Value)
+ {
+ if (this.Playing)
+ {
+ text = this.Duration;
+ imageName = "music-pause";
+ }
+ else
+ {
+ text = "Paused";
+ imageName = "music-play";
+ }
+ }
+ else
+ {
+ text = "Not connected";
+ imageName = "music-play";
+ }
+
+ return LoadBitmapImage(imageName, text);
+ }
+ }
+}
\ No newline at end of file
diff --git a/YTMDesktopPlugin/Commands/PreviousTrackCommand.cs b/YTMDesktopPlugin/Commands/PreviousTrackCommand.cs
new file mode 100644
index 0000000..38e4432
--- /dev/null
+++ b/YTMDesktopPlugin/Commands/PreviousTrackCommand.cs
@@ -0,0 +1,21 @@
+namespace Loupedeck.YTMDesktopPlugin.Commands
+{
+ using System;
+
+ using Services;
+
+ using static Helper.DrawingHelper;
+
+ public class PreviousTrackCommand : PluginDynamicCommand
+ {
+ private SocketService SocketService { get; }
+
+ public PreviousTrackCommand() : base("Previous track", "Goes to previous track", "Track") =>
+ this.SocketService = SocketService.Instance;
+
+ protected override async void RunCommand(String actionParameter) => await this.SocketService.TrackPrevious();
+
+ protected override BitmapImage GetCommandImage(String actionParameter, PluginImageSize imageSize) =>
+ LoadBitmapImage("music-prev");
+ }
+}
\ No newline at end of file
diff --git a/YTMDesktopPlugin/Commands/RepeatCommand.cs b/YTMDesktopPlugin/Commands/RepeatCommand.cs
new file mode 100644
index 0000000..bae9764
--- /dev/null
+++ b/YTMDesktopPlugin/Commands/RepeatCommand.cs
@@ -0,0 +1,47 @@
+namespace Loupedeck.YTMDesktopPlugin.Commands
+{
+ using System;
+ using System.Reactive.Linq;
+ using System.Reactive.Subjects;
+
+ using Services;
+
+ using static Helper.DrawingHelper;
+
+ public class RepeatCommand : PluginDynamicCommand
+ {
+ private SocketService SocketService { get; }
+ private Subject OnDestroy { get; } = new();
+ private String _repeatType = "None";
+
+
+ public RepeatCommand() : base("Repeat", "Toggles repeat types", "Player") =>
+ this.SocketService = SocketService.Instance;
+
+ protected override Boolean OnLoad()
+ {
+ this.SocketService.OnTick
+ .Select(response => response.Player.RepeatType)
+ .DistinctUntilChanged()
+ .TakeUntil(this.OnDestroy)
+ .Subscribe(repeatType =>
+ {
+ this._repeatType = repeatType;
+ this.ActionImageChanged();
+ });
+
+ return base.OnLoad();
+ }
+
+ protected override Boolean OnUnload()
+ {
+ this.OnDestroy.OnNext(true);
+ return base.OnUnload();
+ }
+
+ protected override async void RunCommand(String actionParameter) => await this.SocketService.PlayerRepeat();
+
+ protected override BitmapImage GetCommandImage(String actionParameter, PluginImageSize imageSize) =>
+ LoadBitmapImage("repeat", this._repeatType);
+ }
+}
\ No newline at end of file
diff --git a/YTMDesktopPlugin/Commands/ShuffleCommand.cs b/YTMDesktopPlugin/Commands/ShuffleCommand.cs
new file mode 100644
index 0000000..1d67bc3
--- /dev/null
+++ b/YTMDesktopPlugin/Commands/ShuffleCommand.cs
@@ -0,0 +1,22 @@
+namespace Loupedeck.YTMDesktopPlugin.Commands
+{
+ using System;
+
+ using Services;
+
+ using static Helper.DrawingHelper;
+
+
+ public class ShuffleCommand : PluginDynamicCommand
+ {
+ private SocketService SocketService { get; }
+
+ public ShuffleCommand() : base("Shuffle", "Toggles shuffle", "Player") =>
+ this.SocketService = SocketService.Instance;
+
+ protected override async void RunCommand(String actionParameter) => await this.SocketService.PlayerShuffle();
+
+ protected override BitmapImage GetCommandImage(String actionParameter, PluginImageSize imageSize) =>
+ LoadBitmapImage($"shuffle");
+ }
+}
\ No newline at end of file
diff --git a/YTMDesktopPlugin/Commands/ToggleVolumeCommand.cs b/YTMDesktopPlugin/Commands/ToggleVolumeCommand.cs
new file mode 100644
index 0000000..a9c0aa0
--- /dev/null
+++ b/YTMDesktopPlugin/Commands/ToggleVolumeCommand.cs
@@ -0,0 +1,24 @@
+namespace Loupedeck.YTMDesktopPlugin.Commands
+{
+ using System;
+
+ using Services;
+
+ public class ToggleVolumeCommand : PluginDynamicCommand
+ {
+ private SocketService SocketService { get; }
+
+ public ToggleVolumeCommand() : base("Toggle volume", "Toggles the volume", "Player")
+ => this.SocketService = SocketService.Instance;
+
+
+ protected override async void RunCommand(String actionParameter)
+ {
+ var vol = VolumeAdjustment.OnVolume.Value == 0
+ ? VolumeAdjustment.LastVolume
+ : -1;
+ VolumeAdjustment.OnVolume.OnNext(vol);
+ await this.SocketService.PlayerSetVolume(vol);
+ }
+ }
+}
\ No newline at end of file
diff --git a/YTMDesktopPlugin/Commands/TrackInfoCommand.cs b/YTMDesktopPlugin/Commands/TrackInfoCommand.cs
new file mode 100644
index 0000000..5cb6fe9
--- /dev/null
+++ b/YTMDesktopPlugin/Commands/TrackInfoCommand.cs
@@ -0,0 +1,177 @@
+namespace Loupedeck.YTMDesktopPlugin.Commands
+{
+ using System;
+ using System.Diagnostics;
+ using System.IO;
+ using System.Net;
+ using System.Reactive.Linq;
+ using System.Reactive.Subjects;
+
+ using Classes.EventArgs;
+
+ using Services;
+
+ using static Helper.DrawingHelper;
+
+ public class TrackInfoCommand : PluginDynamicCommand
+ {
+ private Subject OnDestroy { get; } = new();
+ private SocketService SocketService { get; }
+
+ private Int32 TitleIndex { get; set; }
+ private String CurrentTitle { get; set; } = "";
+ private Int32 AuthorIndex { get; set; }
+ private String CurrentAuthor { get; set; } = "";
+ private Int32 AlbumIndex { get; set; }
+ private String CurrentAlbum { get; set; } = "";
+ private String CurrentThumbnail { get; set; } = "";
+ private String CurrentUrl { get; set; } = "";
+ private String PlaceholderCover { get; } = "https://via.placeholder.com/128?text=";
+
+ private String DisplayTitle { get; set; } = "Nothing";
+ private BitmapImage ThumbnailBitmap { get; set; }
+
+
+ public TrackInfoCommand() : base("Track Info",
+ "Shows thumbnail, title, author and album. On push opens link to track", "Track") =>
+ this.SocketService = SocketService.Instance;
+
+ protected override Boolean OnLoad()
+ {
+ this.SocketService.OnTick
+ .DistinctUntilChanged()
+ .TakeUntil(this.OnDestroy)
+ .Subscribe(response =>
+ {
+ var (author, title, album, cover, _, _, url, _, _, _, _) = this.GetSongData(response);
+
+ if (this.CurrentTitle != title)
+ {
+ this.TitleIndex = 0;
+ }
+
+ if (this.CurrentAlbum != album)
+ {
+ this.AlbumIndex = 0;
+ }
+
+ if (this.CurrentAuthor != author)
+ {
+ this.AuthorIndex = 0;
+ }
+
+ if (this.CurrentThumbnail != cover)
+ {
+ this.CurrentThumbnail = cover;
+ using var ms = new MemoryStream();
+ GetImageAsStream(cover).CopyTo(ms);
+ this.ThumbnailBitmap = new BitmapImage(ms.ToArray());
+ this.ThumbnailBitmap.Resize(90, 90);
+ }
+
+ this.CurrentTitle = title;
+ this.CurrentAuthor = author;
+ this.CurrentAlbum = album;
+ this.CurrentThumbnail = cover;
+ this.CurrentUrl = url;
+
+ var displayTitle = this.CurrentTitle;
+ var displayAlbum = this.CurrentAlbum == displayTitle ? "" : this.CurrentAlbum;
+ var displayAuthor = this.CurrentAuthor == this.CurrentTitle ? "" : this.CurrentAuthor;
+
+ var max = 7;
+ if (String.IsNullOrEmpty(displayTitle)
+ && String.IsNullOrEmpty(displayAlbum)
+ && String.IsNullOrEmpty(displayAuthor))
+ {
+ return;
+ }
+
+ if (this.CurrentTitle.Length > max && !String.IsNullOrEmpty(displayTitle))
+ {
+ displayTitle =
+ this.GetScrollingText("".PadLeft(max, '.') + displayTitle + "".PadLeft(max, '.'),
+ this.TitleIndex, max);
+ this.TitleIndex++;
+ if (this.CurrentTitle.Length - this.TitleIndex + max * 2 < max)
+ {
+ this.TitleIndex = 0;
+ }
+ }
+
+ if (this.CurrentAuthor.Length > max && !String.IsNullOrEmpty(displayAuthor))
+ {
+ displayAuthor =
+ this.GetScrollingText("".PadLeft(max, '.') + displayAuthor + "".PadLeft(max, '.'),
+ this.AuthorIndex, max);
+ this.AuthorIndex++;
+ if (this.CurrentAuthor.Length - this.AuthorIndex + max * 2 < max)
+ {
+ this.AuthorIndex = 0;
+ }
+ }
+
+ if (this.CurrentAlbum.Length > max && !String.IsNullOrEmpty(displayAlbum))
+ {
+ displayAlbum =
+ this.GetScrollingText("".PadLeft(max, '.') + displayAlbum + "".PadLeft(max, '.'),
+ this.AlbumIndex, max);
+ this.AlbumIndex++;
+ if (this.CurrentAlbum.Length - this.AlbumIndex + max * 2 < max)
+ {
+ this.AlbumIndex = 0;
+ }
+ }
+
+ this.DisplayTitle = $"{displayTitle}\n{displayAlbum}\n{displayAuthor}";
+ this.ActionImageChanged();
+ });
+ return base.OnLoad();
+ }
+
+ private TrackInfo GetSongData(TrackAndPlayer data)
+ {
+ var hasSong = data.Player.HasSong;
+ var isPaused = data.Player.IsPaused;
+
+ if (isPaused)
+ {
+ data.Track.Cover = this.PlaceholderCover + "Paused";
+ data.Track.Title = "Paused";
+ data.Track.Author = "Paused";
+ data.Track.Album = "Paused";
+ }
+ else if (!hasSong)
+ {
+ data.Track.Cover = this.PlaceholderCover + "N%2FA";
+ data.Track.Title = "N/A";
+ data.Track.Author = "N/A";
+ data.Track.Album = "N/A";
+ }
+
+ return data.Track;
+ }
+
+ private String GetScrollingText(String text, Int32 index, Int32 max) => text.Substring(index, max);
+
+
+ protected override Boolean OnUnload()
+ {
+ this.OnDestroy.OnNext(true);
+ return base.OnUnload();
+ }
+
+ protected override void RunCommand(String actionParameter) => Process.Start(this.CurrentUrl);
+
+ protected override BitmapImage GetCommandImage(String actionParameter, PluginImageSize imageSize) =>
+ LoadBitmapImage(this.ThumbnailBitmap,
+ this.SocketService.IsConnected.Value ? this.DisplayTitle : "Not connected");
+
+ private static Stream GetImageAsStream(String urlImage)
+ {
+ var request = WebRequest.Create(urlImage);
+ var response = request.GetResponse();
+ return response.GetResponseStream();
+ }
+ }
+}
\ No newline at end of file
diff --git a/YTMDesktopPlugin/Commands/VolumeAdjustment.cs b/YTMDesktopPlugin/Commands/VolumeAdjustment.cs
new file mode 100644
index 0000000..9b69756
--- /dev/null
+++ b/YTMDesktopPlugin/Commands/VolumeAdjustment.cs
@@ -0,0 +1,117 @@
+namespace Loupedeck.YTMDesktopPlugin.Commands
+{
+ using System;
+ using System.Drawing;
+ using System.Drawing.Imaging;
+ using System.IO;
+ using System.Reactive.Linq;
+ using System.Reactive.Subjects;
+ using System.Runtime.InteropServices;
+
+ using Extensions;
+
+ using Helper;
+
+ using Services;
+
+ using static Helper.DrawingHelper;
+
+
+ public class VolumeAdjustment : PluginDynamicAdjustment
+ {
+ public static BehaviorSubject OnVolume { get; } = new(50);
+ public static Int32 LastVolume { get; set; } = 50;
+ private Boolean IgnoreServer { get; set; }
+ private Subject OnDestroy { get; } = new();
+ private SocketService SocketService { get; }
+
+
+ protected override Boolean OnLoad()
+ {
+ this.SocketService
+ .OnTick
+ .DistinctUntilChanged()
+ .TakeUntil(this.OnDestroy)
+ .Subscribe(response =>
+ {
+ if (this.IgnoreServer)
+ {
+ this.IgnoreServer = false;
+ return;
+ }
+
+ OnVolume.OnNext(Convert.ToInt32(response.Player.VolumePercent));
+ });
+
+ OnVolume
+ .DistinctUntilChanged()
+ .TakeUntil(this.OnDestroy)
+ .Subscribe(vol =>
+ {
+ LastVolume = vol <= 0 ? LastVolume : vol;
+ this.AdjustmentValueChanged();
+ });
+ return base.OnLoad();
+ }
+
+ protected override Boolean OnUnload()
+ {
+ this.OnDestroy.OnNext(true);
+ return base.OnUnload();
+ }
+
+ public VolumeAdjustment() : base("Change volume", "Changes player volume", "Player", false) =>
+ this.SocketService = SocketService.Instance;
+
+ protected override String GetCommandDisplayName(String actionParameter, PluginImageSize imageSize) => "Volume";
+
+ protected override async void ApplyAdjustment(String actionParameter, Int32 diff)
+ {
+ var vol = OnVolume.Value;
+ vol += diff;
+
+ vol = vol switch
+ {
+ < 0 => -1,
+ > 100 => 100,
+ _ => vol
+ };
+
+ if (vol == LastVolume)
+ {
+ return;
+ }
+
+ LastVolume = vol;
+ OnVolume.OnNext(vol);
+ this.IgnoreServer = true;
+ await this.SocketService.PlayerSetVolume(vol);
+ }
+
+ protected override BitmapImage GetAdjustmentImage(String actionParameter, PluginImageSize imageSize)
+ {
+ var bitmap = new Bitmap(70, 20);
+ var g = Graphics.FromImage(bitmap);
+
+ var percentage = OnVolume.Value;
+ var bgColor = Color.FromArgb(156, 156, 156);
+ var textColor = Color.White;
+ var rect = new Rectangle(0, 0, bitmap.Width - 1, bitmap.Height - 1);
+ var font = new Font("Arial", 20, FontStyle.Bold);
+ var brush = new SolidBrush(Color.White);
+ var width = (Int32)(rect.Width * percentage / 100.0);
+ var sf = new StringFormat() { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center };
+
+ g.DrawRectangle(new Pen(bgColor), rect);
+ g.FillRectangle(new SolidBrush(bgColor), 0, 0, width, rect.Height);
+ g.FillRectangle(new SolidBrush(Color.FromArgb(150, 0, 0, 0)), 0, 0, bitmap.Width, bitmap.Height);
+ g.DrawAutoAdjustedFont($"{percentage}%", font, brush, rect, sf, 12);
+
+ bitmap.RotateFlip(RotateFlipType.Rotate270FlipNone);
+
+ var ms = new MemoryStream();
+ bitmap.Save(ms, ImageFormat.Png);
+ return new BitmapImage(ms.ToArray());
+ }
+ }
+}
\ No newline at end of file
diff --git a/YTMDesktopPlugin/Enums/ErrorCode.cs b/YTMDesktopPlugin/Enums/ErrorCode.cs
new file mode 100644
index 0000000..f9178fb
--- /dev/null
+++ b/YTMDesktopPlugin/Enums/ErrorCode.cs
@@ -0,0 +1,10 @@
+namespace Loupedeck.YTMDesktopPlugin.Enums
+{
+ using System;
+
+ public enum ErrorCode : UInt16
+ {
+ None,
+ CouldNotConnect = 100
+ }
+}
\ No newline at end of file
diff --git a/YTMDesktopPlugin/Extensions/GraphicsExtensions.cs b/YTMDesktopPlugin/Extensions/GraphicsExtensions.cs
new file mode 100644
index 0000000..9554f28
--- /dev/null
+++ b/YTMDesktopPlugin/Extensions/GraphicsExtensions.cs
@@ -0,0 +1,71 @@
+namespace Loupedeck.YTMDesktopPlugin.Extensions
+{
+ using System;
+ using System.Drawing;
+ using System.Drawing.Drawing2D;
+ using System.Windows;
+
+ public static class GraphicsExtensions
+ {
+ public static void DrawAutoAdjustedFont(this Graphics g, String s, Font font, Brush brush, Single x,
+ Single y, Int32 maxFontSize = 90, Int32 minFontSize = 5, Boolean smallestOnFail = true, Int32 width = 0)
+ => g.DrawAutoAdjustedFont(s, font, brush, new RectangleF(x, y, 0.0f, 0.0f), (StringFormat)null,
+ maxFontSize, minFontSize, smallestOnFail);
+
+ public static void DrawAutoAdjustedFont(this Graphics g, String s, Font font, Brush brush, PointF point,
+ Int32 maxFontSize = 90, Int32 minFontSize = 5, Boolean smallestOnFail = true, Int32 width = 0)
+ => g.DrawAutoAdjustedFont(s, font, brush, new RectangleF(point.X, point.Y, 0.0f, 0.0f),
+ (StringFormat)null, maxFontSize, minFontSize, smallestOnFail);
+
+ public static void DrawAutoAdjustedFont(this Graphics g, String s, Font font, Brush brush, Single x, Single y,
+ StringFormat format, Int32 maxFontSize = 90, Int32 minFontSize = 5, Boolean smallestOnFail = true,
+ Int32 width = 0)
+ => g.DrawAutoAdjustedFont(s, font, brush, new RectangleF(x, y, 0.0f, 0.0f), format,
+ maxFontSize, minFontSize, smallestOnFail);
+
+ public static void DrawAutoAdjustedFont(this Graphics g, String s, Font font, Brush brush, PointF point,
+ StringFormat format, Int32 maxFontSize = 90, Int32 minFontSize = 5, Boolean smallestOnFail = true,
+ Int32 width = 0)
+ => g.DrawAutoAdjustedFont(s, font, brush, new RectangleF(point.X, point.Y, 0.0f, 0.0f), format,
+ maxFontSize, minFontSize, smallestOnFail);
+
+ public static void
+ DrawAutoAdjustedFont(this Graphics g, String s, Font font, Brush brush, RectangleF layoutRectangle,
+ Int32 maxFontSize = 90, Int32 minFontSize = 5, Boolean smallestOnFail = true, Int32 width = 0)
+ => g.DrawAutoAdjustedFont(s, font, brush, layoutRectangle, (StringFormat)null,
+ maxFontSize, minFontSize, smallestOnFail);
+
+ public static void DrawAutoAdjustedFont(this Graphics g, String s, Font font, Brush brush,
+ RectangleF layoutRectangle, StringFormat format,
+ Int32 maxFontSize = 90, Int32 minFontSize = 5, Boolean smallestOnFail = true, Int32 width = 0)
+ {
+ g.DrawString(
+ s,
+ g.GetAdjustedFont(s, font, width == 0 ? layoutRectangle.Width : width, maxFontSize, minFontSize,
+ smallestOnFail),
+ brush,
+ layoutRectangle,
+ format
+ );
+ }
+
+ private static Font GetAdjustedFont(this Graphics g, String graphicString, Font originalFont,
+ Single containerWidth, Int32 maxFontSize = 90, Int32 minFontSize = 5, Boolean smallestOnFail = true)
+ {
+ Font testFont = null;
+ for (var adjustedSize = maxFontSize; adjustedSize >= minFontSize; adjustedSize--)
+ {
+ testFont = new Font(originalFont.Name, adjustedSize, originalFont.Style);
+
+ var adjustedSizeNew = g.MeasureString(graphicString, testFont);
+
+ if (containerWidth > Convert.ToInt32(adjustedSizeNew.Width))
+ {
+ return testFont;
+ }
+ }
+
+ return smallestOnFail ? testFont : originalFont;
+ }
+ }
+}
\ No newline at end of file
diff --git a/YTMDesktopPlugin/Extensions/PluginExtensions.cs b/YTMDesktopPlugin/Extensions/PluginExtensions.cs
new file mode 100644
index 0000000..0e58b7f
--- /dev/null
+++ b/YTMDesktopPlugin/Extensions/PluginExtensions.cs
@@ -0,0 +1,45 @@
+namespace Loupedeck.YTMDesktopPlugin.Extensions
+{
+ using System;
+ using System.Text.RegularExpressions;
+
+ using Enums;
+
+ public static class PluginExtensions
+ {
+ public static void SetStatus(this Plugin plugin, PluginStatus status, ErrorCode errorCode = ErrorCode.None)
+ {
+ if (plugin == null)
+ {
+ throw new ArgumentNullException(nameof(plugin));
+ }
+
+ if (status == PluginStatus.Error)
+ {
+ if (errorCode == 0)
+ {
+ throw new ArgumentNullException(nameof(errorCode));
+ }
+ }
+
+ var message = Regex.Replace(errorCode.ToString(), "[a-z][A-Z]",
+ m => $"{m.Value[0]} {Char.ToLower(m.Value[1])}");
+
+ if (plugin.PluginStatus.Status == status && plugin.PluginStatus.Message == message)
+ {
+ return;
+ }
+
+ plugin.OnPluginStatusChanged(
+ status,
+ message,
+ $"https://help.xeroxdev.de/en/loupedeck/ytmd/error/{(UInt16)errorCode}"
+ );
+ }
+
+ public static void ResetStatus(this Plugin plugin)
+ {
+ plugin.SetStatus(PluginStatus.Normal);
+ }
+ }
+}
\ No newline at end of file
diff --git a/YTMDesktopPlugin/GUI/Settings.Designer.cs b/YTMDesktopPlugin/GUI/Settings.Designer.cs
new file mode 100644
index 0000000..b67a09f
--- /dev/null
+++ b/YTMDesktopPlugin/GUI/Settings.Designer.cs
@@ -0,0 +1,190 @@
+namespace Loupedeck.YTMDesktopPlugin.GUI
+{
+ using System.ComponentModel;
+ using System.Windows.Forms;
+
+ partial class Settings
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ this.titleLabel = new System.Windows.Forms.Label();
+ this.panel1 = new System.Windows.Forms.Panel();
+ this.saveButton = new System.Windows.Forms.Button();
+ this.hostBox = new System.Windows.Forms.TextBox();
+ this.portBox = new System.Windows.Forms.TextBox();
+ this.passwordBox = new System.Windows.Forms.TextBox();
+ this.hostLabel = new System.Windows.Forms.Label();
+ this.portLabel = new System.Windows.Forms.Label();
+ this.passwordLabel = new System.Windows.Forms.Label();
+ this.panel1.SuspendLayout();
+ this.SuspendLayout();
+ //
+ // titleLabel
+ //
+ this.titleLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.titleLabel.ForeColor = System.Drawing.Color.White;
+ this.titleLabel.Location = new System.Drawing.Point(19, 11);
+ this.titleLabel.Name = "titleLabel";
+ this.titleLabel.Size = new System.Drawing.Size(100, 23);
+ this.titleLabel.TabIndex = 0;
+ this.titleLabel.Text = "Settings";
+ //
+ // panel1
+ //
+ this.panel1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(26)))), ((int)(((byte)(26)))), ((int)(((byte)(26)))));
+ this.panel1.Controls.Add(this.titleLabel);
+ this.panel1.Location = new System.Drawing.Point(-7, -2);
+ this.panel1.Name = "panel1";
+ this.panel1.Size = new System.Drawing.Size(347, 38);
+ this.panel1.TabIndex = 1;
+ this.panel1.MouseDown += new System.Windows.Forms.MouseEventHandler(this.panel1_MouseDown);
+ this.panel1.MouseMove += new System.Windows.Forms.MouseEventHandler(this.panel1_MouseMove);
+ this.panel1.MouseUp += new System.Windows.Forms.MouseEventHandler(this.panel1_MouseUp);
+ //
+ // saveButton
+ //
+ this.saveButton.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(41)))), ((int)(((byte)(40)))), ((int)(((byte)(42)))));
+ this.saveButton.FlatAppearance.BorderColor = System.Drawing.Color.White;
+ this.saveButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
+ this.saveButton.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.saveButton.ForeColor = System.Drawing.Color.White;
+ this.saveButton.Location = new System.Drawing.Point(206, 147);
+ this.saveButton.Name = "saveButton";
+ this.saveButton.Size = new System.Drawing.Size(113, 37);
+ this.saveButton.TabIndex = 2;
+ this.saveButton.Text = "Save";
+ this.saveButton.UseVisualStyleBackColor = false;
+ this.saveButton.Click += new System.EventHandler(this.saveButton_Click);
+ //
+ // hostBox
+ //
+ this.hostBox.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(41)))), ((int)(((byte)(40)))), ((int)(((byte)(42)))));
+ this.hostBox.Font = new System.Drawing.Font("Microsoft Sans Serif", 14.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.hostBox.ForeColor = System.Drawing.Color.White;
+ this.hostBox.Location = new System.Drawing.Point(136, 42);
+ this.hostBox.Name = "hostBox";
+ this.hostBox.Size = new System.Drawing.Size(183, 29);
+ this.hostBox.TabIndex = 3;
+ //
+ // portBox
+ //
+ this.portBox.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(41)))), ((int)(((byte)(40)))), ((int)(((byte)(42)))));
+ this.portBox.Font = new System.Drawing.Font("Microsoft Sans Serif", 14.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.portBox.ForeColor = System.Drawing.Color.White;
+ this.portBox.Location = new System.Drawing.Point(136, 77);
+ this.portBox.Name = "portBox";
+ this.portBox.Size = new System.Drawing.Size(183, 29);
+ this.portBox.TabIndex = 4;
+ this.portBox.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.portBox_KeyPress);
+ //
+ // passwordBox
+ //
+ this.passwordBox.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(41)))), ((int)(((byte)(40)))), ((int)(((byte)(42)))));
+ this.passwordBox.Font = new System.Drawing.Font("Microsoft Sans Serif", 14.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.passwordBox.ForeColor = System.Drawing.Color.White;
+ this.passwordBox.Location = new System.Drawing.Point(136, 112);
+ this.passwordBox.Name = "passwordBox";
+ this.passwordBox.PasswordChar = '*';
+ this.passwordBox.Size = new System.Drawing.Size(183, 29);
+ this.passwordBox.TabIndex = 5;
+ //
+ // hostLabel
+ //
+ this.hostLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 15.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.hostLabel.ForeColor = System.Drawing.Color.White;
+ this.hostLabel.Location = new System.Drawing.Point(12, 44);
+ this.hostLabel.Name = "hostLabel";
+ this.hostLabel.Size = new System.Drawing.Size(118, 23);
+ this.hostLabel.TabIndex = 6;
+ this.hostLabel.Text = "Host";
+ //
+ // portLabel
+ //
+ this.portLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 15.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.portLabel.ForeColor = System.Drawing.Color.White;
+ this.portLabel.Location = new System.Drawing.Point(12, 79);
+ this.portLabel.Name = "portLabel";
+ this.portLabel.Size = new System.Drawing.Size(118, 23);
+ this.portLabel.TabIndex = 7;
+ this.portLabel.Text = "Port";
+ //
+ // passwordLabel
+ //
+ this.passwordLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 15.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.passwordLabel.ForeColor = System.Drawing.Color.White;
+ this.passwordLabel.Location = new System.Drawing.Point(12, 112);
+ this.passwordLabel.Name = "passwordLabel";
+ this.passwordLabel.Size = new System.Drawing.Size(118, 23);
+ this.passwordLabel.TabIndex = 8;
+ this.passwordLabel.Text = "Password";
+ //
+ // Settings
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(41)))), ((int)(((byte)(40)))), ((int)(((byte)(42)))));
+ this.ClientSize = new System.Drawing.Size(334, 196);
+ this.ControlBox = false;
+ this.Controls.Add(this.passwordLabel);
+ this.Controls.Add(this.portLabel);
+ this.Controls.Add(this.hostLabel);
+ this.Controls.Add(this.passwordBox);
+ this.Controls.Add(this.portBox);
+ this.Controls.Add(this.hostBox);
+ this.Controls.Add(this.saveButton);
+ this.Controls.Add(this.panel1);
+ this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
+ this.Name = "Settings";
+ this.ShowIcon = false;
+ this.ShowInTaskbar = false;
+ this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
+ this.Text = "Settings";
+ this.TopMost = true;
+ this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.Settings_FormClosed);
+ this.Shown += new System.EventHandler(this.Settings_Shown);
+ this.panel1.ResumeLayout(false);
+ this.ResumeLayout(false);
+ this.PerformLayout();
+ }
+
+ private System.Windows.Forms.Label portLabel;
+ private System.Windows.Forms.Label passwordLabel;
+
+ private System.Windows.Forms.Label hostLabel;
+ private System.Windows.Forms.Label titleLabel;
+
+ private System.Windows.Forms.Button saveButton;
+ private System.Windows.Forms.TextBox hostBox;
+ private System.Windows.Forms.TextBox portBox;
+ private System.Windows.Forms.TextBox passwordBox;
+
+ private System.Windows.Forms.Panel panel1;
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/YTMDesktopPlugin/GUI/Settings.cs b/YTMDesktopPlugin/GUI/Settings.cs
new file mode 100644
index 0000000..4048a6d
--- /dev/null
+++ b/YTMDesktopPlugin/GUI/Settings.cs
@@ -0,0 +1,98 @@
+namespace Loupedeck.YTMDesktopPlugin.GUI
+{
+ using System;
+ using System.Drawing;
+ using System.Threading;
+ using System.Windows.Forms;
+
+ using Services;
+
+ public partial class Settings : Form
+ {
+ private readonly YTMDesktopPlugin _plugin;
+ private Boolean _moving;
+ private Point _offset;
+ public static Boolean IsOpen;
+
+ private Settings()
+ {
+ this._plugin = YTMDesktopPlugin.Instance;
+ this.InitializeComponent();
+ this.LoadSettings();
+ }
+
+ private void LoadSettings()
+ {
+ this._plugin.TryGetSetting("host", out var host);
+ this._plugin.TryGetSetting("port", out var port);
+ this._plugin.TryGetSetting("password", out var password);
+ this.hostBox.Text = host ?? "127.0.0.1";
+ this.portBox.Text = port ?? "9863";
+ this.passwordBox.Text = password ?? "";
+ }
+
+ private void panel1_MouseDown(Object sender, MouseEventArgs e)
+ {
+ this._moving = true;
+ this._offset = new Point(e.X, e.Y);
+ }
+
+ private void panel1_MouseMove(Object sender, MouseEventArgs e)
+ {
+ if (!this._moving)
+ {
+ return;
+ }
+
+ Point newlocation = this.Location;
+ newlocation.X += e.X - this._offset.X;
+ newlocation.Y += e.Y - this._offset.Y;
+ this.Location = newlocation;
+ }
+
+ private void panel1_MouseUp(Object sender, MouseEventArgs e)
+ {
+ if (this._moving)
+ {
+ this._moving = false;
+ }
+ }
+
+ private void portBox_KeyPress(Object sender, KeyPressEventArgs e)
+ {
+ e.Handled = !Char.IsDigit(e.KeyChar) && !Char.IsControl(e.KeyChar);
+ }
+
+ public static void Open()
+ {
+ if (IsOpen)
+ {
+ return;
+ }
+
+ Thread s = new Thread(() =>
+ {
+ var settingsWindow = new Settings();
+ settingsWindow.Show();
+ System.Windows.Threading.Dispatcher.Run();
+ });
+
+ s.SetApartmentState(ApartmentState.STA);
+ s.Start();
+ }
+
+ private void saveButton_Click(Object sender, EventArgs e)
+ {
+ this._plugin.SetSetting("host", this.hostBox.Text);
+ this._plugin.SetSetting("port", this.portBox.Text);
+ this._plugin.SetSetting("password", this.passwordBox.Text);
+ SocketService.Instance.SetSettings(this.hostBox.Text, this.portBox.Text, this.passwordBox.Text)
+ .GetAwaiter();
+ this.Close();
+ }
+
+ private void Settings_Shown(Object sender, EventArgs e) => IsOpen = true;
+
+ private void Settings_FormClosed(Object sender, FormClosedEventArgs e) => IsOpen = false;
+ }
+}
\ No newline at end of file
diff --git a/YTMDesktopPlugin/GUI/Settings.resx b/YTMDesktopPlugin/GUI/Settings.resx
new file mode 100644
index 0000000..1af7de1
--- /dev/null
+++ b/YTMDesktopPlugin/GUI/Settings.resx
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/YTMDesktopPlugin/Helper/DrawingHelper.cs b/YTMDesktopPlugin/Helper/DrawingHelper.cs
new file mode 100644
index 0000000..76a7849
--- /dev/null
+++ b/YTMDesktopPlugin/Helper/DrawingHelper.cs
@@ -0,0 +1,48 @@
+namespace Loupedeck.YTMDesktopPlugin.Helper
+{
+ using System;
+ using System.Drawing;
+
+ public static class DrawingHelper
+ {
+ private static String RESOURCE_PATH = "Loupedeck.YTMDesktopPlugin.Resources";
+
+ public static BitmapImage ReadImage(String imageName, String ext = "png", String addPath = "Images")
+ => EmbeddedResources.ReadImage($"{RESOURCE_PATH}.{addPath}.{imageName}.{ext}");
+
+ public static BitmapBuilder LoadBitmapBuilder
+ (String imageName = "clear", String text = null, BitmapColor? textColor = null, String ext = "png",
+ String addPath = "Images")
+ => LoadBitmapBuilder(ReadImage(imageName, ext, addPath), text, textColor);
+
+ public static BitmapBuilder LoadBitmapBuilder
+ (BitmapImage image, String text = null, BitmapColor? textColor = null)
+ {
+ var builder = new BitmapBuilder(90, 90);
+
+ builder.Clear(BitmapColor.Black);
+ builder.DrawImage(image);
+ builder.FillRectangle(0, 0, 90, 90, new BitmapColor(0, 0, 0, 150));
+
+ return text is null ? builder : builder.AddTextOutlined(text, textColor: textColor);
+ }
+
+ public static BitmapImage LoadBitmapImage
+ (String imageName = "clear", String text = null, BitmapColor? textColor = null, String ext = "png",
+ String addPath = "Images")
+ => LoadBitmapBuilder(imageName, text, textColor, ext, addPath).ToImage();
+
+ public static BitmapImage LoadBitmapImage(BitmapImage image, String text = null, BitmapColor? textColor = null)
+ => LoadBitmapBuilder(image, text, textColor).ToImage();
+
+ public static BitmapBuilder AddTextOutlined(this BitmapBuilder builder, String text,
+ BitmapColor? outlineColor = null,
+ BitmapColor? textColor = null, Int32 fontSize = 15)
+ {
+ // Make it outline
+ // builder.DrawText(text, outlineColor ?? BitmapColor.Black, fontSize + 2);
+ builder.DrawText(text, textColor, fontSize);
+ return builder;
+ }
+ }
+}
\ No newline at end of file
diff --git a/YTMDesktopPlugin/PluginConfiguration.json b/YTMDesktopPlugin/PluginConfiguration.json
new file mode 100644
index 0000000..d4a57b3
--- /dev/null
+++ b/YTMDesktopPlugin/PluginConfiguration.json
@@ -0,0 +1,4 @@
+{
+ "displayName": "YTMDesktop",
+ "supportedDevices": "Loupedeck20,Loupedeck30"
+}
diff --git a/YTMDesktopPlugin/Properties/AssemblyInfo.cs b/YTMDesktopPlugin/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..9fac4cf
--- /dev/null
+++ b/YTMDesktopPlugin/Properties/AssemblyInfo.cs
@@ -0,0 +1,33 @@
+using System.Reflection;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("YTMDesktop")]
+[assembly: AssemblyDescription("With this plugin you can control your YTMDesktop instance with your LoupeDeck")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("XeroxDev")]
+[assembly: AssemblyProduct("Loupedeck2")]
+[assembly: AssemblyCopyright("Copyright (c) 2021 XeroxDev. All rights reserved.")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("E8F03AF3-E7CA-4125-9196-E437B93BA224")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+[assembly: AssemblyVersion("1.0.0")]
\ No newline at end of file
diff --git a/YTMDesktopPlugin/Resources/Icon/icon-16.png b/YTMDesktopPlugin/Resources/Icon/icon-16.png
new file mode 100644
index 0000000..f9115e1
Binary files /dev/null and b/YTMDesktopPlugin/Resources/Icon/icon-16.png differ
diff --git a/YTMDesktopPlugin/Resources/Icon/icon-256.png b/YTMDesktopPlugin/Resources/Icon/icon-256.png
new file mode 100644
index 0000000..c6021a2
Binary files /dev/null and b/YTMDesktopPlugin/Resources/Icon/icon-256.png differ
diff --git a/YTMDesktopPlugin/Resources/Icon/icon-32.png b/YTMDesktopPlugin/Resources/Icon/icon-32.png
new file mode 100644
index 0000000..e320e50
Binary files /dev/null and b/YTMDesktopPlugin/Resources/Icon/icon-32.png differ
diff --git a/YTMDesktopPlugin/Resources/Icon/icon-48.png b/YTMDesktopPlugin/Resources/Icon/icon-48.png
new file mode 100644
index 0000000..10385d6
Binary files /dev/null and b/YTMDesktopPlugin/Resources/Icon/icon-48.png differ
diff --git a/YTMDesktopPlugin/Resources/Images/clear.png b/YTMDesktopPlugin/Resources/Images/clear.png
new file mode 100644
index 0000000..9360109
Binary files /dev/null and b/YTMDesktopPlugin/Resources/Images/clear.png differ
diff --git a/YTMDesktopPlugin/Resources/Images/dislike-off.png b/YTMDesktopPlugin/Resources/Images/dislike-off.png
new file mode 100644
index 0000000..b3d3290
Binary files /dev/null and b/YTMDesktopPlugin/Resources/Images/dislike-off.png differ
diff --git a/YTMDesktopPlugin/Resources/Images/dislike-on.png b/YTMDesktopPlugin/Resources/Images/dislike-on.png
new file mode 100644
index 0000000..b4a08d3
Binary files /dev/null and b/YTMDesktopPlugin/Resources/Images/dislike-on.png differ
diff --git a/YTMDesktopPlugin/Resources/Images/like-off.png b/YTMDesktopPlugin/Resources/Images/like-off.png
new file mode 100644
index 0000000..5582871
Binary files /dev/null and b/YTMDesktopPlugin/Resources/Images/like-off.png differ
diff --git a/YTMDesktopPlugin/Resources/Images/like-on.png b/YTMDesktopPlugin/Resources/Images/like-on.png
new file mode 100644
index 0000000..815fbca
Binary files /dev/null and b/YTMDesktopPlugin/Resources/Images/like-on.png differ
diff --git a/YTMDesktopPlugin/Resources/Images/music-next.png b/YTMDesktopPlugin/Resources/Images/music-next.png
new file mode 100644
index 0000000..c80630f
Binary files /dev/null and b/YTMDesktopPlugin/Resources/Images/music-next.png differ
diff --git a/YTMDesktopPlugin/Resources/Images/music-pause.png b/YTMDesktopPlugin/Resources/Images/music-pause.png
new file mode 100644
index 0000000..58fa37c
Binary files /dev/null and b/YTMDesktopPlugin/Resources/Images/music-pause.png differ
diff --git a/YTMDesktopPlugin/Resources/Images/music-play.png b/YTMDesktopPlugin/Resources/Images/music-play.png
new file mode 100644
index 0000000..a35a318
Binary files /dev/null and b/YTMDesktopPlugin/Resources/Images/music-play.png differ
diff --git a/YTMDesktopPlugin/Resources/Images/music-prev.png b/YTMDesktopPlugin/Resources/Images/music-prev.png
new file mode 100644
index 0000000..6dde1d8
Binary files /dev/null and b/YTMDesktopPlugin/Resources/Images/music-prev.png differ
diff --git a/YTMDesktopPlugin/Resources/Images/repeat.png b/YTMDesktopPlugin/Resources/Images/repeat.png
new file mode 100644
index 0000000..41c0c14
Binary files /dev/null and b/YTMDesktopPlugin/Resources/Images/repeat.png differ
diff --git a/YTMDesktopPlugin/Resources/Images/shuffle.png b/YTMDesktopPlugin/Resources/Images/shuffle.png
new file mode 100644
index 0000000..91b2538
Binary files /dev/null and b/YTMDesktopPlugin/Resources/Images/shuffle.png differ
diff --git a/YTMDesktopPlugin/Resources/Images/volume-down.png b/YTMDesktopPlugin/Resources/Images/volume-down.png
new file mode 100644
index 0000000..42f0b7c
Binary files /dev/null and b/YTMDesktopPlugin/Resources/Images/volume-down.png differ
diff --git a/YTMDesktopPlugin/Resources/Images/volume-mute.png b/YTMDesktopPlugin/Resources/Images/volume-mute.png
new file mode 100644
index 0000000..fc03e09
Binary files /dev/null and b/YTMDesktopPlugin/Resources/Images/volume-mute.png differ
diff --git a/YTMDesktopPlugin/Resources/Images/volume-on.png b/YTMDesktopPlugin/Resources/Images/volume-on.png
new file mode 100644
index 0000000..455a317
Binary files /dev/null and b/YTMDesktopPlugin/Resources/Images/volume-on.png differ
diff --git a/YTMDesktopPlugin/Resources/Images/volume-up.png b/YTMDesktopPlugin/Resources/Images/volume-up.png
new file mode 100644
index 0000000..39e1ebb
Binary files /dev/null and b/YTMDesktopPlugin/Resources/Images/volume-up.png differ
diff --git a/YTMDesktopPlugin/Services/SocketService.cs b/YTMDesktopPlugin/Services/SocketService.cs
new file mode 100644
index 0000000..fc9a01f
--- /dev/null
+++ b/YTMDesktopPlugin/Services/SocketService.cs
@@ -0,0 +1,130 @@
+namespace Loupedeck.YTMDesktopPlugin.Services
+{
+ using System;
+ using System.Reactive.Subjects;
+ using System.Threading.Tasks;
+
+ using Classes.EventArgs;
+
+ using H.Socket.IO;
+
+ using Newtonsoft.Json;
+
+ public sealed class SocketService
+ {
+ public static SocketService Instance => Lazy.Value;
+ public Subject OnTick { get; } = new();
+ public Subject OnError { get; } = new();
+ public Subject OnConnected { get; } = new();
+
+ public BehaviorSubject IsConnected { get; } = new(false);
+
+ private String _host = "127.0.0.1";
+ private String _port = "9863";
+ private String _password = "";
+
+ private static readonly Lazy Lazy = new(() => new SocketService());
+ private SocketIoClient Client { get; set; }
+ private SocketService() => this.SetEvents();
+
+ private void SetEvents()
+ {
+ this.Client = new SocketIoClient();
+ this.OnError.Subscribe(_ => this.IsConnected.OnNext(false));
+ this.Client.ErrorReceived += (_, args) => this.OnError.OnNext(args.Value);
+ this.Client.ExceptionOccurred += (sender, args) => this.OnError.OnNext(args.Value.Message);
+ this.Client.Disconnected += (sender, args) => this.OnError.OnNext(args.Reason);
+ this.Client.Connected += (sender, e) =>
+ {
+ this.IsConnected.OnNext(true);
+ this.OnConnected.OnNext(new OnConnectedEvent(sender, e));
+ };
+ this.Client.On("tick", response =>
+ {
+ this.IsConnected.OnNext(true);
+ this.OnTick.OnNext(response);
+ });
+ }
+
+ public async Task SetSettings(String host = "127.0.0.1", String port = "9863", String password = "")
+ {
+ this._host = host;
+ this._port = port;
+ this._password = password;
+ if (this.IsConnected.Value)
+ {
+ await this.Disconnect();
+ await this.StartConnection();
+ }
+ }
+
+ public async Task StartConnection()
+ {
+ try
+ {
+ await this.Client.ConnectAsync(new Uri($"http://{this._host}:{this._port}/?password={this._password}"));
+ }
+ catch (Exception e)
+ {
+ this.OnError.OnNext(e.Message);
+ await Task.Delay(1000);
+ await this.StartConnection();
+ }
+ }
+
+ #region Track functions
+
+ public async Task TrackPlayPause() => await this.Emit(cmd: "track-play-pause");
+ public async Task TrackPlay() => await this.Emit(cmd: "track-play");
+ public async Task TrackPause() => await this.Emit(cmd: "track-pause");
+ public async Task TrackNext() => await this.Emit(cmd: "track-next");
+ public async Task TrackPrevious() => await this.Emit(cmd: "track-previous");
+ public async Task TrackThumbsUp() => await this.Emit(cmd: "track-thumbs-up");
+ public async Task TrackThumbsDown() => await this.Emit(cmd: "track-thumbs-down");
+
+ #endregion
+
+ #region Player functions
+
+ public async Task PlayerVolumeUp() => await this.Emit(cmd: "player-volume-up");
+ public async Task PlayerVolumeDown() => await this.Emit(cmd: "player-volume-down");
+ public async Task PlayerForward() => await this.Emit(cmd: "player-forward");
+ public async Task PlayerRewind() => await this.Emit(cmd: "player-rewind");
+
+ public async Task PlayerSetSeekbar(Double seconds) =>
+ await this.Emit(cmd: "player-set-seekbar", value: seconds);
+
+ public async Task PlayerSetVolume(Int32 percentage) =>
+ await this.Emit(cmd: "player-set-volume", value: percentage);
+
+ public async Task PlayerSetQueue(Int32 index) =>
+ await this.Emit(cmd: "player-set-queue", value: index);
+
+ public async Task PlayerRepeat() => await this.Emit(cmd: "player-repeat");
+ public async Task PlayerShuffle() => await this.Emit(cmd: "player-shuffle");
+ public async Task PlayerAddLibrary() => await this.Emit(cmd: "player-add-library");
+ public async Task PlayerAddPlaylist(Int32 index) => await this.Emit(cmd: "player-add-playlist", value: index);
+
+ #endregion
+
+ #region Other / request functions
+
+ public async Task ShowLyricsHidden() => await this.Emit(cmd: "show-lyrics-hidden");
+ public async Task RequestPlayer() => await this.Emit("query-player");
+ public async Task RequestTrack() => await this.Emit("query-track");
+ public async Task RequestQueue() => await this.Emit("query-queue");
+ public async Task RequestPlaylist() => await this.Emit("query-playlist");
+ public async Task RequestLyrics() => await this.Emit("query-lyrics");
+
+ #endregion
+
+
+ public async Task Disconnect() => await this.Client.DisconnectAsync();
+
+ private async Task Emit(String evt = "media-commands", String cmd = "player-rewind", Object value = null) =>
+ await this.Client
+ .SendEventAsync(
+ $"[{String.Join(",", $"\"{evt}\"", $"\"{cmd}\"", JsonConvert.SerializeObject(value ?? true))}]")
+ .ConfigureAwait(false);
+ }
+}
\ No newline at end of file
diff --git a/YTMDesktopPlugin/YTMDesktopApplication.cs b/YTMDesktopPlugin/YTMDesktopApplication.cs
new file mode 100644
index 0000000..6aaae1c
--- /dev/null
+++ b/YTMDesktopPlugin/YTMDesktopApplication.cs
@@ -0,0 +1,16 @@
+namespace Loupedeck.YTMDesktopPlugin
+{
+ using System;
+
+ using Services;
+
+ public class YTMDesktopApplication : ClientApplication
+ {
+ public YTMDesktopApplication() => SocketService.Instance.StartConnection().ConfigureAwait(true);
+
+ protected override Boolean IsProcessNameSupported(String processName) =>
+ processName.ContainsNoCase("YouTube Music Desktop App");
+
+ protected override String GetBundleName() => "";
+ }
+}
\ No newline at end of file
diff --git a/YTMDesktopPlugin/YTMDesktopPlugin.cs b/YTMDesktopPlugin/YTMDesktopPlugin.cs
new file mode 100644
index 0000000..a586493
--- /dev/null
+++ b/YTMDesktopPlugin/YTMDesktopPlugin.cs
@@ -0,0 +1,68 @@
+namespace Loupedeck.YTMDesktopPlugin
+{
+ using System;
+ using System.Reactive.Linq;
+ using System.Reactive.Subjects;
+
+ using Enums;
+
+ using Extensions;
+
+ using Helper;
+
+ using Services;
+
+ public class YTMDesktopPlugin : Plugin
+ {
+ public static YTMDesktopPlugin Instance { get; private set; }
+ public override Boolean UsesApplicationApiOnly => true;
+
+ private Subject OnDestroy { get; } = new();
+
+ public override void Load()
+ {
+ Instance = this;
+ this.Info.Icon16x16 = DrawingHelper.ReadImage("icon-16", addPath: "Icon");
+ this.Info.Icon32x32 = DrawingHelper.ReadImage("icon-32", addPath: "Icon");
+ this.Info.Icon48x48 = DrawingHelper.ReadImage("icon-48", addPath: "Icon");
+ this.Info.Icon256x256 = DrawingHelper.ReadImage("icon-256", addPath: "Icon");
+ SocketService.Instance.IsConnected
+ .DistinctUntilChanged()
+ .TakeUntil(this.OnDestroy)
+ .Subscribe(connected =>
+ {
+ if (connected)
+ {
+ this.ResetStatus();
+ }
+ else
+ {
+ this.SetStatus(Loupedeck.PluginStatus.Error, ErrorCode.CouldNotConnect);
+ }
+ });
+ }
+
+ public override void Unload()
+ {
+ this.OnDestroy.OnNext(true);
+ SocketService.Instance.Disconnect().ConfigureAwait(true);
+ }
+
+ public override void RunCommand(String commandName, String parameter)
+ {
+ }
+
+ public override void ApplyAdjustment(String adjustmentName, String parameter, Int32 diff)
+ {
+ }
+
+ public Boolean TryGetSetting(String settingName, out String settingValue) =>
+ this.TryGetPluginSetting(settingName, out settingValue);
+
+ public void SetSetting(String settingName, String settingValue, Boolean backupOnline = false) =>
+ this.SetPluginSetting(settingName, settingValue, backupOnline);
+
+ public void DeleteSetting(String settingName) =>
+ this.DeletePluginSetting(settingName);
+ }
+}
\ No newline at end of file
diff --git a/YTMDesktopPlugin/YTMDesktopPlugin.csproj b/YTMDesktopPlugin/YTMDesktopPlugin.csproj
new file mode 100644
index 0000000..df71276
--- /dev/null
+++ b/YTMDesktopPlugin/YTMDesktopPlugin.csproj
@@ -0,0 +1,202 @@
+
+
+
+
+
+ Debug
+ AnyCPU
+ {E8F03AF3-E7CA-4125-9196-E437B93BA224}
+ Library
+ Properties
+ Loupedeck.YTMDesktopPlugin
+ YTMDesktopPlugin
+ v4.7.2
+ 512
+ 9
+ OnBuildSuccess
+
+
+ $(SolutionDir)obj\
+ $(SolutionDir)bin\
+ $(BaseOutputPath)$(Configuration)\
+ $(SolutionDir)packages\ILMerge.3.0.41\tools\net452\ILMerge.exe
+ C:\Users\domin\AppData\Local\Loupedeck\Plugins\
+
+
+ true
+ full
+ false
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ true
+ false
+ None
+
+
+
+ ..\packages\H.Engine.IO.1.1.7.1\lib\net45\H.Engine.IO.dll
+ True
+
+
+ ..\packages\H.Socket.IO.1.1.7.1\lib\net45\H.Socket.IO.dll
+ True
+
+
+ ..\packages\H.WebSockets.1.2.1\lib\net45\H.WebSockets.dll
+ True
+
+
+ ..\packages\Microsoft.Bcl.AsyncInterfaces.5.0.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll
+ True
+
+
+
+ False
+ C:\Program Files (x86)\Loupedeck\Loupedeck2\Newtonsoft.Json.dll
+
+
+ False
+ C:\Program Files (x86)\Loupedeck\Loupedeck2\PluginApi.dll
+
+
+
+ ..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll
+ True
+
+
+
+
+ ..\packages\System.Drawing.Common.5.0.2\lib\net461\System.Drawing.Common.dll
+ True
+
+
+ ..\packages\System.Memory.4.5.4\lib\net461\System.Memory.dll
+ True
+
+
+
+ ..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll
+ True
+
+
+ ..\packages\System.Reactive.5.0.0\lib\net472\System.Reactive.dll
+ True
+
+
+ ..\packages\System.Runtime.CompilerServices.Unsafe.5.0.0\lib\net45\System.Runtime.CompilerServices.Unsafe.dll
+ True
+
+
+ ..\packages\System.Text.Encodings.Web.5.0.1\lib\net461\System.Text.Encodings.Web.dll
+ True
+
+
+ ..\packages\System.Text.Json.5.0.2\lib\net461\System.Text.Json.dll
+ True
+
+
+ ..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll
+ True
+
+
+ ..\packages\System.ValueTuple.4.5.0\lib\net47\System.ValueTuple.dll
+ True
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Form
+
+
+ Settings.cs
+
+
+
+
+
+
+
+
+
+ Settings.cs
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ YTMDesktopPlugin\LICENSE
+
+
+ YTMDesktopPlugin\LoupedeckPackage.yaml
+
+
+
+
+
+ This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105.The missing file is {0}.
+
+
+
+
+
+ $(ILMergeConsolePath) /out:$(PluginPath)YTMDesktopPlugin.dll $(OutputPath)YTMDesktopPlugin.dll $(OutputPath)H.Engine.IO.dll $(OutputPath)H.Socket.IO.dll $(OutputPath)H.WebSockets.dll $(OutputPath)Microsoft.Bcl.AsyncInterfaces.dll $(OutputPath)System.Reactive.dll $(OutputPath)System.Text.Encodings.Web.dll $(OutputPath)System.Text.Json.dll $(OutputPath)System.Threading.Tasks.Extensions.dll $(OutputPath)System.ValueTuple.dll $(OutputPath)System.Drawing.Common.dll
+
+
\ No newline at end of file
diff --git a/YTMDesktopPlugin/packages.config b/YTMDesktopPlugin/packages.config
new file mode 100644
index 0000000..b8abf6a
--- /dev/null
+++ b/YTMDesktopPlugin/packages.config
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/commitlint.config.js b/commitlint.config.js
new file mode 100644
index 0000000..4345d7c
--- /dev/null
+++ b/commitlint.config.js
@@ -0,0 +1 @@
+module.exports = {extends: ['@commitlint/config-conventional']};
\ No newline at end of file
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..d9f0424
--- /dev/null
+++ b/package.json
@@ -0,0 +1,30 @@
+{
+ "name": "ytmdesktop-plugin",
+ "version": "1.0.0",
+ "description": "",
+ "main": ".versionrc.js",
+ "scripts": {
+ "release": "standard-version"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/XeroxDev/Loupedeck-plugin-YTMDesktop.git"
+ },
+ "author": "",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/XeroxDev/Loupedeck-plugin-YTMDesktop/issues"
+ },
+ "homepage": "https://github.com/XeroxDev/Loupedeck-plugin-YTMDesktop#readme",
+ "devDependencies": {
+ "@commitlint/cli": "^13.1.0",
+ "@commitlint/config-conventional": "^13.1.0",
+ "husky": "3",
+ "standard-version": "^9.3.1"
+ },
+ "husky": {
+ "hooks": {
+ "commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
+ }
+ }
+}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
new file mode 100644
index 0000000..33eb32f
--- /dev/null
+++ b/pnpm-lock.yaml
@@ -0,0 +1,1870 @@
+lockfileVersion: 5.3
+
+specifiers:
+ '@commitlint/cli': ^13.1.0
+ '@commitlint/config-conventional': ^13.1.0
+ husky: '3'
+ standard-version: ^9.3.1
+
+devDependencies:
+ '@commitlint/cli': 13.1.0
+ '@commitlint/config-conventional': 13.1.0
+ husky: 3.1.0
+ standard-version: 9.3.1
+
+packages:
+
+ /@babel/code-frame/7.14.5:
+ resolution: {integrity: sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@babel/highlight': 7.14.5
+ dev: true
+
+ /@babel/helper-validator-identifier/7.14.9:
+ resolution: {integrity: sha512-pQYxPY0UP6IHISRitNe8bsijHex4TWZXi2HwKVsjPiltzlhse2znVcm9Ace510VT1kxIHjGJCZZQBX2gJDbo0g==}
+ engines: {node: '>=6.9.0'}
+ dev: true
+
+ /@babel/highlight/7.14.5:
+ resolution: {integrity: sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@babel/helper-validator-identifier': 7.14.9
+ chalk: 2.4.2
+ js-tokens: 4.0.0
+ dev: true
+
+ /@commitlint/cli/13.1.0:
+ resolution: {integrity: sha512-xN/uNYWtGTva5OMSd+xA6e6/c2jk8av7MUbdd6w2cw89u6z3fAWoyiH87X0ewdSMNYmW/6B3L/2dIVGHRDID5w==}
+ engines: {node: '>=v12'}
+ hasBin: true
+ dependencies:
+ '@commitlint/format': 13.1.0
+ '@commitlint/lint': 13.1.0
+ '@commitlint/load': 13.1.0
+ '@commitlint/read': 13.1.0
+ '@commitlint/types': 13.1.0
+ lodash: 4.17.21
+ resolve-from: 5.0.0
+ resolve-global: 1.0.0
+ yargs: 17.1.1
+ dev: true
+
+ /@commitlint/config-conventional/13.1.0:
+ resolution: {integrity: sha512-zukJXqdr6jtMiVRy3tTHmwgKcUMGfqKDEskRigc5W3k2aYF4gBAtCEjMAJGZgSQE4DMcHeok0pEV2ANmTpb0cw==}
+ engines: {node: '>=v12'}
+ dependencies:
+ conventional-changelog-conventionalcommits: 4.6.0
+ dev: true
+
+ /@commitlint/ensure/13.1.0:
+ resolution: {integrity: sha512-NRGyjOdZQnlYwm9it//BZJ2Vm+4x7G9rEnHpLCvNKYY0c6RA8Qf7hamLAB8dWO12RLuFt06JaOpHZoTt/gHutA==}
+ engines: {node: '>=v12'}
+ dependencies:
+ '@commitlint/types': 13.1.0
+ lodash: 4.17.21
+ dev: true
+
+ /@commitlint/execute-rule/13.0.0:
+ resolution: {integrity: sha512-lBz2bJhNAgkkU/rFMAw3XBNujbxhxlaFHY3lfKB/MxpAa+pIfmWB3ig9i1VKe0wCvujk02O0WiMleNaRn2KJqw==}
+ engines: {node: '>=v12'}
+ dev: true
+
+ /@commitlint/format/13.1.0:
+ resolution: {integrity: sha512-n46rYvzf+6Sm99TJjTLjJBkjm6JVcklt31lDO5Q+pCIV0NnJ4qIUcwa6wIL9a9Vqb1XzlMgtp27E0zyYArkvSg==}
+ engines: {node: '>=v12'}
+ dependencies:
+ '@commitlint/types': 13.1.0
+ chalk: 4.1.2
+ dev: true
+
+ /@commitlint/is-ignored/13.1.0:
+ resolution: {integrity: sha512-P6zenLE5Tn3FTNjRzmL9+/KooTXEI0khA2TmUbuei9KiycemeO4q7Xk7w7aXwFPNAbN0O9oI7z3z7cFpzKJWmQ==}
+ engines: {node: '>=v12'}
+ dependencies:
+ '@commitlint/types': 13.1.0
+ semver: 7.3.5
+ dev: true
+
+ /@commitlint/lint/13.1.0:
+ resolution: {integrity: sha512-qH9AYSQDDTaSWSdtOvB3G1RdPpcYSgddAdFYqpFewlKQ1GJj/L+sM7vwqCG7/ip6AiM04Sry1sgmFzaEoFREUA==}
+ engines: {node: '>=v12'}
+ dependencies:
+ '@commitlint/is-ignored': 13.1.0
+ '@commitlint/parse': 13.1.0
+ '@commitlint/rules': 13.1.0
+ '@commitlint/types': 13.1.0
+ dev: true
+
+ /@commitlint/load/13.1.0:
+ resolution: {integrity: sha512-zlZbjJCWnWmBOSwTXis8H7I6pYk6JbDwOCuARA6B9Y/qt2PD+NCo0E/7EuaaFoxjHl+o56QR5QttuMBrf+BJzg==}
+ engines: {node: '>=v12'}
+ dependencies:
+ '@commitlint/execute-rule': 13.0.0
+ '@commitlint/resolve-extends': 13.0.0
+ '@commitlint/types': 13.1.0
+ chalk: 4.1.2
+ cosmiconfig: 7.0.1
+ lodash: 4.17.21
+ resolve-from: 5.0.0
+ dev: true
+
+ /@commitlint/message/13.0.0:
+ resolution: {integrity: sha512-W/pxhesVEk8747BEWJ+VGQ9ILHmCV27/pEwJ0hGny1wqVquUR8SxvScRCbUjHCB1YtWX4dEnOPXOS9CLH/CX7A==}
+ engines: {node: '>=v12'}
+ dev: true
+
+ /@commitlint/parse/13.1.0:
+ resolution: {integrity: sha512-xFybZcqBiKVjt6vTStvQkySWEUYPI0AcO4QQELyy29o8EzYZqWkhUfrb7K61fWiHsplWL1iL6F3qCLoxSgTcrg==}
+ engines: {node: '>=v12'}
+ dependencies:
+ '@commitlint/types': 13.1.0
+ conventional-changelog-angular: 5.0.12
+ conventional-commits-parser: 3.2.1
+ dev: true
+
+ /@commitlint/read/13.1.0:
+ resolution: {integrity: sha512-NrVe23GMKyL6i1yDJD8IpqCBzhzoS3wtLfDj8QBzc01Ov1cYBmDojzvBklypGb+MLJM1NbzmRM4PR5pNX0U/NQ==}
+ engines: {node: '>=v12'}
+ dependencies:
+ '@commitlint/top-level': 13.0.0
+ '@commitlint/types': 13.1.0
+ fs-extra: 10.0.0
+ git-raw-commits: 2.0.10
+ dev: true
+
+ /@commitlint/resolve-extends/13.0.0:
+ resolution: {integrity: sha512-1SyaE+UOsYTkQlTPUOoj4NwxQhGFtYildVS/d0TJuK8a9uAJLw7bhCLH2PEeH5cC2D1do4Eqhx/3bLDrSLH3hg==}
+ engines: {node: '>=v12'}
+ dependencies:
+ import-fresh: 3.3.0
+ lodash: 4.17.21
+ resolve-from: 5.0.0
+ resolve-global: 1.0.0
+ dev: true
+
+ /@commitlint/rules/13.1.0:
+ resolution: {integrity: sha512-b6F+vBqEXsHVghrhomG0Y6YJimHZqkzZ0n5QEpk03dpBXH2OnsezpTw5e+GvbyYCc7PutGbYVQkytuv+7xCxYA==}
+ engines: {node: '>=v12'}
+ dependencies:
+ '@commitlint/ensure': 13.1.0
+ '@commitlint/message': 13.0.0
+ '@commitlint/to-lines': 13.0.0
+ '@commitlint/types': 13.1.0
+ execa: 5.1.1
+ dev: true
+
+ /@commitlint/to-lines/13.0.0:
+ resolution: {integrity: sha512-mzxWwCio1M4/kG9/69TTYqrraQ66LmtJCYTzAZdZ2eJX3I5w52pSjyP/DJzAUVmmJCYf2Kw3s+RtNVShtnZ+Rw==}
+ engines: {node: '>=v12'}
+ dev: true
+
+ /@commitlint/top-level/13.0.0:
+ resolution: {integrity: sha512-baBy3MZBF28sR93yFezd4a5TdHsbXaakeladfHK9dOcGdXo9oQe3GS5hP3BmlN680D6AiQSN7QPgEJgrNUWUCg==}
+ engines: {node: '>=v12'}
+ dependencies:
+ find-up: 5.0.0
+ dev: true
+
+ /@commitlint/types/13.1.0:
+ resolution: {integrity: sha512-zcVjuT+OfKt8h91vhBxt05RMcTGEx6DM7Q9QZeuMbXFk6xgbsSEDMMapbJPA1bCZ81fa/1OQBijSYPrKvtt06g==}
+ engines: {node: '>=v12'}
+ dependencies:
+ chalk: 4.1.2
+ dev: true
+
+ /@hutson/parse-repository-url/3.0.2:
+ resolution: {integrity: sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q==}
+ engines: {node: '>=6.9.0'}
+ dev: true
+
+ /@types/minimist/1.2.2:
+ resolution: {integrity: sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==}
+ dev: true
+
+ /@types/normalize-package-data/2.4.1:
+ resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==}
+ dev: true
+
+ /@types/parse-json/4.0.0:
+ resolution: {integrity: sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==}
+ dev: true
+
+ /JSONStream/1.3.5:
+ resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==}
+ hasBin: true
+ dependencies:
+ jsonparse: 1.3.1
+ through: 2.3.8
+ dev: true
+
+ /add-stream/1.0.0:
+ resolution: {integrity: sha1-anmQQ3ynNtXhKI25K9MmbV9csqo=}
+ dev: true
+
+ /ansi-regex/5.0.0:
+ resolution: {integrity: sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==}
+ engines: {node: '>=8'}
+ dev: true
+
+ /ansi-styles/3.2.1:
+ resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==}
+ engines: {node: '>=4'}
+ dependencies:
+ color-convert: 1.9.3
+ dev: true
+
+ /ansi-styles/4.3.0:
+ resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
+ engines: {node: '>=8'}
+ dependencies:
+ color-convert: 2.0.1
+ dev: true
+
+ /argparse/1.0.10:
+ resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==}
+ dependencies:
+ sprintf-js: 1.0.3
+ dev: true
+
+ /array-ify/1.0.0:
+ resolution: {integrity: sha1-nlKHYrSpBmrRY6aWKjZEGOlibs4=}
+ dev: true
+
+ /arrify/1.0.1:
+ resolution: {integrity: sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=}
+ engines: {node: '>=0.10.0'}
+ dev: true
+
+ /balanced-match/1.0.2:
+ resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
+ dev: true
+
+ /brace-expansion/1.1.11:
+ resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
+ dependencies:
+ balanced-match: 1.0.2
+ concat-map: 0.0.1
+ dev: true
+
+ /buffer-from/1.1.2:
+ resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
+ dev: true
+
+ /caller-callsite/2.0.0:
+ resolution: {integrity: sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=}
+ engines: {node: '>=4'}
+ dependencies:
+ callsites: 2.0.0
+ dev: true
+
+ /caller-path/2.0.0:
+ resolution: {integrity: sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=}
+ engines: {node: '>=4'}
+ dependencies:
+ caller-callsite: 2.0.0
+ dev: true
+
+ /callsites/2.0.0:
+ resolution: {integrity: sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=}
+ engines: {node: '>=4'}
+ dev: true
+
+ /callsites/3.1.0:
+ resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
+ engines: {node: '>=6'}
+ dev: true
+
+ /camelcase-keys/6.2.2:
+ resolution: {integrity: sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==}
+ engines: {node: '>=8'}
+ dependencies:
+ camelcase: 5.3.1
+ map-obj: 4.2.1
+ quick-lru: 4.0.1
+ dev: true
+
+ /camelcase/5.3.1:
+ resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==}
+ engines: {node: '>=6'}
+ dev: true
+
+ /chalk/2.4.2:
+ resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
+ engines: {node: '>=4'}
+ dependencies:
+ ansi-styles: 3.2.1
+ escape-string-regexp: 1.0.5
+ supports-color: 5.5.0
+ dev: true
+
+ /chalk/4.1.2:
+ resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
+ engines: {node: '>=10'}
+ dependencies:
+ ansi-styles: 4.3.0
+ supports-color: 7.2.0
+ dev: true
+
+ /ci-info/2.0.0:
+ resolution: {integrity: sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==}
+ dev: true
+
+ /cliui/7.0.4:
+ resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==}
+ dependencies:
+ string-width: 4.2.2
+ strip-ansi: 6.0.0
+ wrap-ansi: 7.0.0
+ dev: true
+
+ /color-convert/1.9.3:
+ resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
+ dependencies:
+ color-name: 1.1.3
+ dev: true
+
+ /color-convert/2.0.1:
+ resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
+ engines: {node: '>=7.0.0'}
+ dependencies:
+ color-name: 1.1.4
+ dev: true
+
+ /color-name/1.1.3:
+ resolution: {integrity: sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=}
+ dev: true
+
+ /color-name/1.1.4:
+ resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
+ dev: true
+
+ /compare-func/2.0.0:
+ resolution: {integrity: sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==}
+ dependencies:
+ array-ify: 1.0.0
+ dot-prop: 5.3.0
+ dev: true
+
+ /concat-map/0.0.1:
+ resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=}
+ dev: true
+
+ /concat-stream/2.0.0:
+ resolution: {integrity: sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==}
+ engines: {'0': node >= 6.0}
+ dependencies:
+ buffer-from: 1.1.2
+ inherits: 2.0.4
+ readable-stream: 3.6.0
+ typedarray: 0.0.6
+ dev: true
+
+ /conventional-changelog-angular/5.0.12:
+ resolution: {integrity: sha512-5GLsbnkR/7A89RyHLvvoExbiGbd9xKdKqDTrArnPbOqBqG/2wIosu0fHwpeIRI8Tl94MhVNBXcLJZl92ZQ5USw==}
+ engines: {node: '>=10'}
+ dependencies:
+ compare-func: 2.0.0
+ q: 1.5.1
+ dev: true
+
+ /conventional-changelog-atom/2.0.8:
+ resolution: {integrity: sha512-xo6v46icsFTK3bb7dY/8m2qvc8sZemRgdqLb/bjpBsH2UyOS8rKNTgcb5025Hri6IpANPApbXMg15QLb1LJpBw==}
+ engines: {node: '>=10'}
+ dependencies:
+ q: 1.5.1
+ dev: true
+
+ /conventional-changelog-codemirror/2.0.8:
+ resolution: {integrity: sha512-z5DAsn3uj1Vfp7po3gpt2Boc+Bdwmw2++ZHa5Ak9k0UKsYAO5mH1UBTN0qSCuJZREIhX6WU4E1p3IW2oRCNzQw==}
+ engines: {node: '>=10'}
+ dependencies:
+ q: 1.5.1
+ dev: true
+
+ /conventional-changelog-config-spec/2.1.0:
+ resolution: {integrity: sha512-IpVePh16EbbB02V+UA+HQnnPIohgXvJRxHcS5+Uwk4AT5LjzCZJm5sp/yqs5C6KZJ1jMsV4paEV13BN1pvDuxQ==}
+ dev: true
+
+ /conventional-changelog-conventionalcommits/4.5.0:
+ resolution: {integrity: sha512-buge9xDvjjOxJlyxUnar/+6i/aVEVGA7EEh4OafBCXPlLUQPGbRUBhBUveWRxzvR8TEjhKEP4BdepnpG2FSZXw==}
+ engines: {node: '>=10'}
+ dependencies:
+ compare-func: 2.0.0
+ lodash: 4.17.21
+ q: 1.5.1
+ dev: true
+
+ /conventional-changelog-conventionalcommits/4.6.0:
+ resolution: {integrity: sha512-sj9tj3z5cnHaSJCYObA9nISf7eq/YjscLPoq6nmew4SiOjxqL2KRpK20fjnjVbpNDjJ2HR3MoVcWKXwbVvzS0A==}
+ engines: {node: '>=10'}
+ dependencies:
+ compare-func: 2.0.0
+ lodash: 4.17.21
+ q: 1.5.1
+ dev: true
+
+ /conventional-changelog-core/4.2.3:
+ resolution: {integrity: sha512-MwnZjIoMRL3jtPH5GywVNqetGILC7g6RQFvdb8LRU/fA/338JbeWAku3PZ8yQ+mtVRViiISqJlb0sOz0htBZig==}
+ engines: {node: '>=10'}
+ dependencies:
+ add-stream: 1.0.0
+ conventional-changelog-writer: 5.0.0
+ conventional-commits-parser: 3.2.1
+ dateformat: 3.0.3
+ get-pkg-repo: 4.1.2
+ git-raw-commits: 2.0.10
+ git-remote-origin-url: 2.0.0
+ git-semver-tags: 4.1.1
+ lodash: 4.17.21
+ normalize-package-data: 3.0.3
+ q: 1.5.1
+ read-pkg: 3.0.0
+ read-pkg-up: 3.0.0
+ through2: 4.0.2
+ dev: true
+
+ /conventional-changelog-ember/2.0.9:
+ resolution: {integrity: sha512-ulzIReoZEvZCBDhcNYfDIsLTHzYHc7awh+eI44ZtV5cx6LVxLlVtEmcO+2/kGIHGtw+qVabJYjdI5cJOQgXh1A==}
+ engines: {node: '>=10'}
+ dependencies:
+ q: 1.5.1
+ dev: true
+
+ /conventional-changelog-eslint/3.0.9:
+ resolution: {integrity: sha512-6NpUCMgU8qmWmyAMSZO5NrRd7rTgErjrm4VASam2u5jrZS0n38V7Y9CzTtLT2qwz5xEChDR4BduoWIr8TfwvXA==}
+ engines: {node: '>=10'}
+ dependencies:
+ q: 1.5.1
+ dev: true
+
+ /conventional-changelog-express/2.0.6:
+ resolution: {integrity: sha512-SDez2f3iVJw6V563O3pRtNwXtQaSmEfTCaTBPCqn0oG0mfkq0rX4hHBq5P7De2MncoRixrALj3u3oQsNK+Q0pQ==}
+ engines: {node: '>=10'}
+ dependencies:
+ q: 1.5.1
+ dev: true
+
+ /conventional-changelog-jquery/3.0.11:
+ resolution: {integrity: sha512-x8AWz5/Td55F7+o/9LQ6cQIPwrCjfJQ5Zmfqi8thwUEKHstEn4kTIofXub7plf1xvFA2TqhZlq7fy5OmV6BOMw==}
+ engines: {node: '>=10'}
+ dependencies:
+ q: 1.5.1
+ dev: true
+
+ /conventional-changelog-jshint/2.0.9:
+ resolution: {integrity: sha512-wMLdaIzq6TNnMHMy31hql02OEQ8nCQfExw1SE0hYL5KvU+JCTuPaDO+7JiogGT2gJAxiUGATdtYYfh+nT+6riA==}
+ engines: {node: '>=10'}
+ dependencies:
+ compare-func: 2.0.0
+ q: 1.5.1
+ dev: true
+
+ /conventional-changelog-preset-loader/2.3.4:
+ resolution: {integrity: sha512-GEKRWkrSAZeTq5+YjUZOYxdHq+ci4dNwHvpaBC3+ENalzFWuCWa9EZXSuZBpkr72sMdKB+1fyDV4takK1Lf58g==}
+ engines: {node: '>=10'}
+ dev: true
+
+ /conventional-changelog-writer/5.0.0:
+ resolution: {integrity: sha512-HnDh9QHLNWfL6E1uHz6krZEQOgm8hN7z/m7tT16xwd802fwgMN0Wqd7AQYVkhpsjDUx/99oo+nGgvKF657XP5g==}
+ engines: {node: '>=10'}
+ hasBin: true
+ dependencies:
+ conventional-commits-filter: 2.0.7
+ dateformat: 3.0.3
+ handlebars: 4.7.7
+ json-stringify-safe: 5.0.1
+ lodash: 4.17.21
+ meow: 8.1.2
+ semver: 6.3.0
+ split: 1.0.1
+ through2: 4.0.2
+ dev: true
+
+ /conventional-changelog/3.1.24:
+ resolution: {integrity: sha512-ed6k8PO00UVvhExYohroVPXcOJ/K1N0/drJHx/faTH37OIZthlecuLIRX/T6uOp682CAoVoFpu+sSEaeuH6Asg==}
+ engines: {node: '>=10'}
+ dependencies:
+ conventional-changelog-angular: 5.0.12
+ conventional-changelog-atom: 2.0.8
+ conventional-changelog-codemirror: 2.0.8
+ conventional-changelog-conventionalcommits: 4.5.0
+ conventional-changelog-core: 4.2.3
+ conventional-changelog-ember: 2.0.9
+ conventional-changelog-eslint: 3.0.9
+ conventional-changelog-express: 2.0.6
+ conventional-changelog-jquery: 3.0.11
+ conventional-changelog-jshint: 2.0.9
+ conventional-changelog-preset-loader: 2.3.4
+ dev: true
+
+ /conventional-commits-filter/2.0.7:
+ resolution: {integrity: sha512-ASS9SamOP4TbCClsRHxIHXRfcGCnIoQqkvAzCSbZzTFLfcTqJVugB0agRgsEELsqaeWgsXv513eS116wnlSSPA==}
+ engines: {node: '>=10'}
+ dependencies:
+ lodash.ismatch: 4.4.0
+ modify-values: 1.0.1
+ dev: true
+
+ /conventional-commits-parser/3.2.1:
+ resolution: {integrity: sha512-OG9kQtmMZBJD/32NEw5IhN5+HnBqVjy03eC+I71I0oQRFA5rOgA4OtPOYG7mz1GkCfCNxn3gKIX8EiHJYuf1cA==}
+ engines: {node: '>=10'}
+ hasBin: true
+ dependencies:
+ is-text-path: 1.0.1
+ JSONStream: 1.3.5
+ lodash: 4.17.21
+ meow: 8.1.2
+ split2: 3.2.2
+ through2: 4.0.2
+ trim-off-newlines: 1.0.1
+ dev: true
+
+ /conventional-recommended-bump/6.1.0:
+ resolution: {integrity: sha512-uiApbSiNGM/kkdL9GTOLAqC4hbptObFo4wW2QRyHsKciGAfQuLU1ShZ1BIVI/+K2BE/W1AWYQMCXAsv4dyKPaw==}
+ engines: {node: '>=10'}
+ hasBin: true
+ dependencies:
+ concat-stream: 2.0.0
+ conventional-changelog-preset-loader: 2.3.4
+ conventional-commits-filter: 2.0.7
+ conventional-commits-parser: 3.2.1
+ git-raw-commits: 2.0.10
+ git-semver-tags: 4.1.1
+ meow: 8.1.2
+ q: 1.5.1
+ dev: true
+
+ /core-util-is/1.0.3:
+ resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==}
+ dev: true
+
+ /cosmiconfig/5.2.1:
+ resolution: {integrity: sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==}
+ engines: {node: '>=4'}
+ dependencies:
+ import-fresh: 2.0.0
+ is-directory: 0.3.1
+ js-yaml: 3.14.1
+ parse-json: 4.0.0
+ dev: true
+
+ /cosmiconfig/7.0.1:
+ resolution: {integrity: sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ==}
+ engines: {node: '>=10'}
+ dependencies:
+ '@types/parse-json': 4.0.0
+ import-fresh: 3.3.0
+ parse-json: 5.2.0
+ path-type: 4.0.0
+ yaml: 1.10.2
+ dev: true
+
+ /cross-spawn/6.0.5:
+ resolution: {integrity: sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==}
+ engines: {node: '>=4.8'}
+ dependencies:
+ nice-try: 1.0.5
+ path-key: 2.0.1
+ semver: 5.7.1
+ shebang-command: 1.2.0
+ which: 1.3.1
+ dev: true
+
+ /cross-spawn/7.0.3:
+ resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
+ engines: {node: '>= 8'}
+ dependencies:
+ path-key: 3.1.1
+ shebang-command: 2.0.0
+ which: 2.0.2
+ dev: true
+
+ /dargs/7.0.0:
+ resolution: {integrity: sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg==}
+ engines: {node: '>=8'}
+ dev: true
+
+ /dateformat/3.0.3:
+ resolution: {integrity: sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==}
+ dev: true
+
+ /decamelize-keys/1.1.0:
+ resolution: {integrity: sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk=}
+ engines: {node: '>=0.10.0'}
+ dependencies:
+ decamelize: 1.2.0
+ map-obj: 1.0.1
+ dev: true
+
+ /decamelize/1.2.0:
+ resolution: {integrity: sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=}
+ engines: {node: '>=0.10.0'}
+ dev: true
+
+ /detect-indent/6.1.0:
+ resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==}
+ engines: {node: '>=8'}
+ dev: true
+
+ /detect-newline/3.1.0:
+ resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==}
+ engines: {node: '>=8'}
+ dev: true
+
+ /dot-prop/5.3.0:
+ resolution: {integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==}
+ engines: {node: '>=8'}
+ dependencies:
+ is-obj: 2.0.0
+ dev: true
+
+ /dotgitignore/2.1.0:
+ resolution: {integrity: sha512-sCm11ak2oY6DglEPpCB8TixLjWAxd3kJTs6UIcSasNYxXdFPV+YKlye92c8H4kKFqV5qYMIh7d+cYecEg0dIkA==}
+ engines: {node: '>=6'}
+ dependencies:
+ find-up: 3.0.0
+ minimatch: 3.0.4
+ dev: true
+
+ /emoji-regex/8.0.0:
+ resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
+ dev: true
+
+ /end-of-stream/1.4.4:
+ resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==}
+ dependencies:
+ once: 1.4.0
+ dev: true
+
+ /error-ex/1.3.2:
+ resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==}
+ dependencies:
+ is-arrayish: 0.2.1
+ dev: true
+
+ /escalade/3.1.1:
+ resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==}
+ engines: {node: '>=6'}
+ dev: true
+
+ /escape-string-regexp/1.0.5:
+ resolution: {integrity: sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=}
+ engines: {node: '>=0.8.0'}
+ dev: true
+
+ /esprima/4.0.1:
+ resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==}
+ engines: {node: '>=4'}
+ hasBin: true
+ dev: true
+
+ /execa/1.0.0:
+ resolution: {integrity: sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==}
+ engines: {node: '>=6'}
+ dependencies:
+ cross-spawn: 6.0.5
+ get-stream: 4.1.0
+ is-stream: 1.1.0
+ npm-run-path: 2.0.2
+ p-finally: 1.0.0
+ signal-exit: 3.0.3
+ strip-eof: 1.0.0
+ dev: true
+
+ /execa/5.1.1:
+ resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==}
+ engines: {node: '>=10'}
+ dependencies:
+ cross-spawn: 7.0.3
+ get-stream: 6.0.1
+ human-signals: 2.1.0
+ is-stream: 2.0.1
+ merge-stream: 2.0.0
+ npm-run-path: 4.0.1
+ onetime: 5.1.2
+ signal-exit: 3.0.3
+ strip-final-newline: 2.0.0
+ dev: true
+
+ /figures/3.2.0:
+ resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==}
+ engines: {node: '>=8'}
+ dependencies:
+ escape-string-regexp: 1.0.5
+ dev: true
+
+ /find-up/2.1.0:
+ resolution: {integrity: sha1-RdG35QbHF93UgndaK3eSCjwMV6c=}
+ engines: {node: '>=4'}
+ dependencies:
+ locate-path: 2.0.0
+ dev: true
+
+ /find-up/3.0.0:
+ resolution: {integrity: sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==}
+ engines: {node: '>=6'}
+ dependencies:
+ locate-path: 3.0.0
+ dev: true
+
+ /find-up/4.1.0:
+ resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==}
+ engines: {node: '>=8'}
+ dependencies:
+ locate-path: 5.0.0
+ path-exists: 4.0.0
+ dev: true
+
+ /find-up/5.0.0:
+ resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
+ engines: {node: '>=10'}
+ dependencies:
+ locate-path: 6.0.0
+ path-exists: 4.0.0
+ dev: true
+
+ /fs-access/1.0.1:
+ resolution: {integrity: sha1-1qh/JiJxzv6+wwxVNAf7mV2od3o=}
+ engines: {node: '>=0.10.0'}
+ dependencies:
+ null-check: 1.0.0
+ dev: true
+
+ /fs-extra/10.0.0:
+ resolution: {integrity: sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ==}
+ engines: {node: '>=12'}
+ dependencies:
+ graceful-fs: 4.2.8
+ jsonfile: 6.1.0
+ universalify: 2.0.0
+ dev: true
+
+ /function-bind/1.1.1:
+ resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==}
+ dev: true
+
+ /get-caller-file/2.0.5:
+ resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
+ engines: {node: 6.* || 8.* || >= 10.*}
+ dev: true
+
+ /get-pkg-repo/4.1.2:
+ resolution: {integrity: sha512-/FjamZL9cBYllEbReZkxF2IMh80d8TJoC4e3bmLNif8ibHw95aj0N/tzqK0kZz9eU/3w3dL6lF4fnnX/sDdW3A==}
+ engines: {node: '>=6.9.0'}
+ hasBin: true
+ dependencies:
+ '@hutson/parse-repository-url': 3.0.2
+ hosted-git-info: 4.0.2
+ meow: 7.1.1
+ through2: 2.0.5
+ dev: true
+
+ /get-stdin/7.0.0:
+ resolution: {integrity: sha512-zRKcywvrXlXsA0v0i9Io4KDRaAw7+a1ZpjRwl9Wox8PFlVCCHra7E9c4kqXCoCM9nR5tBkaTTZRBoCm60bFqTQ==}
+ engines: {node: '>=8'}
+ dev: true
+
+ /get-stream/4.1.0:
+ resolution: {integrity: sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==}
+ engines: {node: '>=6'}
+ dependencies:
+ pump: 3.0.0
+ dev: true
+
+ /get-stream/6.0.1:
+ resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==}
+ engines: {node: '>=10'}
+ dev: true
+
+ /git-raw-commits/2.0.10:
+ resolution: {integrity: sha512-sHhX5lsbG9SOO6yXdlwgEMQ/ljIn7qMpAbJZCGfXX2fq5T8M5SrDnpYk9/4HswTildcIqatsWa91vty6VhWSaQ==}
+ engines: {node: '>=10'}
+ hasBin: true
+ dependencies:
+ dargs: 7.0.0
+ lodash: 4.17.21
+ meow: 8.1.2
+ split2: 3.2.2
+ through2: 4.0.2
+ dev: true
+
+ /git-remote-origin-url/2.0.0:
+ resolution: {integrity: sha1-UoJlna4hBxRaERJhEq0yFuxfpl8=}
+ engines: {node: '>=4'}
+ dependencies:
+ gitconfiglocal: 1.0.0
+ pify: 2.3.0
+ dev: true
+
+ /git-semver-tags/4.1.1:
+ resolution: {integrity: sha512-OWyMt5zBe7xFs8vglMmhM9lRQzCWL3WjHtxNNfJTMngGym7pC1kh8sP6jevfydJ6LP3ZvGxfb6ABYgPUM0mtsA==}
+ engines: {node: '>=10'}
+ hasBin: true
+ dependencies:
+ meow: 8.1.2
+ semver: 6.3.0
+ dev: true
+
+ /gitconfiglocal/1.0.0:
+ resolution: {integrity: sha1-QdBF84UaXqiPA/JMocYXgRRGS5s=}
+ dependencies:
+ ini: 1.3.8
+ dev: true
+
+ /global-dirs/0.1.1:
+ resolution: {integrity: sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=}
+ engines: {node: '>=4'}
+ dependencies:
+ ini: 1.3.8
+ dev: true
+
+ /graceful-fs/4.2.8:
+ resolution: {integrity: sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==}
+ dev: true
+
+ /handlebars/4.7.7:
+ resolution: {integrity: sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==}
+ engines: {node: '>=0.4.7'}
+ hasBin: true
+ dependencies:
+ minimist: 1.2.5
+ neo-async: 2.6.2
+ source-map: 0.6.1
+ wordwrap: 1.0.0
+ optionalDependencies:
+ uglify-js: 3.14.1
+ dev: true
+
+ /hard-rejection/2.1.0:
+ resolution: {integrity: sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==}
+ engines: {node: '>=6'}
+ dev: true
+
+ /has-flag/3.0.0:
+ resolution: {integrity: sha1-tdRU3CGZriJWmfNGfloH87lVuv0=}
+ engines: {node: '>=4'}
+ dev: true
+
+ /has-flag/4.0.0:
+ resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
+ engines: {node: '>=8'}
+ dev: true
+
+ /has/1.0.3:
+ resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==}
+ engines: {node: '>= 0.4.0'}
+ dependencies:
+ function-bind: 1.1.1
+ dev: true
+
+ /hosted-git-info/2.8.9:
+ resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==}
+ dev: true
+
+ /hosted-git-info/4.0.2:
+ resolution: {integrity: sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg==}
+ engines: {node: '>=10'}
+ dependencies:
+ lru-cache: 6.0.0
+ dev: true
+
+ /human-signals/2.1.0:
+ resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==}
+ engines: {node: '>=10.17.0'}
+ dev: true
+
+ /husky/3.1.0:
+ resolution: {integrity: sha512-FJkPoHHB+6s4a+jwPqBudBDvYZsoQW5/HBuMSehC8qDiCe50kpcxeqFoDSlow+9I6wg47YxBoT3WxaURlrDIIQ==}
+ engines: {node: '>=8.6.0'}
+ hasBin: true
+ requiresBuild: true
+ dependencies:
+ chalk: 2.4.2
+ ci-info: 2.0.0
+ cosmiconfig: 5.2.1
+ execa: 1.0.0
+ get-stdin: 7.0.0
+ opencollective-postinstall: 2.0.3
+ pkg-dir: 4.2.0
+ please-upgrade-node: 3.2.0
+ read-pkg: 5.2.0
+ run-node: 1.0.0
+ slash: 3.0.0
+ dev: true
+
+ /import-fresh/2.0.0:
+ resolution: {integrity: sha1-2BNVwVYS04bGH53dOSLUMEgipUY=}
+ engines: {node: '>=4'}
+ dependencies:
+ caller-path: 2.0.0
+ resolve-from: 3.0.0
+ dev: true
+
+ /import-fresh/3.3.0:
+ resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==}
+ engines: {node: '>=6'}
+ dependencies:
+ parent-module: 1.0.1
+ resolve-from: 4.0.0
+ dev: true
+
+ /indent-string/4.0.0:
+ resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==}
+ engines: {node: '>=8'}
+ dev: true
+
+ /inherits/2.0.4:
+ resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
+ dev: true
+
+ /ini/1.3.8:
+ resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==}
+ dev: true
+
+ /is-arrayish/0.2.1:
+ resolution: {integrity: sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=}
+ dev: true
+
+ /is-core-module/2.6.0:
+ resolution: {integrity: sha512-wShG8vs60jKfPWpF2KZRaAtvt3a20OAn7+IJ6hLPECpSABLcKtFKTTI4ZtH5QcBruBHlq+WsdHWyz0BCZW7svQ==}
+ dependencies:
+ has: 1.0.3
+ dev: true
+
+ /is-directory/0.3.1:
+ resolution: {integrity: sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=}
+ engines: {node: '>=0.10.0'}
+ dev: true
+
+ /is-fullwidth-code-point/3.0.0:
+ resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
+ engines: {node: '>=8'}
+ dev: true
+
+ /is-obj/2.0.0:
+ resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==}
+ engines: {node: '>=8'}
+ dev: true
+
+ /is-plain-obj/1.1.0:
+ resolution: {integrity: sha1-caUMhCnfync8kqOQpKA7OfzVHT4=}
+ engines: {node: '>=0.10.0'}
+ dev: true
+
+ /is-stream/1.1.0:
+ resolution: {integrity: sha1-EtSj3U5o4Lec6428hBc66A2RykQ=}
+ engines: {node: '>=0.10.0'}
+ dev: true
+
+ /is-stream/2.0.1:
+ resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==}
+ engines: {node: '>=8'}
+ dev: true
+
+ /is-text-path/1.0.1:
+ resolution: {integrity: sha1-Thqg+1G/vLPpJogAE5cgLBd1tm4=}
+ engines: {node: '>=0.10.0'}
+ dependencies:
+ text-extensions: 1.9.0
+ dev: true
+
+ /isarray/1.0.0:
+ resolution: {integrity: sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=}
+ dev: true
+
+ /isexe/2.0.0:
+ resolution: {integrity: sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=}
+ dev: true
+
+ /js-tokens/4.0.0:
+ resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
+ dev: true
+
+ /js-yaml/3.14.1:
+ resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==}
+ hasBin: true
+ dependencies:
+ argparse: 1.0.10
+ esprima: 4.0.1
+ dev: true
+
+ /json-parse-better-errors/1.0.2:
+ resolution: {integrity: sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==}
+ dev: true
+
+ /json-parse-even-better-errors/2.3.1:
+ resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==}
+ dev: true
+
+ /json-stringify-safe/5.0.1:
+ resolution: {integrity: sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=}
+ dev: true
+
+ /jsonfile/6.1.0:
+ resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==}
+ dependencies:
+ universalify: 2.0.0
+ optionalDependencies:
+ graceful-fs: 4.2.8
+ dev: true
+
+ /jsonparse/1.3.1:
+ resolution: {integrity: sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=}
+ engines: {'0': node >= 0.2.0}
+ dev: true
+
+ /kind-of/6.0.3:
+ resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==}
+ engines: {node: '>=0.10.0'}
+ dev: true
+
+ /lines-and-columns/1.1.6:
+ resolution: {integrity: sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=}
+ dev: true
+
+ /load-json-file/4.0.0:
+ resolution: {integrity: sha1-L19Fq5HjMhYjT9U62rZo607AmTs=}
+ engines: {node: '>=4'}
+ dependencies:
+ graceful-fs: 4.2.8
+ parse-json: 4.0.0
+ pify: 3.0.0
+ strip-bom: 3.0.0
+ dev: true
+
+ /locate-path/2.0.0:
+ resolution: {integrity: sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=}
+ engines: {node: '>=4'}
+ dependencies:
+ p-locate: 2.0.0
+ path-exists: 3.0.0
+ dev: true
+
+ /locate-path/3.0.0:
+ resolution: {integrity: sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==}
+ engines: {node: '>=6'}
+ dependencies:
+ p-locate: 3.0.0
+ path-exists: 3.0.0
+ dev: true
+
+ /locate-path/5.0.0:
+ resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==}
+ engines: {node: '>=8'}
+ dependencies:
+ p-locate: 4.1.0
+ dev: true
+
+ /locate-path/6.0.0:
+ resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
+ engines: {node: '>=10'}
+ dependencies:
+ p-locate: 5.0.0
+ dev: true
+
+ /lodash.ismatch/4.4.0:
+ resolution: {integrity: sha1-dWy1FQyjum8RCFp4hJZF8Yj4Xzc=}
+ dev: true
+
+ /lodash/4.17.21:
+ resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
+ dev: true
+
+ /lru-cache/6.0.0:
+ resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==}
+ engines: {node: '>=10'}
+ dependencies:
+ yallist: 4.0.0
+ dev: true
+
+ /map-obj/1.0.1:
+ resolution: {integrity: sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=}
+ engines: {node: '>=0.10.0'}
+ dev: true
+
+ /map-obj/4.2.1:
+ resolution: {integrity: sha512-+WA2/1sPmDj1dlvvJmB5G6JKfY9dpn7EVBUL06+y6PoljPkh+6V1QihwxNkbcGxCRjt2b0F9K0taiCuo7MbdFQ==}
+ engines: {node: '>=8'}
+ dev: true
+
+ /meow/7.1.1:
+ resolution: {integrity: sha512-GWHvA5QOcS412WCo8vwKDlTelGLsCGBVevQB5Kva961rmNfun0PCbv5+xta2kUMFJyR8/oWnn7ddeKdosbAPbA==}
+ engines: {node: '>=10'}
+ dependencies:
+ '@types/minimist': 1.2.2
+ camelcase-keys: 6.2.2
+ decamelize-keys: 1.1.0
+ hard-rejection: 2.1.0
+ minimist-options: 4.1.0
+ normalize-package-data: 2.5.0
+ read-pkg-up: 7.0.1
+ redent: 3.0.0
+ trim-newlines: 3.0.1
+ type-fest: 0.13.1
+ yargs-parser: 18.1.3
+ dev: true
+
+ /meow/8.1.2:
+ resolution: {integrity: sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==}
+ engines: {node: '>=10'}
+ dependencies:
+ '@types/minimist': 1.2.2
+ camelcase-keys: 6.2.2
+ decamelize-keys: 1.1.0
+ hard-rejection: 2.1.0
+ minimist-options: 4.1.0
+ normalize-package-data: 3.0.3
+ read-pkg-up: 7.0.1
+ redent: 3.0.0
+ trim-newlines: 3.0.1
+ type-fest: 0.18.1
+ yargs-parser: 20.2.9
+ dev: true
+
+ /merge-stream/2.0.0:
+ resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==}
+ dev: true
+
+ /mimic-fn/2.1.0:
+ resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==}
+ engines: {node: '>=6'}
+ dev: true
+
+ /min-indent/1.0.1:
+ resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==}
+ engines: {node: '>=4'}
+ dev: true
+
+ /minimatch/3.0.4:
+ resolution: {integrity: sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==}
+ dependencies:
+ brace-expansion: 1.1.11
+ dev: true
+
+ /minimist-options/4.1.0:
+ resolution: {integrity: sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==}
+ engines: {node: '>= 6'}
+ dependencies:
+ arrify: 1.0.1
+ is-plain-obj: 1.1.0
+ kind-of: 6.0.3
+ dev: true
+
+ /minimist/1.2.5:
+ resolution: {integrity: sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==}
+ dev: true
+
+ /modify-values/1.0.1:
+ resolution: {integrity: sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==}
+ engines: {node: '>=0.10.0'}
+ dev: true
+
+ /neo-async/2.6.2:
+ resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==}
+ dev: true
+
+ /nice-try/1.0.5:
+ resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==}
+ dev: true
+
+ /normalize-package-data/2.5.0:
+ resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==}
+ dependencies:
+ hosted-git-info: 2.8.9
+ resolve: 1.20.0
+ semver: 5.7.1
+ validate-npm-package-license: 3.0.4
+ dev: true
+
+ /normalize-package-data/3.0.3:
+ resolution: {integrity: sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==}
+ engines: {node: '>=10'}
+ dependencies:
+ hosted-git-info: 4.0.2
+ is-core-module: 2.6.0
+ semver: 7.3.5
+ validate-npm-package-license: 3.0.4
+ dev: true
+
+ /npm-run-path/2.0.2:
+ resolution: {integrity: sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=}
+ engines: {node: '>=4'}
+ dependencies:
+ path-key: 2.0.1
+ dev: true
+
+ /npm-run-path/4.0.1:
+ resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==}
+ engines: {node: '>=8'}
+ dependencies:
+ path-key: 3.1.1
+ dev: true
+
+ /null-check/1.0.0:
+ resolution: {integrity: sha1-l33/1xdgErnsMNKjnbXPcqBDnt0=}
+ engines: {node: '>=0.10.0'}
+ dev: true
+
+ /once/1.4.0:
+ resolution: {integrity: sha1-WDsap3WWHUsROsF9nFC6753Xa9E=}
+ dependencies:
+ wrappy: 1.0.2
+ dev: true
+
+ /onetime/5.1.2:
+ resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==}
+ engines: {node: '>=6'}
+ dependencies:
+ mimic-fn: 2.1.0
+ dev: true
+
+ /opencollective-postinstall/2.0.3:
+ resolution: {integrity: sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==}
+ hasBin: true
+ dev: true
+
+ /p-finally/1.0.0:
+ resolution: {integrity: sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=}
+ engines: {node: '>=4'}
+ dev: true
+
+ /p-limit/1.3.0:
+ resolution: {integrity: sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==}
+ engines: {node: '>=4'}
+ dependencies:
+ p-try: 1.0.0
+ dev: true
+
+ /p-limit/2.3.0:
+ resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==}
+ engines: {node: '>=6'}
+ dependencies:
+ p-try: 2.2.0
+ dev: true
+
+ /p-limit/3.1.0:
+ resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
+ engines: {node: '>=10'}
+ dependencies:
+ yocto-queue: 0.1.0
+ dev: true
+
+ /p-locate/2.0.0:
+ resolution: {integrity: sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=}
+ engines: {node: '>=4'}
+ dependencies:
+ p-limit: 1.3.0
+ dev: true
+
+ /p-locate/3.0.0:
+ resolution: {integrity: sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==}
+ engines: {node: '>=6'}
+ dependencies:
+ p-limit: 2.3.0
+ dev: true
+
+ /p-locate/4.1.0:
+ resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==}
+ engines: {node: '>=8'}
+ dependencies:
+ p-limit: 2.3.0
+ dev: true
+
+ /p-locate/5.0.0:
+ resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
+ engines: {node: '>=10'}
+ dependencies:
+ p-limit: 3.1.0
+ dev: true
+
+ /p-try/1.0.0:
+ resolution: {integrity: sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=}
+ engines: {node: '>=4'}
+ dev: true
+
+ /p-try/2.2.0:
+ resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==}
+ engines: {node: '>=6'}
+ dev: true
+
+ /parent-module/1.0.1:
+ resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
+ engines: {node: '>=6'}
+ dependencies:
+ callsites: 3.1.0
+ dev: true
+
+ /parse-json/4.0.0:
+ resolution: {integrity: sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=}
+ engines: {node: '>=4'}
+ dependencies:
+ error-ex: 1.3.2
+ json-parse-better-errors: 1.0.2
+ dev: true
+
+ /parse-json/5.2.0:
+ resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==}
+ engines: {node: '>=8'}
+ dependencies:
+ '@babel/code-frame': 7.14.5
+ error-ex: 1.3.2
+ json-parse-even-better-errors: 2.3.1
+ lines-and-columns: 1.1.6
+ dev: true
+
+ /path-exists/3.0.0:
+ resolution: {integrity: sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=}
+ engines: {node: '>=4'}
+ dev: true
+
+ /path-exists/4.0.0:
+ resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
+ engines: {node: '>=8'}
+ dev: true
+
+ /path-key/2.0.1:
+ resolution: {integrity: sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=}
+ engines: {node: '>=4'}
+ dev: true
+
+ /path-key/3.1.1:
+ resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
+ engines: {node: '>=8'}
+ dev: true
+
+ /path-parse/1.0.7:
+ resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
+ dev: true
+
+ /path-type/3.0.0:
+ resolution: {integrity: sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==}
+ engines: {node: '>=4'}
+ dependencies:
+ pify: 3.0.0
+ dev: true
+
+ /path-type/4.0.0:
+ resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==}
+ engines: {node: '>=8'}
+ dev: true
+
+ /pify/2.3.0:
+ resolution: {integrity: sha1-7RQaasBDqEnqWISY59yosVMw6Qw=}
+ engines: {node: '>=0.10.0'}
+ dev: true
+
+ /pify/3.0.0:
+ resolution: {integrity: sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=}
+ engines: {node: '>=4'}
+ dev: true
+
+ /pkg-dir/4.2.0:
+ resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==}
+ engines: {node: '>=8'}
+ dependencies:
+ find-up: 4.1.0
+ dev: true
+
+ /please-upgrade-node/3.2.0:
+ resolution: {integrity: sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==}
+ dependencies:
+ semver-compare: 1.0.0
+ dev: true
+
+ /process-nextick-args/2.0.1:
+ resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==}
+ dev: true
+
+ /pump/3.0.0:
+ resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==}
+ dependencies:
+ end-of-stream: 1.4.4
+ once: 1.4.0
+ dev: true
+
+ /q/1.5.1:
+ resolution: {integrity: sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=}
+ engines: {node: '>=0.6.0', teleport: '>=0.2.0'}
+ dev: true
+
+ /quick-lru/4.0.1:
+ resolution: {integrity: sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==}
+ engines: {node: '>=8'}
+ dev: true
+
+ /read-pkg-up/3.0.0:
+ resolution: {integrity: sha1-PtSWaF26D4/hGNBpHcUfSh/5bwc=}
+ engines: {node: '>=4'}
+ dependencies:
+ find-up: 2.1.0
+ read-pkg: 3.0.0
+ dev: true
+
+ /read-pkg-up/7.0.1:
+ resolution: {integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==}
+ engines: {node: '>=8'}
+ dependencies:
+ find-up: 4.1.0
+ read-pkg: 5.2.0
+ type-fest: 0.8.1
+ dev: true
+
+ /read-pkg/3.0.0:
+ resolution: {integrity: sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=}
+ engines: {node: '>=4'}
+ dependencies:
+ load-json-file: 4.0.0
+ normalize-package-data: 2.5.0
+ path-type: 3.0.0
+ dev: true
+
+ /read-pkg/5.2.0:
+ resolution: {integrity: sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==}
+ engines: {node: '>=8'}
+ dependencies:
+ '@types/normalize-package-data': 2.4.1
+ normalize-package-data: 2.5.0
+ parse-json: 5.2.0
+ type-fest: 0.6.0
+ dev: true
+
+ /readable-stream/2.3.7:
+ resolution: {integrity: sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==}
+ dependencies:
+ core-util-is: 1.0.3
+ inherits: 2.0.4
+ isarray: 1.0.0
+ process-nextick-args: 2.0.1
+ safe-buffer: 5.1.2
+ string_decoder: 1.1.1
+ util-deprecate: 1.0.2
+ dev: true
+
+ /readable-stream/3.6.0:
+ resolution: {integrity: sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==}
+ engines: {node: '>= 6'}
+ dependencies:
+ inherits: 2.0.4
+ string_decoder: 1.3.0
+ util-deprecate: 1.0.2
+ dev: true
+
+ /redent/3.0.0:
+ resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==}
+ engines: {node: '>=8'}
+ dependencies:
+ indent-string: 4.0.0
+ strip-indent: 3.0.0
+ dev: true
+
+ /require-directory/2.1.1:
+ resolution: {integrity: sha1-jGStX9MNqxyXbiNE/+f3kqam30I=}
+ engines: {node: '>=0.10.0'}
+ dev: true
+
+ /resolve-from/3.0.0:
+ resolution: {integrity: sha1-six699nWiBvItuZTM17rywoYh0g=}
+ engines: {node: '>=4'}
+ dev: true
+
+ /resolve-from/4.0.0:
+ resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
+ engines: {node: '>=4'}
+ dev: true
+
+ /resolve-from/5.0.0:
+ resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==}
+ engines: {node: '>=8'}
+ dev: true
+
+ /resolve-global/1.0.0:
+ resolution: {integrity: sha512-zFa12V4OLtT5XUX/Q4VLvTfBf+Ok0SPc1FNGM/z9ctUdiU618qwKpWnd0CHs3+RqROfyEg/DhuHbMWYqcgljEw==}
+ engines: {node: '>=8'}
+ dependencies:
+ global-dirs: 0.1.1
+ dev: true
+
+ /resolve/1.20.0:
+ resolution: {integrity: sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==}
+ dependencies:
+ is-core-module: 2.6.0
+ path-parse: 1.0.7
+ dev: true
+
+ /run-node/1.0.0:
+ resolution: {integrity: sha512-kc120TBlQ3mih1LSzdAJXo4xn/GWS2ec0l3S+syHDXP9uRr0JAT8Qd3mdMuyjqCzeZktgP3try92cEgf9Nks8A==}
+ engines: {node: '>=4'}
+ hasBin: true
+ dev: true
+
+ /safe-buffer/5.1.2:
+ resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==}
+ dev: true
+
+ /safe-buffer/5.2.1:
+ resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
+ dev: true
+
+ /semver-compare/1.0.0:
+ resolution: {integrity: sha1-De4hahyUGrN+nvsXiPavxf9VN/w=}
+ dev: true
+
+ /semver/5.7.1:
+ resolution: {integrity: sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==}
+ hasBin: true
+ dev: true
+
+ /semver/6.3.0:
+ resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==}
+ hasBin: true
+ dev: true
+
+ /semver/7.3.5:
+ resolution: {integrity: sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==}
+ engines: {node: '>=10'}
+ hasBin: true
+ dependencies:
+ lru-cache: 6.0.0
+ dev: true
+
+ /shebang-command/1.2.0:
+ resolution: {integrity: sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=}
+ engines: {node: '>=0.10.0'}
+ dependencies:
+ shebang-regex: 1.0.0
+ dev: true
+
+ /shebang-command/2.0.0:
+ resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
+ engines: {node: '>=8'}
+ dependencies:
+ shebang-regex: 3.0.0
+ dev: true
+
+ /shebang-regex/1.0.0:
+ resolution: {integrity: sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=}
+ engines: {node: '>=0.10.0'}
+ dev: true
+
+ /shebang-regex/3.0.0:
+ resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
+ engines: {node: '>=8'}
+ dev: true
+
+ /signal-exit/3.0.3:
+ resolution: {integrity: sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==}
+ dev: true
+
+ /slash/3.0.0:
+ resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
+ engines: {node: '>=8'}
+ dev: true
+
+ /source-map/0.6.1:
+ resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
+ engines: {node: '>=0.10.0'}
+ dev: true
+
+ /spdx-correct/3.1.1:
+ resolution: {integrity: sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==}
+ dependencies:
+ spdx-expression-parse: 3.0.1
+ spdx-license-ids: 3.0.10
+ dev: true
+
+ /spdx-exceptions/2.3.0:
+ resolution: {integrity: sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==}
+ dev: true
+
+ /spdx-expression-parse/3.0.1:
+ resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==}
+ dependencies:
+ spdx-exceptions: 2.3.0
+ spdx-license-ids: 3.0.10
+ dev: true
+
+ /spdx-license-ids/3.0.10:
+ resolution: {integrity: sha512-oie3/+gKf7QtpitB0LYLETe+k8SifzsX4KixvpOsbI6S0kRiRQ5MKOio8eMSAKQ17N06+wdEOXRiId+zOxo0hA==}
+ dev: true
+
+ /split/1.0.1:
+ resolution: {integrity: sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==}
+ dependencies:
+ through: 2.3.8
+ dev: true
+
+ /split2/3.2.2:
+ resolution: {integrity: sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==}
+ dependencies:
+ readable-stream: 3.6.0
+ dev: true
+
+ /sprintf-js/1.0.3:
+ resolution: {integrity: sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=}
+ dev: true
+
+ /standard-version/9.3.1:
+ resolution: {integrity: sha512-5qMxXw/FxLouC5nANyx/5RY1kiorJx9BppUso8gN07MG64q2uLRmrPb4KfXp3Ql4s/gxjZwZ89e0FwxeLubGww==}
+ engines: {node: '>=10'}
+ hasBin: true
+ dependencies:
+ chalk: 2.4.2
+ conventional-changelog: 3.1.24
+ conventional-changelog-config-spec: 2.1.0
+ conventional-changelog-conventionalcommits: 4.5.0
+ conventional-recommended-bump: 6.1.0
+ detect-indent: 6.1.0
+ detect-newline: 3.1.0
+ dotgitignore: 2.1.0
+ figures: 3.2.0
+ find-up: 5.0.0
+ fs-access: 1.0.1
+ git-semver-tags: 4.1.1
+ semver: 7.3.5
+ stringify-package: 1.0.1
+ yargs: 16.2.0
+ dev: true
+
+ /string-width/4.2.2:
+ resolution: {integrity: sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==}
+ engines: {node: '>=8'}
+ dependencies:
+ emoji-regex: 8.0.0
+ is-fullwidth-code-point: 3.0.0
+ strip-ansi: 6.0.0
+ dev: true
+
+ /string_decoder/1.1.1:
+ resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==}
+ dependencies:
+ safe-buffer: 5.1.2
+ dev: true
+
+ /string_decoder/1.3.0:
+ resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==}
+ dependencies:
+ safe-buffer: 5.2.1
+ dev: true
+
+ /stringify-package/1.0.1:
+ resolution: {integrity: sha512-sa4DUQsYciMP1xhKWGuFM04fB0LG/9DlluZoSVywUMRNvzid6XucHK0/90xGxRoHrAaROrcHK1aPKaijCtSrhg==}
+ dev: true
+
+ /strip-ansi/6.0.0:
+ resolution: {integrity: sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==}
+ engines: {node: '>=8'}
+ dependencies:
+ ansi-regex: 5.0.0
+ dev: true
+
+ /strip-bom/3.0.0:
+ resolution: {integrity: sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=}
+ engines: {node: '>=4'}
+ dev: true
+
+ /strip-eof/1.0.0:
+ resolution: {integrity: sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=}
+ engines: {node: '>=0.10.0'}
+ dev: true
+
+ /strip-final-newline/2.0.0:
+ resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==}
+ engines: {node: '>=6'}
+ dev: true
+
+ /strip-indent/3.0.0:
+ resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==}
+ engines: {node: '>=8'}
+ dependencies:
+ min-indent: 1.0.1
+ dev: true
+
+ /supports-color/5.5.0:
+ resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==}
+ engines: {node: '>=4'}
+ dependencies:
+ has-flag: 3.0.0
+ dev: true
+
+ /supports-color/7.2.0:
+ resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
+ engines: {node: '>=8'}
+ dependencies:
+ has-flag: 4.0.0
+ dev: true
+
+ /text-extensions/1.9.0:
+ resolution: {integrity: sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ==}
+ engines: {node: '>=0.10'}
+ dev: true
+
+ /through/2.3.8:
+ resolution: {integrity: sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=}
+ dev: true
+
+ /through2/2.0.5:
+ resolution: {integrity: sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==}
+ dependencies:
+ readable-stream: 2.3.7
+ xtend: 4.0.2
+ dev: true
+
+ /through2/4.0.2:
+ resolution: {integrity: sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==}
+ dependencies:
+ readable-stream: 3.6.0
+ dev: true
+
+ /trim-newlines/3.0.1:
+ resolution: {integrity: sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==}
+ engines: {node: '>=8'}
+ dev: true
+
+ /trim-off-newlines/1.0.1:
+ resolution: {integrity: sha1-n5up2e+odkw4dpi8v+sshI8RrbM=}
+ engines: {node: '>=0.10.0'}
+ dev: true
+
+ /type-fest/0.13.1:
+ resolution: {integrity: sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==}
+ engines: {node: '>=10'}
+ dev: true
+
+ /type-fest/0.18.1:
+ resolution: {integrity: sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==}
+ engines: {node: '>=10'}
+ dev: true
+
+ /type-fest/0.6.0:
+ resolution: {integrity: sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==}
+ engines: {node: '>=8'}
+ dev: true
+
+ /type-fest/0.8.1:
+ resolution: {integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==}
+ engines: {node: '>=8'}
+ dev: true
+
+ /typedarray/0.0.6:
+ resolution: {integrity: sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=}
+ dev: true
+
+ /uglify-js/3.14.1:
+ resolution: {integrity: sha512-JhS3hmcVaXlp/xSo3PKY5R0JqKs5M3IV+exdLHW99qKvKivPO4Z8qbej6mte17SOPqAOVMjt/XGgWacnFSzM3g==}
+ engines: {node: '>=0.8.0'}
+ hasBin: true
+ dev: true
+ optional: true
+
+ /universalify/2.0.0:
+ resolution: {integrity: sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==}
+ engines: {node: '>= 10.0.0'}
+ dev: true
+
+ /util-deprecate/1.0.2:
+ resolution: {integrity: sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=}
+ dev: true
+
+ /validate-npm-package-license/3.0.4:
+ resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==}
+ dependencies:
+ spdx-correct: 3.1.1
+ spdx-expression-parse: 3.0.1
+ dev: true
+
+ /which/1.3.1:
+ resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==}
+ hasBin: true
+ dependencies:
+ isexe: 2.0.0
+ dev: true
+
+ /which/2.0.2:
+ resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
+ engines: {node: '>= 8'}
+ hasBin: true
+ dependencies:
+ isexe: 2.0.0
+ dev: true
+
+ /wordwrap/1.0.0:
+ resolution: {integrity: sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=}
+ dev: true
+
+ /wrap-ansi/7.0.0:
+ resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
+ engines: {node: '>=10'}
+ dependencies:
+ ansi-styles: 4.3.0
+ string-width: 4.2.2
+ strip-ansi: 6.0.0
+ dev: true
+
+ /wrappy/1.0.2:
+ resolution: {integrity: sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=}
+ dev: true
+
+ /xtend/4.0.2:
+ resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==}
+ engines: {node: '>=0.4'}
+ dev: true
+
+ /y18n/5.0.8:
+ resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
+ engines: {node: '>=10'}
+ dev: true
+
+ /yallist/4.0.0:
+ resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
+ dev: true
+
+ /yaml/1.10.2:
+ resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==}
+ engines: {node: '>= 6'}
+ dev: true
+
+ /yargs-parser/18.1.3:
+ resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==}
+ engines: {node: '>=6'}
+ dependencies:
+ camelcase: 5.3.1
+ decamelize: 1.2.0
+ dev: true
+
+ /yargs-parser/20.2.9:
+ resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==}
+ engines: {node: '>=10'}
+ dev: true
+
+ /yargs/16.2.0:
+ resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==}
+ engines: {node: '>=10'}
+ dependencies:
+ cliui: 7.0.4
+ escalade: 3.1.1
+ get-caller-file: 2.0.5
+ require-directory: 2.1.1
+ string-width: 4.2.2
+ y18n: 5.0.8
+ yargs-parser: 20.2.9
+ dev: true
+
+ /yargs/17.1.1:
+ resolution: {integrity: sha512-c2k48R0PwKIqKhPMWjeiF6y2xY/gPMUlro0sgxqXpbOIohWiLNXWslsootttv7E1e73QPAMQSg5FeySbVcpsPQ==}
+ engines: {node: '>=12'}
+ dependencies:
+ cliui: 7.0.4
+ escalade: 3.1.1
+ get-caller-file: 2.0.5
+ require-directory: 2.1.1
+ string-width: 4.2.2
+ y18n: 5.0.8
+ yargs-parser: 20.2.9
+ dev: true
+
+ /yocto-queue/0.1.0:
+ resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
+ engines: {node: '>=10'}
+ dev: true
diff --git a/standard-version-updater/AssemblyInfo.js b/standard-version-updater/AssemblyInfo.js
new file mode 100644
index 0000000..5a67896
--- /dev/null
+++ b/standard-version-updater/AssemblyInfo.js
@@ -0,0 +1,9 @@
+module.exports.readVersion = function (contents) {
+ var regex = /\[assembly: AssemblyVersion\(\"(.*)\"\)\]/g;
+ return contents.match(regex)[0].replace(regex, "$1")
+};
+
+module.exports.writeVersion = function (contents, version) {
+ var regex = /\[assembly: AssemblyVersion\(\"(.*)\"\)\]/g;
+ return contents.replace(regex, `[assembly: AssemblyVersion("${version}")]`)
+};
\ No newline at end of file