diff --git a/ci/azure-pipelines-docfx.yml b/ci/azure-pipelines-docfx.yml index db588fe8c..4a40053a7 100644 --- a/ci/azure-pipelines-docfx.yml +++ b/ci/azure-pipelines-docfx.yml @@ -7,14 +7,6 @@ trigger: - master pr: none -resources: - repositories: - - repository: RGBNET - type: github - endpoint: github.com_SpoinkyNL - name: DarthAffe/RGB.NET - ref: Development - pool: vmImage: 'windows-latest' @@ -28,22 +20,13 @@ variables: SourceVersion: $(Build.SourceVersion) steps: -- checkout: RGBNET - path: s/RGB.NET - checkout: self path: s/Artemis - task: DotNetCoreCLI@2 - displayName: 'RGB.NET - Build' + displayName: 'dotnet build Artemis' inputs: command: 'build' - projects: '$(rgbSolution)' - arguments: '--configuration Release' - -- task: DotNetCoreCLI@2 - displayName: 'dotnet restore Artemis' - inputs: - command: 'restore' projects: '$(artemisSolution)' feedsToUse: 'config' nugetConfigPath: '$(Pipeline.Workspace)/s/Artemis/src/NuGet.Config' diff --git a/docfx/docfx_project/api/index.md b/docfx/docfx_project/api/index.md index 78dc9c005..0d7007d2d 100644 --- a/docfx/docfx_project/api/index.md +++ b/docfx/docfx_project/api/index.md @@ -1,2 +1,26 @@ -# PLACEHOLDER -TODO: Add .NET projects to the *src* folder and run `docfx` to generate **REAL** *API Documentation*! + +# Welcome to the **Artemis API documentation** +On this website you can browse the Artemis Core and Shared UI API. +A large part of this documentation is being generated based on code but over time the plan is to expand the documentation with written guides. + + +## Plugins +Artemis 2.0 has been developed from the ground up with plugins in mind. This means almost all functionality can be expanded. The following plugin types are currently available and fully implemented: + - [DeviceProvider](api/Artemis.Core.DeviceProviders.DeviceProvider.html) + - [LayerBrush\](api/Artemis.Core.LayerBrushes.LayerBrush-1.html) + - [PerLedLayerBrush\](api/Artemis.Core.LayerBrushes.PerLedLayerBrush-1.html) + - [LayerEffect](api/Artemis.Core.LayerEffects.LayerEffect-1.html) + - [Module](api/Artemis.Core.Modules.Module.html) + - [Module\](api/Artemis.Core.Modules.Module-1.html) + +These allow you to expand on Artemis's functionality. For quick and interactive plugin creation, use the [Visual Studio template extension](https://marketplace.visualstudio.com/items?itemName=SpoinkyNL.ArtemisTemplates). + +Example implementations of these plugins can be found on [GitHub](https://github.com/Artemis-RGB/Artemis/tree/master/src/Plugins). + +## Services +Artemis provides plugins with an API through a range of services. +All the services are available to plugins by using dependency injection in your plugin's constructor. Dependency injection is also available for the different view models plugins may provide. + +- [Core Services](api/Artemis.Core.Services.html#interfaces) +- [UI Services](api/Artemis.UI.Shared.Services.html#interfaces) + diff --git a/docfx/docfx_project/articles/intro.md b/docfx/docfx_project/articles/intro.md deleted file mode 100644 index b0db1682f..000000000 --- a/docfx/docfx_project/articles/intro.md +++ /dev/null @@ -1 +0,0 @@ -Bummer, no guides 😌 \ No newline at end of file diff --git a/docfx/docfx_project/articles/plugins_getting_started.md b/docfx/docfx_project/articles/plugins_getting_started.md deleted file mode 100644 index 484967e77..000000000 --- a/docfx/docfx_project/articles/plugins_getting_started.md +++ /dev/null @@ -1,3 +0,0 @@ -Plugins allow you to expand on Artemis's functionality. For quick and interactive plugin creation, use the [Visual Studio template extension](https://marketplace.visualstudio.com/items?itemName=SpoinkyNL.ArtemisTemplates). - -Example implementations of these plugins can be found on [GitHub](https://github.com/Artemis-RGB/Artemis/tree/master/src/Plugins). \ No newline at end of file diff --git a/docfx/docfx_project/articles/toc.yml b/docfx/docfx_project/articles/toc.yml deleted file mode 100644 index 2b505c1d9..000000000 --- a/docfx/docfx_project/articles/toc.yml +++ /dev/null @@ -1,4 +0,0 @@ -- name: Introduction - href: intro.md -- name: Plugins - Getting started - href: plugins_getting_started.md \ No newline at end of file diff --git a/docfx/docfx_project/docfx.json b/docfx/docfx_project/docfx.json index 30f8b4c7a..dc1d65129 100644 --- a/docfx/docfx_project/docfx.json +++ b/docfx/docfx_project/docfx.json @@ -4,8 +4,8 @@ "src": [ { "files": [ - "Artemis.Core/Artemis.Core.csproj", - "Artemis.UI.Shared/Artemis.UI.Shared.csproj", + "Artemis.Core/bin/net6.0/Artemis.Core.dll", + "Artemis.UI.Shared/bin/net6.0/Artemis.UI.Shared.dll", ], "src": "../../src" } @@ -20,14 +20,11 @@ "content": [ { "files": [ - "api/**.yml", - "api/index.md" + "api/**.yml" ] }, { "files": [ - "articles/**.md", - "articles/**/toc.yml", "toc.yml", "*.md" ] @@ -52,16 +49,24 @@ } ], "globalMetadata": { - "_appTitle": "Artemis documentation", - "_enableSearch": true + "_appTitle": "Artemis API Documentation", + "_appName": "Artemis", + "_appFaviconPath": "images/application.ico", + "_appLogoPath": "images/application.ico", + "_appFooter": "\r \r\r\r \r\r\r \r\r\r \r\r\r \r", + "_copyrightFooter": "Content is available under the PolyForm Noncommercial License, by Artemis RGB.", + "_enableSearch": true, + "_disableSideFilter": false, + "_enableNewTab": true, + "_disableContribution": false, + "_disableBreadcrumb": false }, "dest": "_site", "globalMetadataFiles": [], "fileMetadataFiles": [], "template": [ "default", - "templates/artemis", - "templates/material" + "templates/singulinkfx" ], "postProcessors": [], "markdownEngineName": "markdig", diff --git a/docfx/docfx_project/filterConfig.yml b/docfx/docfx_project/filterConfig.yml index fff7b04d2..d56cc1bd4 100644 --- a/docfx/docfx_project/filterConfig.yml +++ b/docfx/docfx_project/filterConfig.yml @@ -11,6 +11,9 @@ apiRules: - exclude: uidRegex: ^Stylet type: Type +- exclude: + uidRegex: ^Artemis\.Core\.CorePropertyChanged + type: Type - exclude: uidRegex: ^Artemis\.Core\.Properties type: Type diff --git a/docfx/docfx_project/images/application.ico b/docfx/docfx_project/images/application.ico new file mode 100644 index 000000000..bdb89dbd0 Binary files /dev/null and b/docfx/docfx_project/images/application.ico differ diff --git a/docfx/docfx_project/images/logo-512.png b/docfx/docfx_project/images/logo-512.png deleted file mode 100644 index d1cd8ebe7..000000000 Binary files a/docfx/docfx_project/images/logo-512.png and /dev/null differ diff --git a/docfx/docfx_project/templates/artemis/favicon.ico b/docfx/docfx_project/templates/artemis/favicon.ico deleted file mode 100644 index 015d1ffa8..000000000 Binary files a/docfx/docfx_project/templates/artemis/favicon.ico and /dev/null differ diff --git a/docfx/docfx_project/templates/artemis/partials/logo.tmpl.partial b/docfx/docfx_project/templates/artemis/partials/logo.tmpl.partial deleted file mode 100644 index f4da7f2c7..000000000 --- a/docfx/docfx_project/templates/artemis/partials/logo.tmpl.partial +++ /dev/null @@ -1,6 +0,0 @@ -{{!Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}} - - - - Artemis logo - diff --git a/docfx/docfx_project/templates/artemis/styles/artemis.css b/docfx/docfx_project/templates/artemis/styles/artemis.css deleted file mode 100644 index 2024ec68d..000000000 --- a/docfx/docfx_project/templates/artemis/styles/artemis.css +++ /dev/null @@ -1,22 +0,0 @@ -.sidefilter { - width: 320px; -} - -.sidetoc { - width: 320px; -} - -@media only screen and (max-width: 768px) -.article.grid-right { - margin-left: 0; -} - -.article.grid-right { - margin-left: 340px; -} - -@media (min-width: 1500px) { -.container { - width: 1470px; -} -} \ No newline at end of file diff --git a/docfx/docfx_project/templates/material/partials/head.tmpl.partial b/docfx/docfx_project/templates/material/partials/head.tmpl.partial deleted file mode 100644 index 9f55e9338..000000000 --- a/docfx/docfx_project/templates/material/partials/head.tmpl.partial +++ /dev/null @@ -1,38 +0,0 @@ -{{!Copyright (c) Oscar Vasquez. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}} - - - - - {{#title}}{{title}}{{/title}}{{^title}}{{>partials/title}}{{/title}} {{#_appTitle}}| {{_appTitle}} {{/_appTitle}} - - - - {{#_description}}{{/_description}} - - - - - - - - {{#_noindex}}{{/_noindex}} - {{#_enableSearch}}{{/_enableSearch}} - {{#_enableNewTab}}{{/_enableNewTab}} - - - - - \ No newline at end of file diff --git a/docfx/docfx_project/templates/material/styles/main.css b/docfx/docfx_project/templates/material/styles/main.css deleted file mode 100644 index 8fb996768..000000000 --- a/docfx/docfx_project/templates/material/styles/main.css +++ /dev/null @@ -1,314 +0,0 @@ -/* COLOR VARIABLES*/ -:root { - --header-bg-color: #009688; - --header-ft-color: #fff; - --highlight-light: #1de9b6; - --highlight-dark: #00bfa5; - --accent-dim: #e0e0e0; - --accent-super-dim: #f3f3f3; - --font-color: #34393e; - --card-box-shadow: 0 1px 2px 0 rgba(61, 65, 68, 0.06), 0 1px 3px 1px rgba(61, 65, 68, 0.16); - --search-box-shadow: 0 1px 2px 0 rgba(41, 45, 48, 0.36), 0 1px 3px 1px rgba(41, 45, 48, 0.46); - --transition: 350ms; -} - -body { - color: var(--font-color); - font-family: "Roboto", sans-serif; - line-height: 1.5; - font-size: 16px; - -ms-text-size-adjust: 100%; - -webkit-text-size-adjust: 100%; - word-wrap: break-word; -} - -/* HIGHLIGHT COLOR */ - -button, -a { - color: var(--highlight-dark); - cursor: pointer; -} - -button:hover, -button:focus, -a:hover, -a:focus { - color: var(--highlight-light); - text-decoration: none; -} - -.toc .nav > li.active > a { - color: var(--highlight-dark); -} - -.toc .nav > li.active > a:hover, -.toc .nav > li.active > a:focus { - color: var(--highlight-light); -} - -.pagination > .active > a { - background-color: var(--header-bg-color); - border-color: var(--header-bg-color); -} - -.pagination > .active > a, -.pagination > .active > a:focus, -.pagination > .active > a:hover, -.pagination > .active > span, -.pagination > .active > span:focus, -.pagination > .active > span:hover { - background-color: var(--highlight-light); - border-color: var(--highlight-light); -} - -.affix ul > li.active > a, .affix ul > li.active > a:before { - color: var(--highlight-dark); -} - -.affix > ul > li.active > a, .affix > ul > li.active > a:before { - color: var(--highlight-dark); -} - -/* HEADINGS */ - -h1 { - font-weight: 600; - font-size: 32px; -} - -h2 { - font-weight: 600; - font-size: 24px; - line-height: 1.8; -} - -h3 { - font-weight: 600; - font-size: 20px; - line-height: 1.8; -} - -h5 { - font-size: 14px; - padding: 10px 0px; -} - -article h1, -article h2, -article h3, -article h4 { - margin-top: 35px; - margin-bottom: 15px; -} - -article h4 { - padding-bottom: 8px; - border-bottom: 2px solid #ddd; -} - -/* NAVBAR */ - -.navbar-brand > img { - color: var(--header-ft-color); -} - -.navbar { - border: none; - /* Both navbars use box-shadow */ - -webkit-box-shadow: var(--card-box-shadow); - -moz-box-shadow: var(--card-box-shadow); - box-shadow: var(--card-box-shadow); -} - -.subnav { - border-top: 1px solid #ddd; - background-color: #fff; -} - -.navbar-inverse { - background-color: var(--header-bg-color); - z-index: 100; -} - -.navbar-inverse .navbar-nav > li > a, -.navbar-inverse .navbar-text { - color: var(--header-ft-color); - background-color: var(--header-bg-color); - border-bottom: 3px solid transparent; - padding-bottom: 12px; - transition: 350ms; -} - -.navbar-inverse .navbar-nav > li > a:focus, -.navbar-inverse .navbar-nav > li > a:hover { - color: var(--header-ft-color); - background-color: var(--header-bg-color); - border-bottom: 3px solid white; -} - -.navbar-inverse .navbar-nav > .active > a, -.navbar-inverse .navbar-nav > .active > a:focus, -.navbar-inverse .navbar-nav > .active > a:hover { - color: var(--header-ft-color); - background-color: var(--header-bg-color); - border-bottom: 3px solid white; -} - -.navbar-form .form-control { - border: 0; - border-radius: 4px; - box-shadow: var(--search-box-shadow); - transition:var(--transition); -} - -.navbar-form .form-control:hover { - background-color: var(--accent-dim); -} - -/* NAVBAR TOGGLED (small screens) */ - -.navbar-inverse .navbar-collapse, .navbar-inverse .navbar-form { - border: none; -} -.navbar-inverse .navbar-toggle { - box-shadow: var(--card-box-shadow); - border: none; -} - -.navbar-inverse .navbar-toggle:focus, -.navbar-inverse .navbar-toggle:hover { - background-color: var(--highlight-dark); -} - -/* SIDEBAR */ - -.toc .level1 > li { - font-weight: 400; -} - -.toc .nav > li > a { - color: var(--font-color); -} - -.sidefilter { - background-color: #fff; - border-left: none; - border-right: none; -} - -.sidefilter { - background-color: #fff; - border-left: none; - border-right: none; -} - -.toc-filter { - padding: 5px; - margin: 0; - box-shadow: var(--card-box-shadow); - transition:var(--transition); -} - -.toc-filter:hover { - background-color: var(--accent-super-dim); -} - -.toc-filter > input { - border: none; - background-color: inherit; - transition: inherit; -} - -.toc-filter > .filter-icon { - display: none; -} - -.sidetoc > .toc { - background-color: #fff; - overflow-x: hidden; -} - -.sidetoc { - background-color: #fff; - border: none; -} - -/* ALERTS */ - -.alert { - padding: 0px 0px 5px 0px; - color: inherit; - background-color: inherit; - border: none; - box-shadow: var(--card-box-shadow); -} - -.alert > p { - margin-bottom: 0; - padding: 5px 10px; -} - -.alert > ul { - margin-bottom: 0; - padding: 5px 40px; -} - -.alert > h5 { - padding: 10px 15px; - margin-top: 0; - text-transform: uppercase; - font-weight: bold; - border-radius: 4px 4px 0 0; -} - -.alert-info > h5 { - color: #1976d2; - border-bottom: 4px solid #1976d2; - background-color: #e3f2fd; -} - -.alert-warning > h5 { - color: #f57f17; - border-bottom: 4px solid #f57f17; - background-color: #fff3e0; -} - -.alert-danger > h5 { - color: #d32f2f; - border-bottom: 4px solid #d32f2f; - background-color: #ffebee; -} - -/* CODE HIGHLIGHT */ -pre { - padding: 9.5px; - margin: 0 0 10px; - font-size: 13px; - word-break: break-all; - word-wrap: break-word; - background-color: #fffaef; - border-radius: 4px; - border: none; - box-shadow: var(--card-box-shadow); -} - -/* STYLE FOR IMAGES */ - -.article .small-image { - margin-top: 15px; - box-shadow: var(--card-box-shadow); - max-width: 350px; -} - -.article .medium-image { - margin-top: 15px; - box-shadow: var(--card-box-shadow); - max-width: 550px; -} - -.article .large-image { - margin-top: 15px; - box-shadow: var(--card-box-shadow); - max-width: 700px; -} \ No newline at end of file diff --git a/docfx/docfx_project/templates/singulinkfx/layout/_master.tmpl b/docfx/docfx_project/templates/singulinkfx/layout/_master.tmpl new file mode 100644 index 000000000..a309bf597 --- /dev/null +++ b/docfx/docfx_project/templates/singulinkfx/layout/_master.tmpl @@ -0,0 +1,62 @@ +{{!Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}} +{{!include(/^styles/.*/)}} +{{!include(/^fonts/.*/)}} +{{!include(favicon.ico)}} +{{!include(logo.svg)}} +{{!include(search-stopwords.json)}} + + + + {{>partials/head}} + + +
+ + + + + {{>partials/logo}} +
+ +
+
+ +
+
+ {{>partials/navbar}} +
+ {{^_disableToc}} + {{>partials/toc}} + {{/_disableToc}} +
+ {{>partials/footer}} +
+ +
+ {{#_enableSearch}} + {{>partials/searchResults}} + {{/_enableSearch}} + +
+ {{^_disableBreadcrumb}} + {{>partials/breadcrumb}} + {{/_disableBreadcrumb}} + +
+ {{!body}} +
+
+ + {{#_copyrightFooter}} +
+ {{_copyrightFooter}} +
+ {{/_copyrightFooter}} +
+
+ + {{>partials/scripts}} + + diff --git a/docfx/docfx_project/templates/singulinkfx/partials/footer.tmpl.partial b/docfx/docfx_project/templates/singulinkfx/partials/footer.tmpl.partial new file mode 100644 index 000000000..dd601a975 --- /dev/null +++ b/docfx/docfx_project/templates/singulinkfx/partials/footer.tmpl.partial @@ -0,0 +1,4 @@ +
+ {{{_appFooter}}} + {{^_appFooter}}DocFX + Singulink = ♥{{/_appFooter}} +
\ No newline at end of file diff --git a/docfx/docfx_project/templates/singulinkfx/partials/head.tmpl.partial b/docfx/docfx_project/templates/singulinkfx/partials/head.tmpl.partial new file mode 100644 index 000000000..01f8b6393 --- /dev/null +++ b/docfx/docfx_project/templates/singulinkfx/partials/head.tmpl.partial @@ -0,0 +1,24 @@ +{{!Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}} + + + + + {{#title}}{{title}}{{/title}}{{^title}}{{>partials/title}}{{/title}} {{#_appTitle}}| {{_appTitle}} {{/_appTitle}} + + + + {{#_description}}{{/_description}} + + + + + + + + + + + {{#_noindex}}{{/_noindex}} + {{#_enableSearch}}{{/_enableSearch}} + {{#_enableNewTab}}{{/_enableNewTab}} + \ No newline at end of file diff --git a/docfx/docfx_project/templates/singulinkfx/partials/li.tmpl.partial b/docfx/docfx_project/templates/singulinkfx/partials/li.tmpl.partial new file mode 100644 index 000000000..2c8a3d0e7 --- /dev/null +++ b/docfx/docfx_project/templates/singulinkfx/partials/li.tmpl.partial @@ -0,0 +1,31 @@ +{{!Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}} + +
    + {{#items}} + {{^dropdown}} +
  • + {{^leaf}} + + {{/leaf}} + {{#topicHref}} + {{name}} + {{/topicHref}} + {{^topicHref}} + {{{name}}} + {{/topicHref}} + + {{^leaf}} + {{>partials/li}} + {{/leaf}} +
  • + {{/dropdown}} + {{#dropdown}} +
  • + {{name}} +
      + {{>partials/dd-li}} +
    +
  • + {{/dropdown}} + {{/items}} +
diff --git a/docfx/docfx_project/templates/singulinkfx/partials/logo.tmpl.partial b/docfx/docfx_project/templates/singulinkfx/partials/logo.tmpl.partial new file mode 100644 index 000000000..738ab5b6f --- /dev/null +++ b/docfx/docfx_project/templates/singulinkfx/partials/logo.tmpl.partial @@ -0,0 +1,6 @@ +{{!Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}} + + + {{_appName}} + {{_appName}} + \ No newline at end of file diff --git a/docfx/docfx_project/templates/singulinkfx/partials/namespace.tmpl.partial b/docfx/docfx_project/templates/singulinkfx/partials/namespace.tmpl.partial new file mode 100644 index 000000000..42d64e69b --- /dev/null +++ b/docfx/docfx_project/templates/singulinkfx/partials/namespace.tmpl.partial @@ -0,0 +1,13 @@ +{{!Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}} + +

{{>partials/title}}

+
{{{summary}}}
+
{{{conceptual}}}
+
{{{remarks}}}
+{{#children}} +

{{>partials/namespaceSubtitle}}

+ {{#children}} +
+
{{{summary}}}
+ {{/children}} +{{/children}} diff --git a/docfx/docfx_project/templates/singulinkfx/partials/navbar.tmpl.partial b/docfx/docfx_project/templates/singulinkfx/partials/navbar.tmpl.partial new file mode 100644 index 000000000..cfddfd830 --- /dev/null +++ b/docfx/docfx_project/templates/singulinkfx/partials/navbar.tmpl.partial @@ -0,0 +1,19 @@ +{{!Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}} + +
+
+ {{>partials/logo}} +
+ + {{#_enableSearch}} +
+
+ + +
+
+ {{/_enableSearch}} + +
+
+
\ No newline at end of file diff --git a/docfx/docfx_project/templates/singulinkfx/partials/scripts.tmpl.partial b/docfx/docfx_project/templates/singulinkfx/partials/scripts.tmpl.partial new file mode 100644 index 000000000..90fb7d576 --- /dev/null +++ b/docfx/docfx_project/templates/singulinkfx/partials/scripts.tmpl.partial @@ -0,0 +1,12 @@ +{{!Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}} + + + + + + + + + + + \ No newline at end of file diff --git a/docfx/docfx_project/templates/singulinkfx/partials/searchResults.tmpl.partial b/docfx/docfx_project/templates/singulinkfx/partials/searchResults.tmpl.partial new file mode 100644 index 000000000..9f08c90e3 --- /dev/null +++ b/docfx/docfx_project/templates/singulinkfx/partials/searchResults.tmpl.partial @@ -0,0 +1,9 @@ +{{!Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}} + +
+

{{__global.searchResults}}

+
+

+
+
    +
    \ No newline at end of file diff --git a/docfx/docfx_project/templates/singulinkfx/partials/toc.tmpl.partial b/docfx/docfx_project/templates/singulinkfx/partials/toc.tmpl.partial new file mode 100644 index 000000000..c660966b6 --- /dev/null +++ b/docfx/docfx_project/templates/singulinkfx/partials/toc.tmpl.partial @@ -0,0 +1,5 @@ +{{!Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}} + +
    +
    +
    diff --git a/docfx/docfx_project/templates/singulinkfx/styles/config.css b/docfx/docfx_project/templates/singulinkfx/styles/config.css new file mode 100644 index 000000000..9f3ca697b --- /dev/null +++ b/docfx/docfx_project/templates/singulinkfx/styles/config.css @@ -0,0 +1,114 @@ +/* Theme Configuration Options */ + +:root +{ + /* General */ + + --base-font-size: 16px; + --smalldevice-base-font-size: 14px; /* Base font size for devices < 1024px */ + + --main-bg-color: #1f1f23; + --footer-bg-color: rgba(0,0,0,.4); + --separator-color: #42474f; + + --table-strip-bg-color: #151515; + --table-header-bg-color: black; + --table-header-color: hsla(0,0%,100%,.8); + --table-header-border-color: #040405; + + /* Text */ + + --appname-color: white; + + --h1-color: white; + --h2-color: #f2f2f2; + --h3-color: #e3e3e3; + --h4-color: #ffffff; + --h5-color: #e0e0e0; + + --text-color: #e1e1e1; + --link-color: #00b0f4; + --link-hover-color: #2ec4ff; + + /* Mobile Topbar */ + + --topbar-bg-color: #18191c; + + /* Button */ + + --button-color: #747f8d; + + /* Sidebar */ + + --sidebar-width: 400px; + --sidebar-bg-color: #292B30; + + --search-color: #bdbdbd; + --search-bg-color: #1b1e21; + --search-searchicon-color: #e3e3e3; + --search-border-color: black; + + --sidebar-item-color: white; + --sidebar-active-item-color: #00b0f4; + --sidebar-level1-item-bg-color: #222429; + --sidebar-level1-item-hover-bg-color: #1D1F22; + + --toc-filter-color: #bdbdbd; + --toc-filter-bg-color: #1b1e21; + --toc-filter-filtericon-color: #e3e3e3; + --toc-filter-clearicon-color: #e68585; + --toc-filter-border-color: black; + + /* Scrollbars */ + + --scrollbar-bg-color: transparent; + --scrollbar-thumb-bg-color: rgba(0,0,0,.4); + --scrollbar-thumb-border-color: transparent; + + /* Alerts and Blocks */ + + --alert-info-border-color: rgba(114,137,218,.5); + --alert-info-bg-color: rgba(114,137,218,.1); + + --alert-warning-border-color: rgba(250,166,26,.5); + --alert-warning-bg-color: rgba(250,166,26,.1); + + --alert-danger-border-color: rgba(240,71,71,.5); + --alert-danger-bg-color: rgba(240,71,71,.1); + + --alert-tip-border-color: rgba(255,255,255,.5); + --alert-tip-bg-color: rgba(255,255,255,.1); + + --blockquote-border-color: rgba(255,255,255,.5); + --blockquote-bg-color: rgba(255,255,255,.1); + + --breadcrumb-bg-color: #2f3136; + + /* Inline Code */ + + --ref-bg-color: black; + --ref-color: #89d4f1; + + /* Code Blocks */ + + --code-bg-color: #151515; + --code-color: #d6deeb; + --code-keyword-color: #569cd6; + --code-comment-color: #57a64a; + --code-macro-color: #beb7ff; + --code-string-color: #d69d85; + --code-string-escape-color: #ffd68f; + --code-field-color: #c8c8c8; + --code-function-color: #dcdcaa; + --code-control-color: #d8a0df; + --code-class-color: #4ec9b0; + --code-number-color: #b5cea8; + --code-params-color: #9a9a9a; + --code-breakpoint-color: #8c2f2f; +} + +/* Code Block Overrides */ + +pre, legend { + --scrollbar-thumb-bg-color: #333; +} \ No newline at end of file diff --git a/docfx/docfx_project/templates/singulinkfx/styles/discord.css b/docfx/docfx_project/templates/singulinkfx/styles/discord.css new file mode 100644 index 000000000..bb3e55a06 --- /dev/null +++ b/docfx/docfx_project/templates/singulinkfx/styles/discord.css @@ -0,0 +1,681 @@ +/* Discord Style */ + +::-webkit-scrollbar { + width: 10px; +} + +::-webkit-scrollbar-track { + background: var(--scrollbar-bg-color); +} + +::-webkit-scrollbar-thumb { + background: var(--scrollbar-thumb-bg-color); + border-color: var(--scrollbar-thumb-border-color); + border-radius: 5px; +} + +::marker { + unicode-bidi: isolate; + font-variant-numeric: tabular-nums; + text-transform: none; + text-indent: 0px !important; + text-align: start !important; + text-align-last: start !important; +} + +*, :after, :before +{ + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +html, body +{ + padding: 0; + margin: 0; + font: 15px/150% 'Roboto', sans-serif; + overflow: hidden; + color: var(--text-color); + background-color: var(--main-bg-color); + + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; +} + +img { + max-width: 100%; +} + +ul > li, ol > li { + display: list-item; +} + +h1,h2,h3,h4,h5 +{ + color: var(--link-active-color); + position: relative; +} + +h1, h2 +{ + margin-block-start: 2em; +} + +h3 +{ + margin-block-start: 1em; + font-weight: 300; + font-size: 1.5em; + color: var(--h3-color); + margin-block-start: 3em; +} + +h4 +{ + opacity: 1; + color: var(--h4-color); + font-size: large; + border-bottom: 2px solid var(--separator-color); + margin: 20px 0 0 0; +} + + +h5 { + margin-block-end: .8em; + margin-block-start: 1em; + font-size: .85em; + font-weight: 500; + color: var(--h5-color); +} + +h6 { + font-size: .75em; + margin: 0; +} + +p +{ + font-weight: 400; +} + +ul +{ + position: relative; +} + +ul, ol +{ + padding-inline-start: 3em; +} + +ul.level1 +{ + list-style-type: none; + padding-inline-start: 0; +} + +ul.level2, ul.level3 +{ + padding-inline-start: 1em; + list-style-type: none; + font-size: .9em; +} + +a +{ + color: var(--link-color); + text-decoration: none; + transition: color .25s; +} + +a:focus, a:hover +{ + color: var(--link-hover-color); + text-decoration: underline; +} + +a.anchorjs-link:hover { + text-decoration: none; +} + +a.active, a:active +{ + color: var(--link-active-color); +} + +.body-content +{ + display: flex; + flex-direction: row; + height: 100%; + overflow-x: hidden; + overflow-y: hidden; +} + +.page-title +{ + margin-block-start: 0; +} + +nav +{ + width: 300px; + transition: left .5s ease-out; + position: fixed; + left: -350px; + top: 40px; + bottom: 0; + background-color: var(--sidebar-bg-color); + overflow-y: auto; + + display: flex; + flex-direction: column; + + z-index: 1000; +} + +h1:first-child +{ + margin-block-start: 1.1em; + margin-top: 1.1em; +} + +.sidebar +{ + padding: 32px 17px 32px 32px; + flex: 1; +} + +.sidebar-item +{ + font-size: 1em; + font-weight: 400; + display: block; + padding: 4px 16px; + color: var(--sidebar-item-color); +} + +.sidebar-item.large, #navbar .sidebar-item +{ + padding: 8px 16px; +} + +a.sidebar-item:hover, a.sidebar-item:focus +{ + color: var(--link-active-color); + text-decoration: none; +} + +a.sidebar-item.active +{ + color: var(--link-active-color); +} + +ul.level1 > li > a.sidebar-item +{ + background-color: transparent; + border-radius: 4px; +} + +#toc ul.level1 > li > a.sidebar-item.active +{ + background-color: var(--link-active-bg-color); +} + +.sidebar-item-separator +{ + height: 2px; + width: 100%; + background-color: var(--separator-color); + margin: 2em 0; + opacity: .8; +} + +span.sidebar-item +{ + font-weight: 700; + text-transform: uppercase; + font-size: .8em; + color: var(--text-color); + margin-block-start: 1.25em; +} + +.main-panel +{ + background-color: var(--main-bg-color); + flex: 1; + overflow-y: auto; + padding: 20px 40px; +} + +.top-navbar +{ + display: flex; + flex-direction: row; + align-items: center; + padding: 0 40px; + height: 40px; + background-color: var(--topbar-bg-color); +} + +.burger-icon +{ + margin-right: 1em; + color: var(--button-color); +} + +.burger-icon:hover, .burger-icon:focus +{ + color: var(--link-active-color); +} + +.burger-icon.active, .burger-icon:active +{ + color: var(--link-active-color); +} + +.brand +{ + display: flex; + align-items: center; + justify-content: start; +} + +.logomark +{ + height: 28px; +} + +.brand-title +{ + padding: 0 .5em; + font-size: .9em; + color: var(--link-active-color); +} + +.footer +{ + background-color: var(--footer-bg-color); + padding: 20px; + margin: 0 20px 20px 20px; + border-radius: 8px; + color: var(--link-active-color); +} + +.footer > h4 +{ + margin-block-start: 0; +} + +.blackout +{ + display: block; + visibility: hidden; + position: absolute; + z-index: 100; + top: 40px; + bottom: 0; + left: 0; + right: 0; + background-color: var(--footer-bg-color); +} + +@keyframes showThat { + 0% { opacity: 0; visibility: hidden; } + 1% { opacity: 0; visibility: visible; } + 100% { opacity: 1; visibility: visible;} +} + +@keyframes hideThat { + 0% { opacity: 1; visibility: visible; } + 99% { opacity: 0; visibility: visible; } + 100% { opacity: 0; visibility: hidden;} +} + +.showThat +{ + animation: showThat .5s forwards; +} + +.hideThat +{ + animation: hideThat .5s forwards; +} + + + +@media (min-width: 1024px) +{ + nav + { + position: relative; + left: 0!important; + top: 0; + bottom: 0; + } + + .top-navbar + { + display: none; + } + + .blackout + { + display: none; + } +} + +/* Table */ + +.table-responsive +{ + overflow-x: auto; + margin-bottom: 64px; +} + +table +{ + background-color: var(--code-bg-color); + border-collapse: collapse; + width: 100%; + table-layout: auto; +} + +table.table-striped tbody tr:nth-child(2n) +{ + background-color: var(--table-strip-bg-color); +} + +table thead +{ + background: var(--table-header-bg-color); +} + +table th +{ + color: var(--table-header-color); + text-transform: uppercase; + font-size: 12px; + line-height: 15px; + border-bottom: 1px solid var(--table-header-border-color); + padding: 8px; +} + +.table-condensed th { + text-align: left; +} + +table td +{ + padding: 8px; + font-weight: 300; +} + +table td > p +{ + margin: 0; +} + +/* Alerts */ +.alert { + border-radius: 4px; + padding: 8px; + margin: 25px 0; +} + +.alert > h5 +{ + display: none; + margin: 0; +} + +.alert > p +{ + margin: 0; + font-weight: 300; + font-size: 13px; +} + +.alert.alert-info +{ + border: 2px solid var(--alert-info-border-color); + background: var(--alert-info-bg-color); +} + +.alert.alert-warning +{ + border: 2px solid var(--alert-warning-border-color); + background: var(--alert-warning-bg-color); +} + +.alert.alert-danger +{ + border: 2px solid var(--alert-danger-border-color); + background: var(--alert-danger-bg-color); +} + +.TIP.alert.alert-info +{ + border: 2px solid var(--alert-tip-border-color); + background: var(--alert-tip-bg-color); +} + +blockquote { + margin: 8px 0; + border-left: 4px solid var(--blockquote-border-color); + padding: 8px; + background: var(--blockquote-bg-color); + border-radius: 4px; +} + +blockquote > p { + margin: 0; + font-style: italic; + font-size: 13px; +} + + +/* Breadcrumb */ + +#breadcrumb +{ + padding: 8px 16px; + background: var(--breadcrumb-bg-color); + border-radius: 4px; + margin-bottom: 30px; +} + +#breadcrumb:empty +{ + display: none; +} + +ul.breadcrumb +{ + display: flex; + flex-direction: row; + margin: 0; +} + +ul.breadcrumb > li { + margin-right: 6px; +} + +ul.breadcrumb > li::before +{ + content: "/"; + margin-right: 5px; +} + +ul.breadcrumb > li:first-child::before +{ + content: ""; + margin: 0; +} + + +/* Code */ + +legend, pre +{ + display: block; + background-color: var(--code-bg-color); + padding: 16px; + border-radius: 4px; +} + +code +{ + background-color: var(--code-bg-color); + padding: 2px 4px; + border-radius: 4px; +} + +.hljs +{ + background: transparent; +} + +/* DocFX related */ + +.small { + font-size: .9em; +} + +.pull-right +{ + float: right; +} + +.hide +{ + display: none; +} + +@media (max-width: 1023.98px) +{ + .mobile-hide + { + display: none; + } +} + +li +{ + display: block; + position: relative; +} + +.expand-stub +{ + cursor: pointer; + position: absolute; + width: 20px; + height: 20px; + left: -10px; +} + +ul.level1 > li > .expand-stub +{ + display: none; +} + +.toc .nav > li > .expand-stub::before, .toc .nav > li.active > .expand-stub::before +{ + content: " "; + position: absolute; + transform: rotate(-90deg); + width: 10px; + height: 10px; + top: 5px; + left: 5px; + background-repeat: no-repeat; + background: url(down-arrow.svg); +} + +.toc .nav > li.active > .expand-stub::before, .toc .nav > li.in > .expand-stub::before, .toc .nav > li.in.active > .expand-stub::before, .toc .nav > li.filtered > .expand-stub::before +{ + transform: none; +} + +li > ul +{ + display: none; +} + +li.in > ul +{ + display: block; +} + +ul.level2 > li > a.sidebar-item, +ul.level3 > li > a.sidebar-item +{ + font-weight: 500; + font-size: .95em; + padding: 0; + margin: 2px 16px; +} + +ul.level2 > li > a.sidebar-item +{ + color: var(--sidebar-item-2nd-color); +} + +ul.level3 > li > a.sidebar-item +{ + color: var(--sidebar-item-3rd-color); +} + +ul.level2 > li > a.sidebar-item:hover, +ul.level2 > li > a.sidebar-item:focus, +ul.level3 > li > a.sidebar-item:hover, +ul.level3 > li > a.sidebar-item:focus +{ + color: var(--link-active-color); + text-decoration: underline; +} + +ul.level2 > li > a.sidebar-item.active, +ul.level3 > li > a.sidebar-item.active +{ + color: var(--link-active-color); +} + +.inheritance .level0:before, +.inheritance .level1:before, +.inheritance .level2:before, +.inheritance .level3:before, +.inheritance .level4:before, +.inheritance .level5:before { + content: '↳'; + margin-right: 5px; +} + +.inheritance .level0 { + margin-left: 0em; +} + +.inheritance .level1 { + margin-left: 1em; +} + +.inheritance .level2 { + margin-left: 2em; +} + +.inheritance .level3 { + margin-left: 3em; +} + +.inheritance .level4 { + margin-left: 4em; +} + +.inheritance .level5 { + margin-left: 5em; +} \ No newline at end of file diff --git a/docfx/docfx_project/templates/singulinkfx/styles/down-arrow.svg b/docfx/docfx_project/templates/singulinkfx/styles/down-arrow.svg new file mode 100644 index 000000000..e086126a2 --- /dev/null +++ b/docfx/docfx_project/templates/singulinkfx/styles/down-arrow.svg @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docfx/docfx_project/templates/singulinkfx/styles/jquery.twbsPagination.js b/docfx/docfx_project/templates/singulinkfx/styles/jquery.twbsPagination.js new file mode 100644 index 000000000..332c01c62 --- /dev/null +++ b/docfx/docfx_project/templates/singulinkfx/styles/jquery.twbsPagination.js @@ -0,0 +1,317 @@ +/*! + * jQuery pagination plugin v1.4.1 + * http://esimakin.github.io/twbs-pagination/ + * + * Copyright 2014-2016, Eugene Simakin + * Released under Apache 2.0 license + * http://apache.org/licenses/LICENSE-2.0.html + */ +(function ($, window, document, undefined) { + + 'use strict'; + + var old = $.fn.twbsPagination; + + // PROTOTYPE AND CONSTRUCTOR + + var TwbsPagination = function (element, options) { + this.$element = $(element); + this.options = $.extend({}, $.fn.twbsPagination.defaults, options); + + if (this.options.startPage < 1 || this.options.startPage > this.options.totalPages) { + throw new Error('Start page option is incorrect'); + } + + this.options.totalPages = parseInt(this.options.totalPages); + if (isNaN(this.options.totalPages)) { + throw new Error('Total pages option is not correct!'); + } + + this.options.visiblePages = parseInt(this.options.visiblePages); + if (isNaN(this.options.visiblePages)) { + throw new Error('Visible pages option is not correct!'); + } + + if (this.options.onPageClick instanceof Function) { + this.$element.first().on('page', this.options.onPageClick); + } + + // hide if only one page exists + if (this.options.hideOnlyOnePage && this.options.totalPages == 1) { + this.$element.trigger('page', 1); + return this; + } + + if (this.options.totalPages < this.options.visiblePages) { + this.options.visiblePages = this.options.totalPages; + } + + if (this.options.href) { + this.options.startPage = this.getPageFromQueryString(); + if (!this.options.startPage) { + this.options.startPage = 1; + } + } + + var tagName = (typeof this.$element.prop === 'function') ? + this.$element.prop('tagName') : this.$element.attr('tagName'); + + if (tagName === 'UL') { + this.$listContainer = this.$element; + } else { + this.$listContainer = $('
      '); + } + + this.$listContainer.addClass(this.options.paginationClass); + + if (tagName !== 'UL') { + this.$element.append(this.$listContainer); + } + + if (this.options.initiateStartPageClick) { + this.show(this.options.startPage); + } else { + this.render(this.getPages(this.options.startPage)); + this.setupEvents(); + } + + return this; + }; + + TwbsPagination.prototype = { + + constructor: TwbsPagination, + + destroy: function () { + this.$element.empty(); + this.$element.removeData('twbs-pagination'); + this.$element.off('page'); + + return this; + }, + + show: function (page) { + if (page < 1 || page > this.options.totalPages) { + throw new Error('Page is incorrect.'); + } + this.currentPage = page; + + this.render(this.getPages(page)); + this.setupEvents(); + + this.$element.trigger('page', page); + + return this; + }, + + buildListItems: function (pages) { + var listItems = []; + + if (this.options.first) { + listItems.push(this.buildItem('first', 1)); + } + + if (this.options.prev) { + var prev = pages.currentPage > 1 ? pages.currentPage - 1 : this.options.loop ? this.options.totalPages : 1; + listItems.push(this.buildItem('prev', prev)); + } + + for (var i = 0; i < pages.numeric.length; i++) { + listItems.push(this.buildItem('page', pages.numeric[i])); + } + + if (this.options.next) { + var next = pages.currentPage < this.options.totalPages ? pages.currentPage + 1 : this.options.loop ? 1 : this.options.totalPages; + listItems.push(this.buildItem('next', next)); + } + + if (this.options.last) { + listItems.push(this.buildItem('last', this.options.totalPages)); + } + + return listItems; + }, + + buildItem: function (type, page) { + var $itemContainer = $('
    • '), + $itemContent = $(''), + itemText = this.options[type] ? this.makeText(this.options[type], page) : page; + + $itemContainer.addClass(this.options[type + 'Class']); + $itemContainer.data('page', page); + $itemContainer.data('page-type', type); + $itemContainer.append($itemContent.attr('href', this.makeHref(page)).addClass(this.options.anchorClass).html(itemText)); + + return $itemContainer; + }, + + getPages: function (currentPage) { + var pages = []; + + var half = Math.floor(this.options.visiblePages / 2); + var start = currentPage - half + 1 - this.options.visiblePages % 2; + var end = currentPage + half; + + // handle boundary case + if (start <= 0) { + start = 1; + end = this.options.visiblePages; + } + if (end > this.options.totalPages) { + start = this.options.totalPages - this.options.visiblePages + 1; + end = this.options.totalPages; + } + + var itPage = start; + while (itPage <= end) { + pages.push(itPage); + itPage++; + } + + return {"currentPage": currentPage, "numeric": pages}; + }, + + render: function (pages) { + var _this = this; + this.$listContainer.children().remove(); + var items = this.buildListItems(pages); + jQuery.each(items, function(key, item){ + _this.$listContainer.append(item); + }); + + this.$listContainer.children().each(function () { + var $this = $(this), + pageType = $this.data('page-type'); + + switch (pageType) { + case 'page': + if ($this.data('page') === pages.currentPage) { + $this.addClass(_this.options.activeClass); + } + break; + case 'first': + $this.toggleClass(_this.options.disabledClass, pages.currentPage === 1); + break; + case 'last': + $this.toggleClass(_this.options.disabledClass, pages.currentPage === _this.options.totalPages); + break; + case 'prev': + $this.toggleClass(_this.options.disabledClass, !_this.options.loop && pages.currentPage === 1); + break; + case 'next': + $this.toggleClass(_this.options.disabledClass, + !_this.options.loop && pages.currentPage === _this.options.totalPages); + break; + default: + break; + } + + }); + }, + + setupEvents: function () { + var _this = this; + this.$listContainer.off('click').on('click', 'li', function (evt) { + var $this = $(this); + if ($this.hasClass(_this.options.disabledClass) || $this.hasClass(_this.options.activeClass)) { + return false; + } + // Prevent click event if href is not set. + !_this.options.href && evt.preventDefault(); + _this.show(parseInt($this.data('page'))); + }); + }, + + makeHref: function (page) { + return this.options.href ? this.generateQueryString(page) : "#"; + }, + + makeText: function (text, page) { + return text.replace(this.options.pageVariable, page) + .replace(this.options.totalPagesVariable, this.options.totalPages) + }, + getPageFromQueryString: function (searchStr) { + var search = this.getSearchString(searchStr), + regex = new RegExp(this.options.pageVariable + '(=([^&#]*)|&|#|$)'), + page = regex.exec(search); + if (!page || !page[2]) { + return null; + } + page = decodeURIComponent(page[2]); + page = parseInt(page); + if (isNaN(page)) { + return null; + } + return page; + }, + generateQueryString: function (pageNumber, searchStr) { + var search = this.getSearchString(searchStr), + regex = new RegExp(this.options.pageVariable + '=*[^&#]*'); + if (!search) return ''; + return '?' + search.replace(regex, this.options.pageVariable + '=' + pageNumber); + + }, + getSearchString: function (searchStr) { + var search = searchStr || window.location.search; + if (search === '') { + return null; + } + if (search.indexOf('?') === 0) search = search.substr(1); + return search; + } + + }; + + // PLUGIN DEFINITION + + $.fn.twbsPagination = function (option) { + var args = Array.prototype.slice.call(arguments, 1); + var methodReturn; + + var $this = $(this); + var data = $this.data('twbs-pagination'); + var options = typeof option === 'object' ? option : {}; + + if (!data) $this.data('twbs-pagination', (data = new TwbsPagination(this, options) )); + if (typeof option === 'string') methodReturn = data[ option ].apply(data, args); + + return ( methodReturn === undefined ) ? $this : methodReturn; + }; + + $.fn.twbsPagination.defaults = { + totalPages: 1, + startPage: 1, + visiblePages: 5, + initiateStartPageClick: true, + hideOnlyOnePage: false, + href: false, + pageVariable: '{{page}}', + totalPagesVariable: '{{total_pages}}', + page: null, + first: 'First', + prev: 'Previous', + next: 'Next', + last: 'Last', + loop: false, + onPageClick: null, + paginationClass: 'pagination', + nextClass: 'page-item next', + prevClass: 'page-item prev', + lastClass: 'page-item last', + firstClass: 'page-item first', + pageClass: 'page-item', + activeClass: 'active', + disabledClass: 'disabled', + anchorClass: 'page-link' + }; + + $.fn.twbsPagination.Constructor = TwbsPagination; + + $.fn.twbsPagination.noConflict = function () { + $.fn.twbsPagination = old; + return this; + }; + + $.fn.twbsPagination.version = "1.4.1"; + +})(window.jQuery, window, document); diff --git a/docfx/docfx_project/templates/singulinkfx/styles/main.css b/docfx/docfx_project/templates/singulinkfx/styles/main.css new file mode 100644 index 000000000..e69de29bb diff --git a/docfx/docfx_project/templates/singulinkfx/styles/main.js b/docfx/docfx_project/templates/singulinkfx/styles/main.js new file mode 100644 index 000000000..e69de29bb diff --git a/docfx/docfx_project/templates/singulinkfx/styles/singulink.css b/docfx/docfx_project/templates/singulinkfx/styles/singulink.css new file mode 100644 index 000000000..b7200ca68 --- /dev/null +++ b/docfx/docfx_project/templates/singulinkfx/styles/singulink.css @@ -0,0 +1,471 @@ +body { + font-size: var(--base-font-size); +} + +@media (max-width: 1024px) { + body { + font-size: var(--smalldevice-base-font-size); + } +} + +/* Headings */ + +h1, h2, h3, h4, h5 { + line-height: initial; +} + +h1, h1:first-child { + font-size: 2.25em; + letter-spacing: 0.5px; + color: var(--h1-color); + margin-block-start: 1em; + margin-block-end: -0.05em; +} + +.article h1 { + margin-block-end: -0.2em; +} + +h2 { + font-size: 2.1em; + color: var(--h2-color); +} + +.article h2 { + margin-block-start: 1.3em; + padding-bottom: 6px; + border-bottom: 1px solid var(--separator-color); +} + +h3 { + font-size: 1.95em; + font-weight: 500; + margin-block-start: 1.7em; +} + +.article h3 { + font-size: 1.85em; + font-weight: 500; + margin-block-start: 1.2em; + margin-block-end: 0.9em; +} + +h4 { + font-size: 1.8em; + font-weight: 400; + margin-block-start: 2em; + padding-bottom: 10px; +} + +.article h4 { + font-size: 1.5em; + font-weight: 300; + margin-block-start: 1em; + margin-block-end: 1em; + padding-bottom: 0; + border-bottom: none; +} + +h5 { + font-size: 1.1em; +} + +.article h5 { + font-size: 1.13em; + font-weight: 400; + text-decoration: underline; + margin-block-start: 1.5em; + margin-block-end: 1.0em; +} + +a.brand:hover +{ + text-decoration: none; +} + +a.brand .brand-title { + font-size: 1.4em; + font-weight: 500; + letter-spacing: 0.5px; + color: var(--appname-color); + margin-top: 1px; + padding: 0 0 0 0.4em; +} + +@media (min-width: 1024px) { + a.brand .brand-title { + font-size: 1.55em; + } +} + + +a.brand .logomark { + height: 35px; +} + +/* Top bar */ + +.top-navbar { + height: 60px; + padding: 0 0 0 10px; +} + +.burger-icon { + margin-right: 10px; +} + +/* Side Bar */ + +.sidebar { + padding: 25px 17px 32px 17px; +} + +.blackout { + top: 60px; +} + +@media (max-width: 1023.98px) { + .navbar-nav { + margin-top: 0; + } +} + +nav { + width: 94%; + max-width: var(--sidebar-width); + left: calc(var(--sidebar-width) * -1); +} + +@media (max-width: 1023.98px) { + nav { + top: 60px; + } +} + +nav ul { + list-style-type: none; +} + +nav .nav a, nav .nav a:hover { + text-decoration: none; + cursor: pointer; + display: block; +} + +nav a.sidebar-item { + padding: 4px 0 4px 10px; + cursor: pointer; +} + +nav a:focus, nav a.sidebar-item:hover, nav a.sidebar-item:focus { + text-decoration: underline; +} + +nav a, nav a:hover, nav a:focus { + color: var(--sidebar-item-color) !important; +} + +nav a.active, nav a.active:hover, nav a.active:focus { + color: var(--sidebar-active-item-color) !important; +} + +.sidebar-item-separator { + margin: 20px 0; +} + +#toc ul li a { + padding: 0 0 0 10px; +} + +.search { + background: var(--search-bg-color); + border: 1px solid var(--search-border-color); + border-radius: 5px; + position: relative; + margin-block-start: 25px; +} + +@media (max-width: 1023.98px) { + .search { + margin-block-start: 0; + margin-block-end: 15px; + } +} + +.search > input { + font-size: 0.95em; + color: var(--search-color); + border: 0; + background: none; + padding: 11px 30px 10px 37px; + width: 100%; +} + +.search > input:focus { + outline: 0; +} + +.search > .search-icon { + font-size: 1.2em; + color: var(--search-searchicon-color); + position: absolute; + top: 9px; + left: 9px; +} + +.toc-filter { + background: var(--toc-filter-bg-color); + border: 1px solid var(--toc-filter-border-color); + border-radius: 5px; + position: relative; +} + +.toc-filter > input { + font-size: 0.95em; + color: var(--toc-filter-color); + border: 0; + background: none; + padding: 11px 30px 10px 37px; + width: 100%; +} + +.toc-filter > input:focus { + outline: 0; +} + +.toc-filter > .filter-icon { + font-size: 1.2em; + color: var(--toc-filter-filtericon-color); + position: absolute; + top: 9px; + left: 9px; +} + +.toc-filter > .clear-icon { + color: var(--toc-filter-clearicon-color); + position: absolute; + top: 9px; + right: 9px; + cursor: pointer; +} + +.toc .nav > li > .expand-stub::before, .toc .nav > li.active > .expand-stub::before +{ + width: 8px; + height: 8px; + top: 6px; + left: 6px; +} + +#toc ul.level2 +{ + margin-bottom: 20px; +} + +#toc ul.level1 > li > a { + font-weight: 500; + margin-bottom: 10px; + padding: 5px 10px; +} + +#toc ul.level1 > li > a, #toc ul.level1 > li > a.active { + background-color: var(--sidebar-level1-item-bg-color) !important; + border-radius: 2px; +} + +#toc ul.level1 > li > a:hover, #toc ul.level1 > li > a.active:hover, +#toc ul.level1 > li > a:focus, #toc ul.level1 > li > a.active:focus { + background-color: var(--sidebar-level1-item-hover-bg-color) !important; + text-decoration: none; +} + +ul.level2 { + padding-inline-start: 0.7em; +} + +ul.level2 .expand-stub { + top: 1px; +} + +ul.level2 > li > a, ul.level2 > li > a.sidebar-item { + font-weight: 400; + color: var(--sidebar-item-color); + margin: 4px 0 4px; +} + +ul.level3 { + padding-inline-start: 1em; +} + +ul.level3 > li > a, ul.level3 > li > a.sidebar-item { + font-size: 1.05em; + color: var(--sidebar-item-color); + margin: 4px 0; +} + +ul.level4 { + padding-inline-start: 0; + margin-bottom: 12px; +} + +ul.level4 > li > a, ul.level4 > li > a.sidebar-item { + font-size: 1.05em; + color: var(--sidebar-item-color); + margin: 5px 0 5px 10px; +} + +/* Breadcrumbs */ + +.subnav.navbar { + margin: 0 -15px; +} + +#breadcrumb { + overflow: scroll; + margin-bottom: 0; +} + +#breadcrumb::-webkit-scrollbar { + display: none; +} + +#breadcrumb a { + white-space: nowrap; +} + +#breadcrumb wbr { + display: none; +} + +/* Search Results */ + +#search-results h1 { + margin-block-start: 0.5em; +} + +#search-results .item-title { + font-size: 1.3em; + margin-top: 1.5em; +} + +#search-results .item-href { + font-size: 0.85em; +} + +#search-results .item-brief { + margin-top: 0.7em; +} + +#search-results ul.pagination { + text-align: center; + padding: 10px 0 0 0; + margin-block-start: 40px; + border-top: 1px solid var(--separator-color); +} + +#search-results ul.pagination > li { + display: inline-block; + margin: 0 10px; +} + +#search-results ul.pagination > li.disabled a, #search-results ul.pagination > li.disabled a:hover { + color: var(--text-color); + cursor: txt; + text-decoration: none; +} + +/* Content */ + +.main-panel { + margin-bottom: 60px; + padding: 20px; +} + +@media (min-width: 1024px) { + .main-panel { + margin-bottom: 0; + padding: 20px 40px; + } +} + +.pull-right { + margin-top: 70px; + /* Fix unclickable links */ + position: relative; + z-index: 1; +} + +.divider { + margin-left: 4px; +} + +article ul li, article ol li { + margin-bottom: 10px; +} + +legend, pre { + padding: 6px; +} + +.hljs { + color: var(--code-color); +} + +.hljs::-webkit-scrollbar { + height: 6px; +} + +.hljs-keyword, .hljs-title, .hljs-built_in { + font-style: normal; +} + +p .xref, code { + background-color: var(--ref-bg-color); + color: var(--ref-color); + padding: 2px 3px; + font-family: monospace; + font-size: 0.95em; + border-radius: 6px; +} + +span.parametername { + font-family: monospace; +} + +.table { + width: auto; +} + +.table-responsive { + margin-bottom: 0; +} + +table th { + font-size: 14px; + padding: 9px 10px; +} + +table td p { + font-weight: 300; +} + +table td { + padding: 6px 10px; +} + +.footer { + text-align: center; + color: var(--text-color); + padding: 10px; +} + +.footer a:hover, .footer a:focus { + text-decoration: underline; +} + +.copyright-footer { + font-size: 0.85em; + font-weight: bold; + text-align: center; + margin-block-start: 30px; +} \ No newline at end of file diff --git a/docfx/docfx_project/templates/singulinkfx/styles/singulink.js b/docfx/docfx_project/templates/singulinkfx/styles/singulink.js new file mode 100644 index 000000000..c2c0b4c45 --- /dev/null +++ b/docfx/docfx_project/templates/singulinkfx/styles/singulink.js @@ -0,0 +1,38 @@ +// Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information. + +function toggleMenu() { + + var sidebar = document.getElementById("sidebar"); + var blackout = document.getElementById("blackout"); + + if (sidebar.style.left === "0px") + { + sidebar.style.left = "-" + sidebar.offsetWidth + "px"; + blackout.classList.remove("showThat"); + blackout.classList.add("hideThat"); + } + else + { + sidebar.style.left = "0px"; + blackout.classList.remove("hideThat"); + blackout.classList.add("showThat"); + } +} + +$(function() { + $('table').each(function(a, tbl) { + var currentTableRows = $(tbl).find('tbody tr').length; + $(tbl).find('th').each(function(i) { + var remove = 0; + var currentTable = $(this).parents('table'); + + var tds = currentTable.find('tr td:nth-child(' + (i + 1) + ')'); + tds.each(function(j) { if ($(this).text().trim() === '') remove++; }); + + if (remove == currentTableRows) { + $(this).hide(); + tds.hide(); + } + }); + }); +}); \ No newline at end of file diff --git a/docfx/docfx_project/templates/singulinkfx/styles/url.min.js b/docfx/docfx_project/templates/singulinkfx/styles/url.min.js new file mode 100644 index 000000000..8057e0aa0 --- /dev/null +++ b/docfx/docfx_project/templates/singulinkfx/styles/url.min.js @@ -0,0 +1 @@ +/*! url - v1.8.6 - 2013-11-22 */window.url=function(){function a(a){return!isNaN(parseFloat(a))&&isFinite(a)}return function(b,c){var d=c||window.location.toString();if(!b)return d;b=b.toString(),"//"===d.substring(0,2)?d="http:"+d:1===d.split("://").length&&(d="http://"+d),c=d.split("/");var e={auth:""},f=c[2].split("@");1===f.length?f=f[0].split(":"):(e.auth=f[0],f=f[1].split(":")),e.protocol=c[0],e.hostname=f[0],e.port=f[1]||("https"===e.protocol.split(":")[0].toLowerCase()?"443":"80"),e.pathname=(c.length>3?"/":"")+c.slice(3,c.length).join("/").split("?")[0].split("#")[0];var g=e.pathname;"/"===g.charAt(g.length-1)&&(g=g.substring(0,g.length-1));var h=e.hostname,i=h.split("."),j=g.split("/");if("hostname"===b)return h;if("domain"===b)return/^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/.test(h)?h:i.slice(-2).join(".");if("sub"===b)return i.slice(0,i.length-2).join(".");if("port"===b)return e.port;if("protocol"===b)return e.protocol.split(":")[0];if("auth"===b)return e.auth;if("user"===b)return e.auth.split(":")[0];if("pass"===b)return e.auth.split(":")[1]||"";if("path"===b)return e.pathname;if("."===b.charAt(0)){if(b=b.substring(1),a(b))return b=parseInt(b,10),i[0>b?i.length+b:b-1]||""}else{if(a(b))return b=parseInt(b,10),j[0>b?j.length+b:b]||"";if("file"===b)return j.slice(-1)[0];if("filename"===b)return j.slice(-1)[0].split(".")[0];if("fileext"===b)return j.slice(-1)[0].split(".")[1]||"";if("?"===b.charAt(0)||"#"===b.charAt(0)){var k=d,l=null;if("?"===b.charAt(0)?k=(k.split("?")[1]||"").split("#")[0]:"#"===b.charAt(0)&&(k=k.split("#")[1]||""),!b.charAt(1))return k;b=b.substring(1),k=k.split("&");for(var m=0,n=k.length;n>m;m++)if(l=k[m].split("="),l[0]===b)return l[1]||"";return null}}return""}}(),"undefined"!=typeof jQuery&&jQuery.extend({url:function(a,b){return window.url(a,b)}}); \ No newline at end of file diff --git a/docfx/docfx_project/templates/singulinkfx/toc.html.tmpl b/docfx/docfx_project/templates/singulinkfx/toc.html.tmpl new file mode 100644 index 000000000..6549e62e7 --- /dev/null +++ b/docfx/docfx_project/templates/singulinkfx/toc.html.tmpl @@ -0,0 +1,22 @@ +{{!Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}} + +
      +
      + {{^_disableSideFilter}} +
      +
      + + + +
      +
      + {{/_disableSideFilter}} +
      +
      + {{^leaf}} + {{>partials/li}} + {{/leaf}} +
      +
      +
      +
      \ No newline at end of file diff --git a/docfx/docfx_project/toc.yml b/docfx/docfx_project/toc.yml index 7db7ecaea..842c6b36f 100644 --- a/docfx/docfx_project/toc.yml +++ b/docfx/docfx_project/toc.yml @@ -1,6 +1,2 @@ -- name: Articles - href: articles/ - homepage: articles/intro.md - name: API Documentation href: api/ - homepage: api/index.md diff --git a/src/Artemis.Core/Constants.cs b/src/Artemis.Core/Constants.cs index 15f337748..e37f18ffb 100644 --- a/src/Artemis.Core/Constants.cs +++ b/src/Artemis.Core/Constants.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.IO; using System.Reflection; using Artemis.Core.JsonConverters; @@ -90,6 +91,11 @@ public static class Constants /// public static readonly Plugin CorePlugin = new(CorePluginInfo, new DirectoryInfo(ApplicationFolder), null); + /// + /// Gets the startup arguments provided to the application + /// + public static ReadOnlyCollection StartupArguments { get; set; } = null!; + internal static readonly CorePluginFeature CorePluginFeature = new() {Plugin = CorePlugin, Profiler = CorePlugin.GetProfiler("Feature - Core")}; internal static readonly EffectPlaceholderPlugin EffectPlaceholderPlugin = new() {Plugin = CorePlugin, Profiler = CorePlugin.GetProfiler("Feature - Effect Placeholder")}; diff --git a/src/Artemis.Core/Services/CoreService.cs b/src/Artemis.Core/Services/CoreService.cs index 3aad1511b..81d8232ab 100644 --- a/src/Artemis.Core/Services/CoreService.cs +++ b/src/Artemis.Core/Services/CoreService.cs @@ -58,7 +58,6 @@ internal class CoreService : ICoreService _scriptingService = scriptingService; _loggingLevel = settingsService.GetSetting("Core.LoggingLevel", LogEventLevel.Debug); _frameStopWatch = new Stopwatch(); - StartupArguments = new List(); _rgbService.Surface.Updating += SurfaceOnUpdating; _loggingLevel.SettingChanged += (sender, args) => ApplyLoggingLevel(); @@ -78,7 +77,7 @@ internal class CoreService : ICoreService private void ApplyLoggingLevel() { - string? argument = StartupArguments.FirstOrDefault(a => a.StartsWith("--logging")); + string? argument = Constants.StartupArguments.FirstOrDefault(a => a.StartsWith("--logging")); if (argument != null) { // Parse the provided log level @@ -194,7 +193,6 @@ internal class CoreService : ICoreService public int FrameRate { get; private set; } public TimeSpan FrameTime { get; private set; } public bool ProfileRenderingDisabled { get; set; } - public List StartupArguments { get; set; } public bool IsElevated { get; set; } public void Dispose() @@ -217,7 +215,7 @@ internal class CoreService : ICoreService Constants.BuildInfo.BuildNumber, Constants.BuildInfo.SourceBranch ); - _logger.Information("Startup arguments: {args}", StartupArguments); + _logger.Information("Startup arguments: {args}", Constants.StartupArguments); _logger.Information("Elevated permissions: {perms}", IsElevated); _logger.Information("Stopwatch high resolution: {perms}", Stopwatch.IsHighResolution); @@ -230,9 +228,9 @@ internal class CoreService : ICoreService // Initialize the services _pluginManagementService.CopyBuiltInPlugins(); - _pluginManagementService.LoadPlugins(StartupArguments, IsElevated); + _pluginManagementService.LoadPlugins(IsElevated); - _rgbService.ApplyPreferredGraphicsContext(StartupArguments.Contains("--force-software-render")); + _rgbService.ApplyPreferredGraphicsContext(Constants.StartupArguments.Contains("--force-software-render")); _rgbService.SetRenderPaused(false); OnInitialized(); } diff --git a/src/Artemis.Core/Services/Interfaces/ICoreService.cs b/src/Artemis.Core/Services/Interfaces/ICoreService.cs index 4eb41b269..8dd297038 100644 --- a/src/Artemis.Core/Services/Interfaces/ICoreService.cs +++ b/src/Artemis.Core/Services/Interfaces/ICoreService.cs @@ -27,12 +27,7 @@ public interface ICoreService : IArtemisService, IDisposable /// Gets or sets whether profiles are rendered each frame by calling their Render method /// bool ProfileRenderingDisabled { get; set; } - - /// - /// Gets or sets a list of startup arguments - /// - List StartupArguments { get; set; } - + /// /// Gets a boolean indicating whether Artemis is running in an elevated environment (admin permissions) /// diff --git a/src/Artemis.Core/Services/Interfaces/IPluginManagementService.cs b/src/Artemis.Core/Services/Interfaces/IPluginManagementService.cs index d1281ef8e..209158529 100644 --- a/src/Artemis.Core/Services/Interfaces/IPluginManagementService.cs +++ b/src/Artemis.Core/Services/Interfaces/IPluginManagementService.cs @@ -26,7 +26,7 @@ public interface IPluginManagementService : IArtemisService, IDisposable /// /// Loads all installed plugins. If plugins already loaded this will reload them all /// - void LoadPlugins(List startupArguments, bool isElevated); + void LoadPlugins(bool isElevated); /// /// Unloads all installed plugins. diff --git a/src/Artemis.Core/Services/PluginManagementService.cs b/src/Artemis.Core/Services/PluginManagementService.cs index 5b1abd65b..c5eba9608 100644 --- a/src/Artemis.Core/Services/PluginManagementService.cs +++ b/src/Artemis.Core/Services/PluginManagementService.cs @@ -203,17 +203,17 @@ internal class PluginManagementService : IPluginManagementService #region Plugins - public void LoadPlugins(List startupArguments, bool isElevated) + public void LoadPlugins(bool isElevated) { - if (startupArguments.Contains("--no-plugins")) + if (Constants.StartupArguments.Contains("--no-plugins")) { _logger.Warning("Artemis launched with --no-plugins, skipping the loading of plugins"); return; } - bool ignorePluginLock = startupArguments.Contains("--ignore-plugin-lock"); - bool stayElevated = startupArguments.Contains("--force-elevation"); - bool droppedAdmin = startupArguments.Contains("--dropped-admin"); + bool ignorePluginLock = Constants.StartupArguments.Contains("--ignore-plugin-lock"); + bool stayElevated = Constants.StartupArguments.Contains("--force-elevation"); + bool droppedAdmin = Constants.StartupArguments.Contains("--dropped-admin"); if (LoadingPlugins) throw new ArtemisCoreException("Cannot load plugins while a previous load hasn't been completed yet."); diff --git a/src/Artemis.Core/Services/WebServer/WebServerService.cs b/src/Artemis.Core/Services/WebServer/WebServerService.cs index 0fffc9570..f082ed6df 100644 --- a/src/Artemis.Core/Services/WebServer/WebServerService.cs +++ b/src/Artemis.Core/Services/WebServer/WebServerService.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; +using System.Threading; using System.Threading.Tasks; using Artemis.Core.Modules; using EmbedIO; @@ -17,15 +18,19 @@ internal class WebServerService : IWebServerService, IDisposable private readonly List _controllers; private readonly ILogger _logger; private readonly List _modules; + private readonly PluginSetting _webServerEnabledSetting; private readonly PluginSetting _webServerPortSetting; + private CancellationTokenSource? _cts; - public WebServerService(ILogger logger, ISettingsService settingsService, IPluginManagementService pluginManagementService) + public WebServerService(ILogger logger, ICoreService coreService, ISettingsService settingsService, IPluginManagementService pluginManagementService) { _logger = logger; _controllers = new List(); _modules = new List(); + _webServerEnabledSetting = settingsService.GetSetting("WebServer.Enabled", true); _webServerPortSetting = settingsService.GetSetting("WebServer.Port", 9696); + _webServerEnabledSetting.SettingChanged += WebServerEnabledSettingOnSettingChanged; _webServerPortSetting.SettingChanged += WebServerPortSettingOnSettingChanged; pluginManagementService.PluginFeatureDisabled += PluginManagementServiceOnPluginFeatureDisabled; @@ -33,9 +38,9 @@ internal class WebServerService : IWebServerService, IDisposable StartWebServer(); } - protected virtual void OnWebServerStarting() + private void WebServerEnabledSettingOnSettingChanged(object? sender, EventArgs e) { - WebServerStarting?.Invoke(this, EventArgs.Empty); + StartWebServer(); } private void WebServerPortSettingOnSettingChanged(object? sender, EventArgs e) @@ -72,14 +77,23 @@ internal class WebServerService : IWebServerService, IDisposable public WebServer? Server { get; private set; } public PluginsModule PluginsModule { get; } - public event EventHandler? WebServerStarting; #region Web server managament private WebServer CreateWebServer() { - Server?.Dispose(); - Server = null; + if (Server != null) + { + if (_cts != null) + { + _cts.Cancel(); + _cts = null; + } + + Server.Dispose(); + OnWebServerStopped(); + Server = null; + } WebApiModule apiModule = new("/", JsonNetSerializer); PluginsModule.ServerUrl = $"http://localhost:{_webServerPortSetting.Value}/"; @@ -112,8 +126,20 @@ internal class WebServerService : IWebServerService, IDisposable private void StartWebServer() { Server = CreateWebServer(); + + if (!_webServerEnabledSetting.Value) + return; + + if (Constants.StartupArguments.Contains("--disable-webserver")) + { + _logger.Warning("Artemis launched with --disable-webserver, not enabling the webserver"); + return; + } + OnWebServerStarting(); - Server.Start(); + _cts = new CancellationTokenSource(); + Server.Start(_cts.Token); + OnWebServerStarted(); } #endregion @@ -276,4 +302,27 @@ internal class WebServerService : IWebServerService, IDisposable } #endregion + + #region Events + + protected virtual void OnWebServerStopped() + { + WebServerStopped?.Invoke(this, EventArgs.Empty); + } + + protected virtual void OnWebServerStarting() + { + WebServerStarting?.Invoke(this, EventArgs.Empty); + } + + protected virtual void OnWebServerStarted() + { + WebServerStarted?.Invoke(this, EventArgs.Empty); + } + + public event EventHandler? WebServerStopped; + public event EventHandler? WebServerStarting; + public event EventHandler? WebServerStarted; + + #endregion } \ No newline at end of file diff --git a/src/Artemis.Core/Utilities/Numeric.cs b/src/Artemis.Core/Utilities/Numeric.cs index e5dca7fd5..36f04e14f 100644 --- a/src/Artemis.Core/Utilities/Numeric.cs +++ b/src/Artemis.Core/Utilities/Numeric.cs @@ -159,7 +159,12 @@ public readonly struct Numeric : IComparable, IConvertible { return (byte) Math.Clamp(p._value, 0, 255); } - + + public static implicit operator Numeric(double d) => new(d); + public static implicit operator Numeric(float f) => new(f); + public static implicit operator Numeric(int i) => new(i); + public static implicit operator Numeric(byte b) => new(b); + public static implicit operator long(Numeric p) { return (long) p._value; diff --git a/src/Artemis.Core/VisualScripting/InputPin.cs b/src/Artemis.Core/VisualScripting/InputPin.cs index 108e7c620..9db82c693 100644 --- a/src/Artemis.Core/VisualScripting/InputPin.cs +++ b/src/Artemis.Core/VisualScripting/InputPin.cs @@ -94,18 +94,11 @@ public sealed class InputPin : Pin /// The new type of the pin. public void ChangeType(Type type) { - if (_type == type) + if (type == _type) return; - - // Disconnect pins incompatible with the new type - List toDisconnect = ConnectedTo.Where(p => !p.IsTypeCompatible(type)).ToList(); - foreach (IPin pin in toDisconnect) - DisconnectFrom(pin); - - // Change the type - SetAndNotify(ref _type, type, nameof(Type)); + + base.ChangeType(type, ref _type); Value = type.GetDefault(); - IsNumeric = type == typeof(Numeric); } private void Evaluate() @@ -117,10 +110,10 @@ public sealed class InputPin : Pin else Value = Type.GetDefault()!; } - else - { + else if (ConnectedTo.Count > 0) Value = ConnectedTo[0].PinValue; - } + else + Value = null; } #endregion diff --git a/src/Artemis.Core/VisualScripting/Interfaces/IPin.cs b/src/Artemis.Core/VisualScripting/Interfaces/IPin.cs index 819d57fc1..67a0619c3 100644 --- a/src/Artemis.Core/VisualScripting/Interfaces/IPin.cs +++ b/src/Artemis.Core/VisualScripting/Interfaces/IPin.cs @@ -85,6 +85,7 @@ public interface IPin /// Determines whether this pin is compatible with the given type /// /// The type to check for compatibility + /// A boolean indicating whether or not enums should be exactly equal or just both be enums /// if the type is compatible, otherwise . - public bool IsTypeCompatible(Type type); + public bool IsTypeCompatible(Type type, bool forgivingEnumMatching = true); } \ No newline at end of file diff --git a/src/Artemis.Core/VisualScripting/NodeScript.cs b/src/Artemis.Core/VisualScripting/NodeScript.cs index 330b3b417..ce6a7af00 100644 --- a/src/Artemis.Core/VisualScripting/NodeScript.cs +++ b/src/Artemis.Core/VisualScripting/NodeScript.cs @@ -341,8 +341,14 @@ public abstract class NodeScript : CorePropertyChanged, INodeScript, IStorageMod private void SavePins(INode node, int collectionId, IEnumerable pins) { int sourcePinId = 0; - foreach (IPin sourcePin in pins.Where(p => p.Direction == PinDirection.Input)) + foreach (IPin sourcePin in pins) { + if (sourcePin.Direction == PinDirection.Output) + { + sourcePinId++; + continue; + } + foreach (IPin targetPin in sourcePin.ConnectedTo) { int targetPinCollectionId = -1; diff --git a/src/Artemis.Core/VisualScripting/OutputPin.cs b/src/Artemis.Core/VisualScripting/OutputPin.cs index f7d03e042..2e135eb13 100644 --- a/src/Artemis.Core/VisualScripting/OutputPin.cs +++ b/src/Artemis.Core/VisualScripting/OutputPin.cs @@ -84,15 +84,11 @@ public sealed class OutputPin : Pin /// The new type of the pin. public void ChangeType(Type type) { - // Disconnect pins incompatible with the new type - List toDisconnect = ConnectedTo.Where(p => !p.IsTypeCompatible(type)).ToList(); - foreach (IPin pin in toDisconnect) - DisconnectFrom(pin); + if (type == _type) + return; - // Change the type - SetAndNotify(ref _type, type, nameof(Type)); + base.ChangeType(type, ref _type); Value = type.GetDefault(); - IsNumeric = type == typeof(Numeric); } #endregion diff --git a/src/Artemis.Core/VisualScripting/Pin.cs b/src/Artemis.Core/VisualScripting/Pin.cs index 21e37c7f3..a562f7961 100644 --- a/src/Artemis.Core/VisualScripting/Pin.cs +++ b/src/Artemis.Core/VisualScripting/Pin.cs @@ -127,13 +127,46 @@ public abstract class Pin : CorePropertyChanged, IPin } /// - public bool IsTypeCompatible(Type type) + public bool IsTypeCompatible(Type type, bool forgivingEnumMatching = true) { return Type == type - || (Type == typeof(Enum) && type.IsEnum) - || (Type.IsEnum && type == typeof(Enum)) - || (Direction == PinDirection.Input && Type == typeof(object)) - || (Direction == PinDirection.Output && type == typeof(object)); + || (Direction == PinDirection.Input && type.IsAssignableTo(Type)) + || (Direction == PinDirection.Output && type.IsAssignableFrom(Type)) + || (Direction == PinDirection.Input && Type == typeof(Enum) && type.IsEnum && forgivingEnumMatching) + || (Direction == PinDirection.Output && type == typeof(Enum) && Type.IsEnum && forgivingEnumMatching); + } + + /// + /// Changes the type of this pin, disconnecting any pins that are incompatible with the new type. + /// + /// The new type of the pin. + /// The backing field of the current type of the pin. + protected void ChangeType(Type type, ref Type currentType) + { + // Enums are a special case that disconnect and, if still compatible, reconnect + if (type.IsEnum && currentType.IsEnum) + { + List connections = new(ConnectedTo); + DisconnectAll(); + + // Change the type + SetAndNotify(ref currentType, type, nameof(Type)); + IsNumeric = type == typeof(Numeric); + + foreach (IPin pin in connections.Where(p => p.IsTypeCompatible(type))) + ConnectTo(pin); + } + // Disconnect pins incompatible with the new type + else + { + List toDisconnect = ConnectedTo.Where(p => !p.IsTypeCompatible(type, false)).ToList(); + foreach (IPin pin in toDisconnect) + DisconnectFrom(pin); + + // Change the type + SetAndNotify(ref currentType, type, nameof(Type)); + IsNumeric = type == typeof(Numeric); + } } #endregion diff --git a/src/Artemis.UI.Shared/Services/NodeEditor/NodeConnectionStore.cs b/src/Artemis.UI.Shared/Services/NodeEditor/NodeConnectionStore.cs index 3598d41fa..596e66304 100644 --- a/src/Artemis.UI.Shared/Services/NodeEditor/NodeConnectionStore.cs +++ b/src/Artemis.UI.Shared/Services/NodeEditor/NodeConnectionStore.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; using Artemis.Core; namespace Artemis.UI.Shared.Services.NodeEditor; @@ -30,20 +31,18 @@ public class NodeConnectionStore public void Store() { _pinConnections.Clear(); - foreach (IPin nodePin in Node.Pins) - { + + // Iterate to save + foreach (IPin nodePin in Node.Pins.ToList()) + _pinConnections.Add(nodePin, new List(nodePin.ConnectedTo)); + foreach (IPin nodePin in Node.PinCollections.ToList().SelectMany(nodePinCollection => nodePinCollection)) _pinConnections.Add(nodePin, new List(nodePin.ConnectedTo)); - nodePin.DisconnectAll(); - } - foreach (IPinCollection nodePinCollection in Node.PinCollections) - { - foreach (IPin nodePin in nodePinCollection) - { - _pinConnections.Add(nodePin, new List(nodePin.ConnectedTo)); - nodePin.DisconnectAll(); - } - } + // Iterate to disconnect + foreach (IPin nodePin in Node.Pins.ToList()) + nodePin.DisconnectAll(); + foreach (IPin nodePin in Node.PinCollections.ToList().SelectMany(nodePinCollection => nodePinCollection)) + nodePin.DisconnectAll(); } /// @@ -51,23 +50,10 @@ public class NodeConnectionStore /// public void Restore() { - foreach (IPin nodePin in Node.Pins) + foreach ((IPin? pin, List? connections) in _pinConnections) { - if (!_pinConnections.TryGetValue(nodePin, out List? connections)) - continue; foreach (IPin connection in connections) - nodePin.ConnectTo(connection); - } - - foreach (IPinCollection nodePinCollection in Node.PinCollections) - { - foreach (IPin nodePin in nodePinCollection) - { - if (!_pinConnections.TryGetValue(nodePin, out List? connections)) - continue; - foreach (IPin connection in connections) - nodePin.ConnectTo(connection); - } + pin.ConnectTo(connection); } _pinConnections.Clear(); diff --git a/src/Artemis.UI.Windows/App.axaml.cs b/src/Artemis.UI.Windows/App.axaml.cs index 411bd3227..f82d58f29 100644 --- a/src/Artemis.UI.Windows/App.axaml.cs +++ b/src/Artemis.UI.Windows/App.axaml.cs @@ -1,3 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Net.Http; +using System.Threading; +using Artemis.Core; using Artemis.Core.Services; using Artemis.UI.Windows.Ninject; using Artemis.UI.Windows.Providers.Input; @@ -13,14 +21,23 @@ namespace Artemis.UI.Windows; public class App : Application { + private StandardKernel? _kernel; + private bool _shutDown; + // ReSharper disable NotAccessedField.Local private ApplicationStateManager? _applicationStateManager; + private Mutex? _artemisMutex; // ReSharper restore NotAccessedField.Local - private StandardKernel? _kernel; - public override void Initialize() { + // If Artemis is already running, bring it to foreground and stop this process + if (FocusExistingInstance()) + { + _shutDown = true; + Environment.Exit(1); + } + _kernel = ArtemisBootstrapper.Bootstrap(this, new WindowsModule()); Program.CreateLogger(_kernel); RxApp.MainThreadScheduler = AvaloniaScheduler.Instance; @@ -29,7 +46,7 @@ public class App : Application public override void OnFrameworkInitializationCompleted() { - if (ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktop || Design.IsDesignMode) + if (ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktop || Design.IsDesignMode || _shutDown) return; ArtemisBootstrapper.Initialize(); @@ -42,4 +59,55 @@ public class App : Application IInputService inputService = standardKernel.Get(); inputService.AddInputProvider(standardKernel.Get()); } + + private bool FocusExistingInstance() + { + _artemisMutex = new Mutex(true, "Artemis-3c24b502-64e6-4587-84bf-9072970e535f", out bool createdNew); + return !createdNew && RemoteFocus(); + } + + private bool RemoteFocus() + { + // At this point we cannot read the database yet to retrieve the web server port. + // Instead use the method external applications should use as well. + if (!File.Exists(Path.Combine(Constants.DataFolder, "webserver.txt"))) + { + KillOtherInstances(); + return false; + } + + string url = File.ReadAllText(Path.Combine(Constants.DataFolder, "webserver.txt")); + using HttpClient client = new(); + try + { + CancellationTokenSource cts = new(); + cts.CancelAfter(2000); + + HttpResponseMessage httpResponseMessage = client.Send(new HttpRequestMessage(HttpMethod.Post, url + "remote/bring-to-foreground"), cts.Token); + httpResponseMessage.EnsureSuccessStatusCode(); + return true; + } + catch (Exception) + { + KillOtherInstances(); + return false; + } + } + + private void KillOtherInstances() + { + // Kill everything else heh + List processes = Process.GetProcessesByName("Artemis.UI.Windows").Where(p => p.Id != Process.GetCurrentProcess().Id).ToList(); + foreach (Process process in processes) + { + try + { + process.Kill(true); + } + catch (Exception) + { + // ignored + } + } + } } \ No newline at end of file diff --git a/src/Artemis.UI.Windows/ApplicationStateManager.cs b/src/Artemis.UI.Windows/ApplicationStateManager.cs index 6225b10e5..c08f42316 100644 --- a/src/Artemis.UI.Windows/ApplicationStateManager.cs +++ b/src/Artemis.UI.Windows/ApplicationStateManager.cs @@ -3,12 +3,9 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; -using System.Net.Http; using System.Security.Principal; -using System.Threading; using Artemis.Core; using Artemis.Core.Services; -using Artemis.UI.Shared.Services; using Artemis.UI.Windows.Utilities; using Avalonia; using Avalonia.Controls.ApplicationLifetimes; @@ -19,14 +16,8 @@ namespace Artemis.UI.Windows; public class ApplicationStateManager { - private readonly IWindowService _windowService; - - // ReSharper disable once NotAccessedField.Local - Kept in scope to ensure it does not get released - private Mutex? _artemisMutex; - public ApplicationStateManager(IKernel kernel, string[] startupArguments) { - _windowService = kernel.Get(); StartupArguments = startupArguments; IsElevated = new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator); @@ -51,72 +42,6 @@ public class ApplicationStateManager public string[] StartupArguments { get; } public bool IsElevated { get; } - public bool FocusExistingInstance() - { - _artemisMutex = new Mutex(true, "Artemis-3c24b502-64e6-4587-84bf-9072970e535f", out bool createdNew); - if (createdNew) - return false; - - return RemoteFocus(); - } - - public void DisplayException(Exception e) - { - try - { - _windowService.ShowExceptionDialog("An unhandled exception occured", e); - } - catch - { - // ignored, we tried - } - } - - private bool RemoteFocus() - { - // At this point we cannot read the database yet to retrieve the web server port. - // Instead use the method external applications should use as well. - if (!File.Exists(Path.Combine(Constants.DataFolder, "webserver.txt"))) - { - KillOtherInstances(); - return false; - } - - string url = File.ReadAllText(Path.Combine(Constants.DataFolder, "webserver.txt")); - using HttpClient client = new(); - try - { - CancellationTokenSource cts = new(); - cts.CancelAfter(2000); - - HttpResponseMessage httpResponseMessage = client.Send(new HttpRequestMessage(HttpMethod.Post, url + "remote/bring-to-foreground"), cts.Token); - httpResponseMessage.EnsureSuccessStatusCode(); - return true; - } - catch (Exception) - { - KillOtherInstances(); - return false; - } - } - - private void KillOtherInstances() - { - // Kill everything else heh - List processes = Process.GetProcessesByName("Artemis.UI.Windows").Where(p => p.Id != Process.GetCurrentProcess().Id).ToList(); - foreach (Process process in processes) - { - try - { - process.Kill(true); - } - catch (Exception) - { - // ignored - } - } - } - private void UtilitiesOnRestartRequested(object? sender, RestartEventArgs e) { List argsList = new(); diff --git a/src/Artemis.UI/ArtemisBootstrapper.cs b/src/Artemis.UI/ArtemisBootstrapper.cs index 5eac6489d..7bade4f67 100644 --- a/src/Artemis.UI/ArtemisBootstrapper.cs +++ b/src/Artemis.UI/ArtemisBootstrapper.cs @@ -1,4 +1,7 @@ using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; using System.Reactive; using Artemis.Core; using Artemis.Core.Ninject; @@ -52,6 +55,8 @@ public static class ArtemisBootstrapper if (_application.ApplicationLifetime is not IClassicDesktopStyleApplicationLifetime desktop) return; + Constants.StartupArguments = new ReadOnlyCollection(new List(desktop.Args)); + // Don't shut down when the last window closes, we might still be active in the tray desktop.ShutdownMode = ShutdownMode.OnExplicitShutdown; // Create the root view model that drives the UI diff --git a/src/Artemis.UI/MainWindow.axaml.cs b/src/Artemis.UI/MainWindow.axaml.cs index df0371090..a22f46831 100644 --- a/src/Artemis.UI/MainWindow.axaml.cs +++ b/src/Artemis.UI/MainWindow.axaml.cs @@ -47,12 +47,12 @@ public class MainWindow : ReactiveCoreWindow private void OnActivated(object? sender, EventArgs e) { - ViewModel.Focused(); + ViewModel?.Focused(); } private void OnDeactivated(object? sender, EventArgs e) { - ViewModel.Unfocused(); + ViewModel?.Unfocused(); } private void InitializeComponent() diff --git a/src/Artemis.UI/Screens/Root/RootViewModel.cs b/src/Artemis.UI/Screens/Root/RootViewModel.cs index 0845b50e7..61fbe58c2 100644 --- a/src/Artemis.UI/Screens/Root/RootViewModel.cs +++ b/src/Artemis.UI/Screens/Root/RootViewModel.cs @@ -54,8 +54,7 @@ public class RootViewModel : ActivatableViewModelBase, IScreen, IMainWindowProvi _defaultTitleBarViewModel = defaultTitleBarViewModel; _sidebarVmFactory = sidebarVmFactory; _lifeTime = (IClassicDesktopStyleApplicationLifetime) Application.Current!.ApplicationLifetime!; - - coreService.StartupArguments = _lifeTime.Args.ToList(); + mainWindowService.ConfigureMainWindowProvider(this); DisplayAccordingToSettings(); @@ -99,8 +98,8 @@ public class RootViewModel : ActivatableViewModelBase, IScreen, IMainWindowProvi private void DisplayAccordingToSettings() { - bool autoRunning = _coreService.StartupArguments.Contains("--autorun"); - bool minimized = _coreService.StartupArguments.Contains("--minimized"); + bool autoRunning = Constants.StartupArguments.Contains("--autorun"); + bool minimized = Constants.StartupArguments.Contains("--minimized"); bool showOnAutoRun = _settingsService.GetSetting("UI.ShowOnStartup", true).Value; if ((autoRunning && !showOnAutoRun) || minimized) diff --git a/src/Artemis.UI/Screens/Settings/Tabs/GeneralTabView.axaml b/src/Artemis.UI/Screens/Settings/Tabs/GeneralTabView.axaml index 012f083ad..d8cd0ff65 100644 --- a/src/Artemis.UI/Screens/Settings/Tabs/GeneralTabView.axaml +++ b/src/Artemis.UI/Screens/Settings/Tabs/GeneralTabView.axaml @@ -13,364 +13,379 @@ x:DataType="settings:GeneralTabViewModel"> - - - - General - - - - - - - Auto-run on startup - - - - + + + + General + + + + + + + Auto-run on startup + + + + - - - Hide window on auto-run - - - - - - + + + Hide window on auto-run + + + + + + - - - Startup delay - - Set the amount of seconds to wait before auto-running Artemis. - - - If some devices don't work because Artemis starts before the manufacturer's software, try increasing this value. - - - - - - - - - sec - - - - - - - - - Log level - - - Sets the logging level, a higher logging level will result in more log files. - - - - - - - + + + Startup delay + + Set the amount of seconds to wait before auto-running Artemis. + + + If some devices don't work because Artemis starts before the manufacturer's software, try increasing this value. + + + + + + + + + sec + + + + - - - - Logs - - - Opens the directory where logs are stored. - - - -