diff --git a/app.py b/app.py index 5761030..f5bf86f 100644 --- a/app.py +++ b/app.py @@ -3,18 +3,15 @@ import os from html import escape from typing import Optional -from bs4 import BeautifulSoup from flask import Flask, request, send_from_directory, url_for, Response from flask import render_template -from minify_html import minify from werkzeug.exceptions import HTTPException -from website.content import get_articles, get_projects, get_tools, sanitize_input_tags, load_content_items, get_content, \ - get_applets -from website.contributors import reload_contributors_data, get_contributors_data +from website.content import get_projects, get_tools, sanitize_input_tags, load_content_items, get_content, \ + get_applets, get_projects_languages, get_projects_by_languages +from website.contributors import reload_contributors_data from website.domains import ALLOWED_DOMAINS -from website.l10n.utils import get_user_lang, localize, reload_strings, l10n_url_abs, l10n_url_switch, L10N, \ - DEFAULT_LANG +from website.l10n.utils import get_user_lang, localize, reload_strings, l10n_url_abs, l10n_url_switch, DEFAULT_LANG from website.renderers.applet import render_applet_scripts, render_applet_head from website.renderers.button import render_button from website.renderers.code import render_code_block @@ -22,6 +19,7 @@ from website.renderers.headings import render_heading, render_h2, render_h1, ren from website.renderers.paragraph import render_paragraph from website.renderers.lists import render_list_ul from website.renderers.splide import render_splide +from website.renderers.standalone import get_standalone_common_headers from website.sidebar import reload_sidebar_entries, get_sidebar_entries from website.sitemap import reload_sitemap_entries, get_sitemap_entries @@ -34,11 +32,15 @@ except ImportError: if os.environ.get('NP_HTML_POST_PROCESS', "NONE") == "MINIFY": print("Using 'minify' as HTML post-processor") + from minify_html import minify + def post_process_html(html: str) -> str: return minify(html).replace("> <", "><") elif os.environ.get('NP_HTML_POST_PROCESS', "NONE") == "BS4": print("Using 'BeautifulSoup4' as HTML post-processor") + from bs4 import BeautifulSoup + def post_process_html(html: str) -> str: return BeautifulSoup(html, features="html.parser").prettify() else: @@ -104,8 +106,11 @@ def inject_processors(): # Content get_content=get_content, - get_articles=get_articles, + get_applets=get_applets, + # get_articles=get_articles, get_projects=get_projects, + get_projects_by_languages=get_projects_by_languages, + get_projects_languages=get_projects_languages, get_tools=get_tools, # Renderers @@ -125,6 +130,9 @@ def inject_processors(): # Commons url_for=url_for, escape=escape, + + # Standalone + get_standalone_common_headers=get_standalone_common_headers ) @@ -150,12 +158,13 @@ def route_sitemap(): @app.route('/fr/', defaults={'lang': "fr"}) def route_root(lang: Optional[str]): user_lang = get_user_lang(lang, request.headers.get("HTTP_ACCEPT_LANGUAGE")) + return post_process_html(render_template( "pages/root.jinja", user_lang=user_lang, raw_lang=lang, request_path=request.path, - standalone="standalone" in request.args, + is_standalone="standalone" in request.args, )).replace("> <", "><") @@ -164,12 +173,13 @@ def route_root(lang: Optional[str]): @app.route('/fr/contact/', defaults={'lang': "fr"}) def route_contact(lang: Optional[str]): user_lang = get_user_lang(lang, request.headers.get("HTTP_ACCEPT_LANGUAGE")) + return post_process_html(render_template( "pages/contact.jinja", user_lang=user_lang, raw_lang=lang, request_path=request.path, - standalone="standalone" in request.args, + is_standalone="standalone" in request.args, )).replace("> <", "><") @@ -189,7 +199,7 @@ def route_content(lang: Optional[str]): user_lang=user_lang, raw_lang=lang, request_path=request.path, - standalone="standalone" in request.args, + is_standalone="standalone" in request.args, requested_tags=requested_tags, )).replace("> <", "><") @@ -215,7 +225,7 @@ def route_content_project(lang: Optional[str], project_id: str): user_lang=user_lang, raw_lang=lang, request_path=request.path, - standalone="standalone" in request.args, + is_standalone="standalone" in request.args, error_key=error_key, error_code=error_code, )).replace("> <", "><"), error_code @@ -225,7 +235,7 @@ def route_content_project(lang: Optional[str], project_id: str): user_lang=user_lang, raw_lang=lang, request_path=request.path, - standalone="standalone" in request.args, + is_standalone="standalone" in request.args, project_data=get_projects().get(project_id), project_id=project_id, )).replace("> <", "><") @@ -247,7 +257,7 @@ def route_tools_index(lang: Optional[str]): user_lang=user_lang, raw_lang=lang, request_path=request.path, - standalone="standalone" in request.args, + is_standalone="standalone" in request.args, requested_tags=requested_tags, )).replace("> <", "><") @@ -273,7 +283,7 @@ def route_tools_page(lang: Optional[str], tool_id: str): user_lang=user_lang, raw_lang=lang, request_path=request.path, - standalone="standalone" in request.args, + is_standalone="standalone" in request.args, error_key=error_key, error_code=error_code, )).replace("> <", "><"), error_code @@ -283,7 +293,7 @@ def route_tools_page(lang: Optional[str], tool_id: str): user_lang=user_lang, raw_lang=lang, request_path=request.path, - standalone="standalone" in request.args, + is_standalone="standalone" in request.args, tool_data=get_tools().get(tool_id), tool_id=tool_id, applet_data=get_applets().get(get_tools().get(tool_id).applet_id), @@ -300,7 +310,7 @@ def route_about(lang: Optional[str]): user_lang=user_lang, raw_lang=lang, request_path=request.path, - standalone="standalone" in request.args, + is_standalone="standalone" in request.args, )).replace("> <", "><") @@ -314,7 +324,7 @@ def route_privacy(lang: Optional[str]): user_lang=user_lang, raw_lang=lang, request_path=request.path, - standalone="standalone" in request.args, + is_standalone="standalone" in request.args, )).replace("> <", "><") @@ -328,7 +338,7 @@ def route_links(lang: Optional[str]): user_lang=user_lang, raw_lang=lang, request_path=request.path, - standalone="standalone" in request.args, + is_standalone="standalone" in request.args, )).replace("> <", "><") @@ -342,7 +352,7 @@ def route_debug(lang: Optional[str]): user_lang=user_lang, raw_lang=lang, request_path=request.path, - standalone="standalone" in request.args, + is_standalone="standalone" in request.args, )).replace("> <", "><") @@ -361,7 +371,7 @@ def handle_exception(e: Exception): user_lang=DEFAULT_LANG, raw_lang=DEFAULT_LANG, request_path=request.path, - standalone="standalone" in request.args, + is_standalone="standalone" in request.args, error_key=str(e.code), error_code=e.code, )).replace("> <", "><"), error_code @@ -374,24 +384,13 @@ if __name__ == '__main__': reload_contributors_data(os.path.join(os.getcwd(), "data/contributors.yml")) reload_sitemap_entries(os.path.join(os.getcwd(), "data/sitemap.yml")) - # try: - # os.remove("data/strings/dumps.json") - # except OSError: - # pass - # - # try: - # with open("data/strings/dumps.json", "w") as f: - # f.write(json.dumps(L10N._langs_data, indent=2)) - # except Exception as err: - # print(err) - - # from waitress import serve - # serve(app, host='0.0.0.0', port=5000, threads=64) + #from waitress import serve + #serve(app, host='0.0.0.0', port=5000, threads=64) app.run( host="0.0.0.0", port=5000, debug=True, - #debug=False, + # debug=False, load_dotenv=False ) diff --git a/data/applets/uuid-generator.yml b/data/applets/uuid-generator.yml index 49d0333..f88240c 100644 --- a/data/applets/uuid-generator.yml +++ b/data/applets/uuid-generator.yml @@ -1,8 +1,8 @@ applets: - - id: "uuid-generator" + - id: "web-usb-test" resources: scripts: - - "uuid-generator.mjs" + - "web-usb-test.mjs" stylesheets: - - "uuid-generator.css" + - "web-usb-test.css" diff --git a/data/applets/web-usb-test.yml b/data/applets/web-usb-test.yml new file mode 100644 index 0000000..49d0333 --- /dev/null +++ b/data/applets/web-usb-test.yml @@ -0,0 +1,8 @@ + +applets: + - id: "uuid-generator" + resources: + scripts: + - "uuid-generator.mjs" + stylesheets: + - "uuid-generator.css" diff --git a/data/projects/lscom-cli-dotnet.yml b/data/projects/lscom-cli-dotnet.yml new file mode 100644 index 0000000..0b49bca --- /dev/null +++ b/data/projects/lscom-cli-dotnet.yml @@ -0,0 +1,34 @@ + +projects: + - id: "lscom-cli-dotnet" + metadata: + head: + title_key: "meta.title" + description_key: "meta.description" + opengraph: + title_key: "meta.title" + description_key: "meta.description" + type: null + url: null + image_url: "/resources/NibblePoker/images/content/lscom/lscom-v2-text-01-bkgd-cli.png" + image_type: null + twitter: + title_key: "meta.title" + description_key: "meta.description" + index: + priority: 105 + enable: true + title_key: "meta.title" + preamble_key: "meta.description" + image_url: "/resources/NibblePoker/images/content/lscom/lscom-v2-text-01-bkgd-cli.png" + image_alt_key: "" + general: + icon: "fad fa-terminal" + title_key: "meta.title" + subtitle_key: "article.subtitle" + tags: + - "application" + - "tool" + - "windows" + languages: + - "dotnet" diff --git a/data/projects/web-usb-test.yml b/data/projects/web-usb-test.yml new file mode 100644 index 0000000..08c200d --- /dev/null +++ b/data/projects/web-usb-test.yml @@ -0,0 +1,32 @@ + +projects: + - id: "web-usb-test" + metadata: + head: + title_key: "meta.title" + description_key: "meta.description" + opengraph: + title_key: "meta.title" + description_key: "meta.description" + type: null + url: null + image_url: "/resources/NibblePoker/images/content/web-usb-test/main.png" + image_type: null + twitter: + title_key: "meta.title" + description_key: "meta.description" + index: + priority: 105 + enable: true + title_key: "meta.title" + preamble_key: "meta.description" + image_url: "/resources/NibblePoker/images/content/web-usb-test/main.png" + image_alt_key: "" + general: + icon: "fab fa-docker" + title_key: "meta.title" + subtitle_key: "article.subtitle" + tags: + - "experiments" + languages: + - "javascript" diff --git a/data/strings/en/commons.yml b/data/strings/en/commons.yml index e9183a0..110f919 100644 --- a/data/strings/en/commons.yml +++ b/data/strings/en/commons.yml @@ -20,6 +20,42 @@ user-agent: User-Agent server: Server -cpu.architecture: Architecture de CPU +cpu.architecture: CPU Architecture +cpu.responsive: "CPU Architecture" +cpu.any: "Any architecture" +cpu.x64: "x64" +#cpu.x64: "

x64
AMD64

" +cpu.x86: "x86" +cpu.arm32: "ARM32" +#cpu.arm32: "

ARM32
ARMv7
AArch32

" +cpu.arm64: "ARM64" +#cpu.arm64: "

ARM64
ARMv8
AArch64

" +cpu.risc-v: "RISC-V" + +requirements: Requirements python: Python +java: Java +c99: C99 +dotnet: .NET +purebasic: PureBasic + +version: "Version" +version.current: "Current version" +version.previous.single: "Previous version" +version.previous.multiple: "Previous versions" +version.old.single: "Old version" +version.old.multiple: "Old versions" +source.code: "Source code" + +download.single: "Download" +download.multiple: "Downloads" + +github: "GitHub Repository" +gitea: "Self-hosted Gitea Repository" +nuget: "Nuget Package" + +none.ms: None +none.mp: None +none.fs: None +none.fp: None diff --git a/data/strings/en/lscom-cli-dotnet.yml b/data/strings/en/lscom-cli-dotnet.yml new file mode 100644 index 0000000..00a81eb --- /dev/null +++ b/data/strings/en/lscom-cli-dotnet.yml @@ -0,0 +1,47 @@ +# EN - DotNet-ListComPort + +meta.title: DotNet-ListComPort +meta.description: A simple CLI tool that can list COM ports with their name, friendly + name and device name easily and cleanly. +article.subtitle: View on GitHub +intro.title: Introduction +intro.p1: A simple CLI tool that can list COM ports with their full name easily and + cleanly. +intro.p2: This tool is intended to replace the tedious task of having to use the mode command, and the Device Manager to find a newly + plugged-in device that provides a COM port. +intro.p3: This version of the program is a complete refactoring of my old PB-ListComPort + project that also changes from the proprietary and paid PureBasic language and compiler + to .NET 6.0. +requirements.title: Requirements +requirements.1: Windows +requirements.2: Any CPU architecture +requirements.3: ".NET 6.0" +requirements.4: Optional if using the larger "self-contained" builds. +improvements.title: Improvements over PB-ListComPort +improvements.1: Switched from PureBasic to .NET 6.0. +improvements.2: Improved a lot of the program's logic. +improvements.3: Added the -H/--short-help. +improvements.4: Added support for Windows ARM & ARM64. +improvements.5: Support for running without a console. +usage.title: Usage +formatting.title: Output formatting +requirements.table.title: Requirements +requirements.text.dotnet: .NET + 6.0 +packages.title: Packages +packages.single.title: Single Builds +packages.single.1: Lighter builds that only contain the exe and required licenses.
You + will need to install the .NET + 6.0 Runtime. +packages.self.title: Self-Contained Builds +packages.self.1: Larger builds that contain the exe and the .NET + 6.0 Runtime as well as the required licenses. +packages.msi.title: MSI Installers +packages.msi.1: Windows installers that contain the relevant "Self-Contained" build + with an option to automatically update existing installations and add the program + to the %PATH%.
The install location is %ProgramFiles%\NibblePoker\lscom\ + and cannot be changed. (This will be possible in future releases) +links.title: Links +screenshots.title: Screenshots diff --git a/data/strings/fr/commons.yml b/data/strings/fr/commons.yml index 5c98c95..9fae486 100644 --- a/data/strings/fr/commons.yml +++ b/data/strings/fr/commons.yml @@ -20,6 +20,42 @@ user-agent: User-Agent server: Serveur -cpu.architecture: CPU Architecture +cpu.architecture: Architecture de CPU +cpu.responsive: "Architecture de CPU" +cpu.any: "Indépendante" +cpu.x64: "x64" +#cpu.x64: "

x64
AMD64

" +cpu.x86: "x86" +cpu.arm32: "ARM32" +#cpu.arm32: "

ARM32
ARMv7
AArch32

" +cpu.arm64: "ARM64" +#cpu.arm64: "

ARM64
ARMv8
AArch64

" +cpu.risc-v: "RISC-V" + +requirements: "Dépendances" python: Python +java: Java +c99: C99 +dotnet: .NET +purebasic: PureBasic + +version: "Version" +version.current: "Version actuelle" +version.previous.single: "Version précédente" +version.previous.multiple: "Versions précédentes" +version.old.single: "Ancienne version" +version.old.multiple: "Anciennes versions" +source.code: "Code source" + +download.single: "Téléchargement" +download.multiple: "Téléchargements" + +github: "Dépôt GitHub" +gitea: "Dépôt Gitea auto-hébergé" +nuget: "Packet Nuget" + +none.ms: Aucun +none.mp: Aucuns +none.fs: Aucune +none.fp: Aucunes diff --git a/data/strings/fr/lscom-cli-dotnet.yml b/data/strings/fr/lscom-cli-dotnet.yml new file mode 100644 index 0000000..4d5fbf8 --- /dev/null +++ b/data/strings/fr/lscom-cli-dotnet.yml @@ -0,0 +1,46 @@ +# FR - DotNet-ListComPort + +meta.title: DotNet-ListComPort +meta.description: Un petit utilitaire pour invité de commande qui permet de facilement + lister les noms, noms formatés et chemin des ports COM. +article.subtitle: Voir sur GitHub +intro.title: Introduction +intro.p1: Un petit utilitaire pour invité de commande qui permet de facilement lister + les noms, noms formatés et chemins des ports COM. +intro.p2: Cet outil a pour bût de faciliter cette tâche sans avoir à utiliser la commande + mode ou le Gestionnaire de périphérique. +intro.p3: Cette version du programme a completement été réecrit depuis le projet original + PB-ListComPort en + .NET 6.0 au lieu de PureBasic afin de ne plus utiliser de langage de programmation + propriétaire. +requirements.title: Dépendances +requirements.1: Windows +requirements.2: Toutes architectures de CPU +requirements.3: ".NET 6.0" +requirements.4: Optionnel si vous utilisez les paquets "self-contained". +improvements.title: Améliorations +improvements.1: Changement de PureBasic vers .NET 6.0. +improvements.2: Amélioration de la logique interne du programme. +improvements.3: Ajout de l'option -H/--short-help. +improvements.4: Support pour Windows ARM et ARM64. +improvements.5: Support pour le lancement sans invité de commande. +usage.title: Utilisation +formatting.title: Formatage de sortie +requirements.table.title: Dépendances +requirements.text.dotnet: .NET 6.0 +packages.title: Paquets +packages.single.title: ???  (Single) +packages.single.1: Lighter builds that only contain the exe and required licenses.
You + will need to install the .NET + 6.0 Runtime. +packages.self.title: ???  (Self-Contained) +packages.self.1: Larger builds that contain the exe and the .NET + 6.0 Runtime as well as the required licenses. +packages.msi.title: Installateurs MSI +packages.msi.1: Windows installers that contain the relevant "Self-Contained" build + with an option to automatically update existing installations and add the program + to the %PATH%.
The install location is %ProgramFiles%\NibblePoker\lscom\ + and cannot be changed. (This will be possible in future releases) +links.title: Liens +screenshots.title: Captures d'écran diff --git a/static/resources/NibblePoker/applets/web-usb-test/web-usb-test.css b/static/resources/NibblePoker/applets/web-usb-test/web-usb-test.css new file mode 100644 index 0000000..e69de29 diff --git a/static/resources/NibblePoker/applets/web-usb-test/web-usb-test.mjs b/static/resources/NibblePoker/applets/web-usb-test/web-usb-test.mjs new file mode 100644 index 0000000..f290443 --- /dev/null +++ b/static/resources/NibblePoker/applets/web-usb-test/web-usb-test.mjs @@ -0,0 +1,60 @@ +/** + * Checks if the required JS API for WebUSB is available. + * @returns {boolean} + */ +function isWebUsbAvailable() { + return navigator.usb !== null; +} + +// Tool-centric stuff +{ + /** @type {HTMLElement} */ + const eApiSupportedText = document.querySelector("#web-usb-test-api-supported"); + /** @type {HTMLElement} */ + const eApiNonSupportedText = document.querySelector("#web-usb-test-api-not-supported"); + /** @type {HTMLElement} */ + const eApiSupportUnknownText = document.querySelector("#web-usb-test-api-support-unknown"); + + /** @type {HTMLButtonElement} */ + const eListAllButton = document.querySelector("button#web-usb-test-list-all"); + + /** @type {HTMLButtonElement} */ + const eBa63GetterButton = document.querySelector("button#web-usb-test-ba63-getter"); + + window.onload = function () { + if (!isWebUsbAvailable()) { + eApiSupportUnknownText.hidden = true; + eApiNonSupportedText.hidden = false; + return; + } + eApiSupportUnknownText.hidden = true; + eApiSupportedText.hidden = false; + + eListAllButton.addEventListener("click", function () { + navigator.usb.getDevices().then((devices) => { + console.log(`Total devices: ${devices.length}`); + devices.forEach((device) => { + console.log( + `Product name: ${device.productName}, serial number ${device.serialNumber}`, + ); + console.log(device); + }); + }); + }); + + eBa63GetterButton.addEventListener("click", function () { + const filters = [ + {vendorId: 0x0AA7, productId: 0x0200}, + ]; + navigator.usb + .requestDevice({filters}) + .then((usbDevice) => { + console.log(`Product name: ${usbDevice.productName}`); + }) + .catch((e) => { + console.error(`There is no device. ${e}`); + }); + }); + + } +} diff --git a/static/resources/Standalone/nibblepoker.min.css b/static/resources/Standalone/nibblepoker.min.css new file mode 100644 index 0000000..7503836 --- /dev/null +++ b/static/resources/Standalone/nibblepoker.min.css @@ -0,0 +1 @@ +*,*:before,*:after{box-sizing:border-box}html,body,div,span,object,iframe,figure,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,code,em,img,small,strike,strong,sub,sup,tt,b,u,i,ol,ul,li,fieldset,form,label,table,caption,tbody,tfoot,thead,tr,th,td,main,canvas,embed,footer,header,nav,section,video{margin:0;padding:0;border:0;font-size:100%;font:inherit;vertical-align:baseline;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;text-size-adjust:none}footer,header,nav,section,main{display:block}body{line-height:1}ol,ul{list-style:none}blockquote,q{quotes:none}blockquote:before,blockquote:after,q:before,q:after{content:"";content:none}table{border-collapse:collapse;border-spacing:0}input{-webkit-appearance:none;border-radius:0}html,body{min-width:100vw;min-height:100%;width:100vw !important;height:100% !important;max-width:100vw;max-height:100%}body.layout-generic{display:grid;grid-template-columns:min-content 1fr;grid-template-rows:min-content 1fr min-content;gap:0}body.layout-generic>nav{grid-column:1;grid-row:1/span 2;overflow-x:hidden;overflow-y:auto;z-index:5}body.layout-generic>header{grid-column:2;grid-row:1;display:grid;grid-template-columns:1fr min-content;grid-template-rows:min-content}body.layout-generic>header>h1{grid-column:1}body.layout-generic>header>#lang-selector{grid-column:2}body.layout-generic>main{grid-column:2;grid-row:2;overflow-x:hidden;overflow-y:auto;position:relative}body.layout-generic>footer{grid-column:1/span 2;grid-row:3}@media only screen and (max-width: 768px){body.layout-generic>nav{border-right:1px solid #141417}body.layout-generic>header{grid-column:1/span 2}body.layout-generic>main{grid-column:1/span 2}}body.layout-generic .sidebar{width:15rem;max-width:15rem;min-height:100%}body.layout-generic .sidebar.retracted{width:0;padding-left:0;padding-right:0;overflow:hidden}body.layout-generic .sidebar .sidebar-entry{display:flex;align-items:center;justify-content:left}@media only screen and (max-width: 768px){body.layout-generic .sidebar{width:0;padding-left:0;padding-right:0;overflow:hidden}body.layout-generic .sidebar.retracted{width:15rem;max-width:15rem;min-height:100%;overflow:auto;overflow-x:hidden;padding-left:1rem;padding-right:1rem}body.layout-generic .sidebar .sidebar-entry{display:flex;align-items:center;justify-content:left}}@media only screen and (max-width: 768px){body.layout-generic main{border-left:0 !important;border-radius:0 !important}}@media only screen and (min-width: 768px){body.layout-generic main.expanded{border-left:0;border-radius:0}}body.layout-generic main,body.layout-generic .sidebar{transition:width .4s,padding .4s,border-width .4s,border-radius .4s;transition-timing-function:cubic-bezier(0.25, 0.8, 0.25, 1.1)}body{background-color:#1D1E22;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol"}header,nav,footer{background-color:#1D1E22}main{background-color:#24252B;box-shadow:0 0 6px 0 rgba(0,0,0,.375) inset}*{color:#ebede9}p{line-height:1.2}a,.a{text-decoration:underline;color:#73bed3}a *,.a *{color:#73bed3}a:hover,.a:hover{color:#a4dddb;cursor:pointer}a:hover *,.a:hover *{color:#a4dddb;text-decoration:underline}a.a-bland,.a.a-bland{text-decoration:none}a.a-bland:hover,.a.a-bland:hover{text-decoration:none}a.a-bland:hover *,.a.a-bland:hover *{text-decoration:none}a.a-hidden,.a.a-hidden{text-decoration:none;color:#ebede9}a.a-hidden *,.a.a-hidden *{color:#ebede9}a.a-hidden:hover,.a.a-hidden:hover{text-decoration:underline;color:rgb(253, 255, 251)}a.a-hidden:hover *,.a.a-hidden:hover *{color:rgb(253, 255, 251)}.t-logo-text{font-size:1.775em;line-height:1}.sidebar-entry{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.sidebar-entry i{width:1.9rem !important;min-width:1.9rem !important}.heading-main>h2>i,.heading-main>h3>i,.heading-main>h4>i{margin-right:.4rem;padding-right:.1rem;width:1.75rem !important;text-align:center}hr{border-color:#141417}hr.subtle,hr.hr-subtle{opacity:.2}hr.dashed,hr.hr-dashed{border-style:dashed}hr.dotted,hr.hr-dotted{border-style:dotted}hr.double,hr.hr-double,hr.thick,hr.hr-thick{border-style:double}hr.outset,hr.hr-outset{border-style:outset}hr.cut-here,hr.ht-cut-here{border-width:1px 0 0 0;height:9px}hr.cut-here:after,hr.ht-cut-here:after{content:"✂";display:inline-block;position:relative;color:#141417;font-size:18px;top:-8px;left:40px;font-family:serif}*::-webkit-scrollbar{width:1.25em;height:1.25em;background-color:rgba(0,0,0,0)}*::-webkit-scrollbar-track:vertical{border-left:1px solid #141417}*::-webkit-scrollbar-thumb{background-color:hsla(0,0%,100%,.25);border:.4rem solid rgba(0,0,0,0);background-clip:content-box;border-radius:1em}*::-webkit-scrollbar-thumb:hover{background-color:hsla(0,0%,100%,.4);border-color:rgba(0,0,0,0)}*::-webkit-scrollbar-corner{background-color:rgba(0,0,0,0);border-left:1px solid rgba(0,0,0,.1);border-top:1px solid rgba(0,0,0,.1)}button,input,textarea,select{border:0;outline:none;color:#ebede9;background-color:#2d2f36}button:not(.no-focus):focus,input:not(.no-focus):focus,textarea:not(.no-focus):focus,select:not(.no-focus):focus{box-shadow:0px 0px 3px 0px #3c5e8b}button:disabled,input:disabled,textarea:disabled,select:disabled{cursor:not-allowed}label{cursor:pointer;user-select:none}input[type=checkbox]{width:1.65em;height:1.65em;background-color:#a53030;cursor:pointer;position:relative;vertical-align:middle}input[type=checkbox]:checked{background-color:#468232}input[type=checkbox]+label{position:relative;display:inline-block;vertical-align:middle}button{cursor:pointer;background-color:#2d2f36}button:hover{background-color:#373841}button.btn-primary{background-color:rgb(45.4, 75, 112.4)}button.btn-primary:hover{background-color:rgb(49.3375, 82.96875, 121.025)}button.btn-success{background-color:#25562e}button.btn-success:hover{background-color:rgb(53.5, 108, 48)}button.btn-error{background-color:#752438}button.btn-error:hover{background-color:#a53030}button.btn-warning{background-color:#884b2b}button.btn-warning:hover{background-color:#be772b}button+button,.button-link+.button-link>button{margin-left:.75rem}#lang-selector{position:relative;white-space:nowrap}#lang-selector>summary{cursor:pointer;list-style:none;user-select:none}#lang-selector>div{position:absolute;z-index:10;top:2rem;right:0;min-width:100%}.ta-resize{resize:both}.ta-resize-h{resize:horizontal}.ta-resize-v{resize:vertical}.ta-resize-none{resize:none}.table-stylish{border-collapse:separate;overflow:hidden}.table-stylish th{font-weight:bold}.table-stylish tr{background-color:rgb(29.875, 30.875, 35.125)}.table-stylish tr:nth-child(2n),.table-stylish th{background-color:rgb(32.5, 33.5, 38.5)}.table-stylish td:not(:last-of-type),.table-stylish th:not(:last-of-type){border-right:1.5px solid #141417CF}.table-stylish tr:not(:last-of-type) td,.table-stylish th{border-bottom:1.5px solid #141417CF}code,.code,kbd{font-family:Consolas,"Courier New",monospace}.code,kbd{background-color:rgb(30.75, 31.75, 36.25);border:1px solid;border-radius:calc(5px*.75);padding-left:calc(5px*.625);padding-right:calc(5px*.625)}.code-line{background:rgba(0,0,0,0) !important}code,.code,.code-line{line-height:1.35}.code:not(code){padding:calc(5px*.625)}video{width:100% !important;height:auto !important}.border,code,.code,kbd{border-color:#141417}code,.code,kbd{border-color:#141417CF}main .border{border-color:#141417}.bkgd-dark{background:#1D1E22}.bkgd-grey,.bkgd-gray{background:rgb(32.5,33.5,38.5)}.bkgd-light{background:#24252b}.bkgd-grid10,.bkgd-surround,header,nav,footer{background:#1D1E22 url(".//3px-tile-0.1.png") repeat scroll center center}.bkgd-grid20{background:#1D1E22 url(".//3px-tile-0.2.png") repeat scroll center center}.bkgd-grid30{background:#1D1E22 url(".//3px-tile-0.3.png") repeat scroll center center}.bkgd-grid40,.bkgd-grid{background:#1D1E22 url("data:image/png;base64,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") repeat scroll center center}.bkgd-transparent{background:rgba(0,0,0,0)}.bkgd-blue{background-color:rgb(45.4, 75, 112.4)}.bkgd-blue-light{background-color:rgb(49.3375, 82.96875, 121.025)}.bkgd-green{background-color:#25562e}.bkgd-green-light{background-color:rgb(53.5, 108, 48)}.bkgd-red{background-color:#752438}.bkgd-red-light{background-color:#a53030}.bkgd-orange{background-color:#884b2b}.bkgd-orange-light{background-color:#be772b}@media only screen and (max-width: 768px){main{padding:calc(1rem*.5) !important}.content-search-entry>p{width:100% !important}.mobile-hide{display:none}}.img-text{height:1em;vertical-align:top}.btn-img{display:flex;height:1em}.img-showcase{width:96px;height:96px}.img-profile{width:7.5rem;height:7.5rem}.img-contributor,.img-contributor>img{width:6rem;height:6rem;border-radius:50%;transition:width .175s ease-in-out,height .175s ease-in-out,opacity .175s ease-in-out}.img-contributor:hover,.img-contributor:hover>img,.img-contributor>img:hover,.img-contributor>img:hover>img{width:7.5rem;height:7.5rem}.img-contributor.kitty,.img-contributor>img.kitty{cursor:grab}.img-contributor{display:inline-block;box-shadow:0 0 6px 0 rgba(0,0,0,.75)}.img-contributor>img{position:absolute}.img-contributor>img:last-child{box-shadow:none;position:relative;opacity:0}.img-contributor:hover img{opacity:0}.img-contributor:hover>img:last-child{opacity:1}#logo-sidebar,.logo-sidebar{filter:brightness(110%) grayscale(100%);width:13rem;height:auto}.logo-sidebar-v2{filter:drop-shadow(0 0 0.15rem rgba(0, 0, 0, 0.4980392157)) saturate(75%);width:13rem;height:auto;transition-property:-moz-filter,-ms-filter,-o-filter,-webkit-filter,filter;transition-duration:.4s;transition-timing-function:cubic-bezier(0.25, 0.8, 0.25, 1.1)}.logo-sidebar-v2:hover{filter:drop-shadow(0 0 0.15rem rgba(0, 0, 0, 0.4980392157)) saturate(100%)}#logo-footer{filter:brightness(110%) grayscale(100%);opacity:40%;height:1.8em;object-fit:contain}#error-page-skit{max-width:40%;height:auto;position:absolute;bottom:1.5em;right:1.5em;filter:drop-shadow(0 0 0.25rem rgba(0, 0, 0, 0.4980392157));opacity:.4;user-drag:none;user-select:none}wedge,.wedge{position:absolute;color:#ebede9;background-color:#2d2f36;user-select:none;cursor:pointer}wedge:hover:not(.no-hover),.wedge:hover:not(.no-hover){background-color:#373841}wedge.wedge-shadow,.wedge.wedge-shadow{box-shadow:0px 0px 5px 0px rgba(0,0,0,.5)}wedge.primary,.wedge.primary{background-color:rgb(45.4, 75, 112.4)}wedge.primary:hover:not(.no-hover),.wedge.primary:hover:not(.no-hover){background-color:rgb(49.3375, 82.96875, 121.025)}wedge.success,.wedge.success{background-color:#25562e}wedge.success:hover:not(.no-hover),.wedge.success:hover:not(.no-hover){background-color:rgb(53.5, 108, 48)}wedge.error,.wedge.error{background-color:#752438}wedge.error:hover:not(.no-hover),.wedge.error:hover:not(.no-hover){background-color:#a53030}wedge.warning,.wedge.warning{background-color:#884b2b}wedge.warning:hover:not(.no-hover),.wedge.warning:hover:not(.no-hover){background-color:#be772b}wedge.wedge-tr,.wedge.wedge-tr{top:0;right:0}wedge.wedge-br,.wedge.wedge-br{bottom:0;right:0}wedge.wedge-tl,.wedge.wedge-tl{top:0;left:0}wedge.wedge-bl,.wedge.wedge-bl{bottom:0;left:0}.content-search-entry{min-height:128px}.content-search-image{width:128px;height:128px;float:left;filter:drop-shadow(0 0 0.2rem rgba(0, 0, 0, 0.8117647059))}.content-search-entry>p{width:90%}.content-error-text{max-width:700px}.content-card-image{width:calc(128px*.75);height:calc(128px*.75);float:left}.app-card-icons>img{height:1.25rem}.modal{position:absolute;top:0;left:0;z-index:99;background-color:rgba(0,0,0,.8117647059);width:100vw;height:100vh}.modal>#modal-content-cross{cursor:pointer}.modal>#modal-content-inner{position:absolute;top:0;left:0;right:0;height:100vh;width:75vw;margin-left:auto;margin-right:auto;display:grid;place-items:center}.modal-inner-image{max-width:100%;max-height:100%}.b,.border{border:1px solid #141417}.b-0{border:0}.bt{border-top:1px solid #141417}.bt-0{border-top:0}.bb{border-bottom:1px solid #141417}.bb-0{border-bottom:0}.bl{border-left:1px solid #141417}.bl-0{border-left:0}.br{border-right:1px solid #141417}.br-0{border-right:0}.bx{border-left:1px solid #141417;border-right:1px solid #141417}.bx-0{border-left:0;border-right:0}.by{border-top:1px solid #141417;border-bottom:1px solid #141417}.by-0{border-top:0;border-bottom:0}.fl,.f-left{float:left}.fr,.f-right{float:right}.fn,.f-none{float:none}.w-full{width:100%}.wm-full{max-width:100%}.h-full{height:100%}.hm-full{max-height:100%}.w-half{width:50%}.wm-half{max-width:50%}.h-half{height:50%}.hm-half{max-height:50%}.d-flex{display:flex}.d-inline-flex{display:inline-flex}.flex-justify-start{justify-content:flex-start}.flex-justify-end{justify-content:flex-end}.flex-justify-center{justify-content:center}.flex-justify-between{justify-content:space-between}.flex-justify-around{justify-content:space-around}.flex-justify-evenly{justify-content:space-evenly}.flex-justify-left{justify-content:left}.flex-justify-right{justify-content:right}.flex-align-center{align-items:center}.flex-align-start{align-items:flex-start}.flex-direction-row,.flex-row{flex-direction:row}.flex-direction-row-reverse,.flex-row-reverse{flex-direction:row-reverse}.flex-direction-column,.flex-column{flex-direction:column}.flex-direction-column-reverse,.flex-column-reverse{flex-direction:column-reverse}.flex-fill{-ms-flex:1 1 auto !important;flex:1 1 auto !important}.grid,.d-grid{display:grid}.grid-col-1{grid-template-columns:repeat(1, 1fr)}.grid-col-2{grid-template-columns:repeat(2, 1fr)}.grid-col-3{grid-template-columns:repeat(3, 1fr)}.grid-col-4{grid-template-columns:repeat(4, 1fr)}.grid-col-6{grid-template-columns:repeat(6, 1fr)}.grid-col-8{grid-template-columns:repeat(8, 1fr)}.grid-gap-xs{grid-gap:calc(1rem * 0.5)}.grid-gap-s{grid-gap:calc(1rem * 0.75)}.grid-gap-m{grid-gap:1rem}.grid-gap-l{grid-gap:calc(1rem * 1.5)}.grid-gap-xl{grid-gap:calc(1rem * 2)}@media only screen and (max-width: 992px){.grid-col-medium-1{grid-template-columns:repeat(1, 1fr)}.grid-col-medium-2{grid-template-columns:repeat(2, 1fr)}.grid-col-medium-3{grid-template-columns:repeat(3, 1fr)}}@media only screen and (max-width: 768px){.grid-col-mobile-1{grid-template-columns:repeat(1, 1fr)}.grid-col-mobile-2{grid-template-columns:repeat(2, 1fr)}.grid-col-mobile-3{grid-template-columns:repeat(3, 1fr)}}h1,h2,h3,h4,h5,h6{font-weight:600}.t-size-6{font-size:0.6rem !important}.t-size-8{font-size:0.8rem !important}.t-size-10{font-size:1rem !important}.t-size-12{font-size:1.2rem !important}.t-size-14{font-size:1.4rem !important}.t-size-16{font-size:1.6rem !important}.t-size-18{font-size:1.8rem !important}.t-size-20{font-size:2rem !important}.t-size-22{font-size:2.2rem !important}.t-size-24{font-size:2.4rem !important}.t-size-26{font-size:2.6rem !important}.t-size-28{font-size:2.8rem !important}.t-size-30{font-size:3rem !important}.t-size-32{font-size:3.2rem !important}.t-size-34{font-size:3.4rem !important}.t-size-36{font-size:3.6rem !important}.t-size-38{font-size:3.8rem !important}.t-size-40{font-size:4rem !important}.t-size-11{font-size:1.1rem !important}.t-size-13{font-size:1.3rem !important}.t-size-15{font-size:1.5rem !important}.t-size-17{font-size:1.7rem !important}.t-size-35{font-size:3.5rem !important}.t-size-40{font-size:4rem !important}.t-w-normal,.t-normal{font-weight:normal !important}b,.t-w-bold,.t-bold{font-weight:bold !important}.t-w-bolder,.t-bolder{font-weight:bolder !important}.t-w-lighter,.t-lighter{font-weight:lighter !important}.t-w-100{font-weight:100 !important}.t-w-200{font-weight:200 !important}.t-w-300{font-weight:300 !important}.t-w-400{font-weight:400 !important}.t-w-500{font-weight:500 !important}.t-w-600{font-weight:600 !important}.t-w-700{font-weight:700 !important}.t-w-800{font-weight:800 !important}.t-w-900{font-weight:900 !important}i,.t-italic{font-style:italic}.t-oblique{font-style:oblique}u,.t-underline{text-decoration:underline}s,.t-strikethrough{text-decoration:line-through}.t-overline{text-decoration:overline}.t-justify{text-align:justify}.t-center{text-align:center}.t-left{text-align:left}.t-right{text-align:right}.t-start{text-align:start}.t-end{text-align:end}.t-ucase{text-transform:uppercase}.t-lcase{text-transform:lowercase}.t-capitalize{text-transform:capitalize}.t-nowrap{white-space:nowrap;text-wrap:nowrap}.t-half-muted{opacity:65%}.t-muted{opacity:50%}.t-super-muted{opacity:45%}.t-noselect{user-select:none}.d-none{display:none}.d-block{display:block}.d-inline-block{display:inline-block}.o-hidden{overflow:hidden}.p-relative{position:relative}.o-auto{overflow:auto !important}.ox-auto{overflow-x:auto !important}.oy-auto{overflow-y:auto !important}.o-scroll{overflow:scroll !important}.ox-scroll{overflow-x:scroll !important}.oy-scroll{overflow-y:scroll !important}.o-clip{overflow:clip !important}.ox-clip{overflow-x:clip !important}.oy-clip{overflow-y:clip !important}.o-hidden{overflow:hidden !important}.ox-hidden{overflow-x:hidden !important}.oy-hidden{overflow-y:hidden !important}ul.l-bullets,ul.list-bullets{list-style-type:disc}ol.l-bullets,ol.list-bullets{list-style-type:decimal}ul.l-bullet-inside,ul.list-bullet-inside,ol.l-bullet-inside,ol.list-bullet-inside{list-style-position:inside}ul.l-bullet-outside,ul.list-bullet-outside,ol.l-bullet-outside,ol.list-bullet-outside{list-style-position:outside}ul.l-bullet-manual,ul.list-bullet-manual,ol.l-bullet-manual,ol.list-bullet-manual{list-style-position:outside;padding-left:1.5rem}ul.list-bullet-none,ul.l-bullet-none,ol.list-bullet-none,ol.l-bullet-none,li.list-bullet-none,li.l-bullet-none{list-style-type:none}ul.list-bullet-disc,ul.l-bullet-disc,ol.list-bullet-disc,ol.l-bullet-disc,li.list-bullet-disc,li.l-bullet-disc{list-style-type:disc}ul.list-bullet-square,ul.l-bullet-square,ol.list-bullet-square,ol.l-bullet-square,li.list-bullet-square,li.l-bullet-square{list-style-type:square}ul.list-bullet-decimal,ul.l-bullet-decimal,ol.list-bullet-decimal,ol.l-bullet-decimal,li.list-bullet-decimal,li.l-bullet-decimal{list-style-type:decimal}ul.list-bullet-decimal-leading,ul.l-bullet-decimal-leading,ol.list-bullet-decimal-leading,ol.l-bullet-decimal-leading,li.list-bullet-decimal-leading,li.l-bullet-decimal-leading{list-style-type:decimal-leading-zero}ul.list-bullet-georgian,ul.l-bullet-georgian,ol.list-bullet-georgian,ol.l-bullet-georgian,li.list-bullet-georgian,li.l-bullet-georgian{list-style-type:georgian}ul.list-bullet-chinese,ul.l-bullet-chinese,ol.list-bullet-chinese,ol.l-bullet-chinese,li.list-bullet-chinese,li.l-bullet-chinese{list-style-type:trad-chinese-informal}ul.list-bullet-kannada,ul.l-bullet-kannada,ol.list-bullet-kannada,ol.l-bullet-kannada,li.list-bullet-kannada,li.l-bullet-kannada{list-style-type:kannada}tr,td{vertical-align:top}.table-v-center tr,.table-v-center td{vertical-align:middle}.table-v-bottom tr,.table-v-bottom td{vertical-align:bottom}.table-p-xxs td,.table-p-xxs th{padding:calc(1rem * 0.25)}.table-p-xs td,.table-p-xs th{padding:calc(1rem * 0.5)}.table-p-s td,.table-p-s th{padding:calc(1rem * 0.75)}.table-p-m td,.table-p-m th{padding:1rem}.table-p-l td,.table-p-l th{padding:calc(1rem * 1.5)}.table-p-xl td,.table-p-xl th{padding:calc(1rem * 2)}.r-xs{border-radius:calc(5px * 0.5)}.r-s{border-radius:calc(5px * 0.75)}.r-m{border-radius:5px}.r-l{border-radius:calc(5px * 1.5)}.r-xl{border-radius:calc(5px * 2)}.r-0{border-radius:0}.r-r{border-radius:50%}.rt-xs{border-top-left-radius:calc(5px * 0.5);border-top-right-radius:calc(5px * 0.5)}.rb-xs{border-bottom-left-radius:calc(5px * 0.5);border-bottom-right-radius:calc(5px * 0.5)}.rl-xs{border-top-left-radius:calc(5px * 0.5);border-bottom-left-radius:calc(5px * 0.5)}.rr-xs{border-top-right-radius:calc(5px * 0.5);border-bottom-right-radius:calc(5px * 0.5)}.rt-s{border-top-left-radius:calc(5px * 0.75);border-top-right-radius:calc(5px * 0.75)}.rb-s{border-bottom-left-radius:calc(5px * 0.75);border-bottom-right-radius:calc(5px * 0.75)}.rl-s{border-top-left-radius:calc(5px * 0.75);border-bottom-left-radius:calc(5px * 0.75)}.rr-s{border-top-right-radius:calc(5px * 0.75);border-bottom-right-radius:calc(5px * 0.75)}.rt-m{border-top-left-radius:5px;border-top-right-radius:5px}.rb-m{border-bottom-left-radius:5px;border-bottom-right-radius:5px}.rl-m{border-top-left-radius:5px;border-bottom-left-radius:5px}.rr-m{border-top-right-radius:5px;border-bottom-right-radius:5px}.rt-l{border-top-left-radius:calc(5px * 1.5);border-top-right-radius:calc(5px * 1.5)}.rb-l{border-bottom-left-radius:calc(5px * 1.5);border-bottom-right-radius:calc(5px * 1.5)}.rl-l{border-top-left-radius:calc(5px * 1.5);border-bottom-left-radius:calc(5px * 1.5)}.rr-l{border-top-right-radius:calc(5px * 1.5);border-bottom-right-radius:calc(5px * 1.5)}.rt-xl{border-top-left-radius:calc(5px * 2);border-top-right-radius:calc(5px * 2)}.rb-xl{border-bottom-left-radius:calc(5px * 2);border-bottom-right-radius:calc(5px * 2)}.rl-xl{border-top-left-radius:calc(5px * 2);border-bottom-left-radius:calc(5px * 2)}.rr-xl{border-top-right-radius:calc(5px * 2);border-bottom-right-radius:calc(5px * 2)}.rt-0{border-top-left-radius:0;border-top-right-radius:0}.rb-0{border-bottom-left-radius:0;border-bottom-right-radius:0}.rl-0{border-top-left-radius:0;border-bottom-left-radius:0}.rr-0{border-top-right-radius:0;border-bottom-right-radius:0}.rt-r{border-top-left-radius:50%;border-top-right-radius:50%}.rb-r{border-bottom-left-radius:50%;border-bottom-right-radius:50%}.rl-r{border-top-left-radius:50%;border-bottom-left-radius:50%}.rr-r{border-top-right-radius:50%;border-bottom-right-radius:50%}.rtl-xs{border-top-left-radius:calc(5px * 0.5)}.rtr-xs{border-top-right-radius:calc(5px * 0.5)}.rbl-xs{border-bottom-left-radius:calc(5px * 0.5)}.rbr-xs{border-bottom-right-radius:calc(5px * 0.5)}.rtl-s{border-top-left-radius:calc(5px * 0.75)}.rtr-s{border-top-right-radius:calc(5px * 0.75)}.rbl-s{border-bottom-left-radius:calc(5px * 0.75)}.rbr-s{border-bottom-right-radius:calc(5px * 0.75)}.rtl-m{border-top-left-radius:5px}.rtr-m{border-top-right-radius:5px}.rbl-m{border-bottom-left-radius:5px}.rbr-m{border-bottom-right-radius:5px}.rtl-l{border-top-left-radius:calc(5px * 1.5)}.rtr-l{border-top-right-radius:calc(5px * 1.5)}.rbl-l{border-bottom-left-radius:calc(5px * 1.5)}.rbr-l{border-bottom-right-radius:calc(5px * 1.5)}.rtl-xl{border-top-left-radius:calc(5px * 2)}.rtr-xl{border-top-right-radius:calc(5px * 2)}.rbl-xl{border-bottom-left-radius:calc(5px * 2)}.rbr-xl{border-bottom-right-radius:calc(5px * 2)}.rtl-0{border-top-left-radius:0}.rtr-0{border-top-right-radius:0}.rbl-0{border-bottom-left-radius:0}.rbr-0{border-bottom-right-radius:0}.rtl-r{border-top-left-radius:50%}.rtr-r{border-top-right-radius:50%}.rbl-r{border-bottom-left-radius:50%}.rbr-r{border-bottom-right-radius:50%}.m-xxs{margin:calc(1rem * 0.25)}.m-xs{margin:calc(1rem * 0.5)}.m-s{margin:calc(1rem * 0.75)}.m-m{margin:1rem}.m-l{margin:calc(1rem * 1.5)}.m-xl{margin:calc(1rem * 2)}.p-xxs{padding:calc(1rem * 0.25)}.p-xs{padding:calc(1rem * 0.5)}.p-s{padding:calc(1rem * 0.75)}.p-m{padding:1rem}.p-l{padding:calc(1rem * 1.5)}.p-xl{padding:calc(1rem * 2)}.m-0{margin:0}.p-0{padding:0}.m-auto{margin:auto}.p-auto{padding:auto}.p-xxxs{padding:calc(1rem*.125)}.p-mxs{padding:calc(1rem*.375)}.p-ms{padding:calc(1rem*.625)}.mx-xxs{margin-left:calc(1rem * 0.25);margin-right:calc(1rem * 0.25)}.my-xxs{margin-top:calc(1rem * 0.25);margin-bottom:calc(1rem * 0.25)}.mx-xs{margin-left:calc(1rem * 0.5);margin-right:calc(1rem * 0.5)}.my-xs{margin-top:calc(1rem * 0.5);margin-bottom:calc(1rem * 0.5)}.mx-s{margin-left:calc(1rem * 0.75);margin-right:calc(1rem * 0.75)}.my-s{margin-top:calc(1rem * 0.75);margin-bottom:calc(1rem * 0.75)}.mx-m{margin-left:1rem;margin-right:1rem}.my-m{margin-top:1rem;margin-bottom:1rem}.mx-l{margin-left:calc(1rem * 1.5);margin-right:calc(1rem * 1.5)}.my-l{margin-top:calc(1rem * 1.5);margin-bottom:calc(1rem * 1.5)}.mx-xl{margin-left:calc(1rem * 2);margin-right:calc(1rem * 2)}.my-xl{margin-top:calc(1rem * 2);margin-bottom:calc(1rem * 2)}.px-xxs{padding-left:calc(1rem * 0.25);padding-right:calc(1rem * 0.25)}.py-xxs{padding-top:calc(1rem * 0.25);padding-bottom:calc(1rem * 0.25)}.px-xs{padding-left:calc(1rem * 0.5);padding-right:calc(1rem * 0.5)}.py-xs{padding-top:calc(1rem * 0.5);padding-bottom:calc(1rem * 0.5)}.px-s{padding-left:calc(1rem * 0.75);padding-right:calc(1rem * 0.75)}.py-s{padding-top:calc(1rem * 0.75);padding-bottom:calc(1rem * 0.75)}.px-m{padding-left:1rem;padding-right:1rem}.py-m{padding-top:1rem;padding-bottom:1rem}.px-l{padding-left:calc(1rem * 1.5);padding-right:calc(1rem * 1.5)}.py-l{padding-top:calc(1rem * 1.5);padding-bottom:calc(1rem * 1.5)}.px-xl{padding-left:calc(1rem * 2);padding-right:calc(1rem * 2)}.py-xl{padding-top:calc(1rem * 2);padding-bottom:calc(1rem * 2)}.mx-0{margin-left:0;margin-right:0}.my-0{margin-top:0;margin-bottom:0}.px-0{padding-left:0;padding-right:0}.py-0{padding-top:0;padding-bottom:0}.mx-auto{margin-left:auto;margin-right:auto}.my-auto{margin-top:auto;margin-bottom:auto}.px-auto{padding-left:auto;padding-right:auto}.py-auto{padding-top:auto;padding-bottom:auto}.mt-xxs{margin-top:calc(1rem * 0.25)}.mb-xxs{margin-bottom:calc(1rem * 0.25)}.ml-xxs{margin-left:calc(1rem * 0.25)}.mr-xxs{margin-right:calc(1rem * 0.25)}.mt-xs{margin-top:calc(1rem * 0.5)}.mb-xs{margin-bottom:calc(1rem * 0.5)}.ml-xs{margin-left:calc(1rem * 0.5)}.mr-xs{margin-right:calc(1rem * 0.5)}.mt-s{margin-top:calc(1rem * 0.75)}.mb-s{margin-bottom:calc(1rem * 0.75)}.ml-s{margin-left:calc(1rem * 0.75)}.mr-s{margin-right:calc(1rem * 0.75)}.mt-m{margin-top:1rem}.mb-m{margin-bottom:1rem}.ml-m{margin-left:1rem}.mr-m{margin-right:1rem}.mt-l{margin-top:calc(1rem * 1.5)}.mb-l{margin-bottom:calc(1rem * 1.5)}.ml-l{margin-left:calc(1rem * 1.5)}.mr-l{margin-right:calc(1rem * 1.5)}.mt-xl{margin-top:calc(1rem * 2)}.mb-xl{margin-bottom:calc(1rem * 2)}.ml-xl{margin-left:calc(1rem * 2)}.mr-xl{margin-right:calc(1rem * 2)}.pt-xxs{padding-top:calc(1rem * 0.25)}.pb-xxs{padding-bottom:calc(1rem * 0.25)}.pl-xxs{padding-left:calc(1rem * 0.25)}.pr-xxs{padding-right:calc(1rem * 0.25)}.pt-xs{padding-top:calc(1rem * 0.5)}.pb-xs{padding-bottom:calc(1rem * 0.5)}.pl-xs{padding-left:calc(1rem * 0.5)}.pr-xs{padding-right:calc(1rem * 0.5)}.pt-s{padding-top:calc(1rem * 0.75)}.pb-s{padding-bottom:calc(1rem * 0.75)}.pl-s{padding-left:calc(1rem * 0.75)}.pr-s{padding-right:calc(1rem * 0.75)}.pt-m{padding-top:1rem}.pb-m{padding-bottom:1rem}.pl-m{padding-left:1rem}.pr-m{padding-right:1rem}.pt-l{padding-top:calc(1rem * 1.5)}.pb-l{padding-bottom:calc(1rem * 1.5)}.pl-l{padding-left:calc(1rem * 1.5)}.pr-l{padding-right:calc(1rem * 1.5)}.pt-xl{padding-top:calc(1rem * 2)}.pb-xl{padding-bottom:calc(1rem * 2)}.pl-xl{padding-left:calc(1rem * 2)}.pr-xl{padding-right:calc(1rem * 2)}.mt-0{margin-top:0}.mb-0{margin-bottom:0}.ml-0{margin-left:0}.mr-0{margin-right:0}.pt-0{padding-top:0}.pb-0{padding-bottom:0}.pl-0{padding-left:0}.pr-0{padding-right:0}.mt-auto{margin-top:auto}.mb-auto{margin-bottom:auto}.ml-auto{margin-left:auto}.mr-auto{margin-right:auto}.pt-auto{padding-top:auto}.pb-auto{padding-bottom:auto}.pl-auto{padding-left:auto}.pr-auto{padding-right:auto}.splide li{transition:all .2s ease-in-out}.splide li:hover{transform:scale(1.05)}.splide,.splide *{max-height:400px} diff --git a/templates/applets/web-usb-test.jinja b/templates/applets/web-usb-test.jinja new file mode 100644 index 0000000..b8d5d37 --- /dev/null +++ b/templates/applets/web-usb-test.jinja @@ -0,0 +1,10 @@ + +

Test 123

+ + + +

Unknown / Blocked

+ + + + diff --git a/templates/base_standalone.jinja b/templates/base_standalone.jinja index f055e3e..8480b67 100644 --- a/templates/base_standalone.jinja +++ b/templates/base_standalone.jinja @@ -7,12 +7,17 @@ + - - - - + + + + + {{ get_standalone_common_headers() }} + + + {% block extra_stylesheets %}{% endblock %} +{% block extra_scripts %}{% endblock %} + \ No newline at end of file diff --git a/templates/pages/tools_page.jinja b/templates/pages/tools_page.jinja index 281ea34..22a26f7 100644 --- a/templates/pages/tools_page.jinja +++ b/templates/pages/tools_page.jinja @@ -1,10 +1,14 @@ -{% extends "base_www.jinja" %} +{% if is_standalone %} + {% extends "base_standalone.jinja" %} +{% else %} + {% extends "base_www.jinja" %} +{% endif %} {% block head_title %}{{ l10n(tool_data.metadata.head.title_key, tool_id, user_lang) }}{% endblock %} {% block head_description %}{{ l10n(tool_data.metadata.head.description_key, tool_id, user_lang) }}{% endblock %} {% block extra_stylesheets %} - {{ render_applet_head(applet_data) }} + {{ render_applet_head(applet_data, is_standalone) }} {% endblock %} {% block header_title %} @@ -24,5 +28,5 @@ {% endblock %} {% block extra_scripts %} - {{ render_applet_scripts(applet_data) }} + {{ render_applet_scripts(applet_data, is_standalone) }} {% endblock %} diff --git a/templates/projects/circuitpython-custom-fs.jinja b/templates/projects/.circuitpython-custom-fs.jinja similarity index 100% rename from templates/projects/circuitpython-custom-fs.jinja rename to templates/projects/.circuitpython-custom-fs.jinja diff --git a/templates/projects/circuitpython-ebyte-e32.jinja b/templates/projects/.circuitpython-ebyte-e32.jinja similarity index 100% rename from templates/projects/circuitpython-ebyte-e32.jinja rename to templates/projects/.circuitpython-ebyte-e32.jinja diff --git a/templates/projects/lscom-cli-dotnet.jinja b/templates/projects/lscom-cli-dotnet.jinja new file mode 100644 index 0000000..818fb49 --- /dev/null +++ b/templates/projects/lscom-cli-dotnet.jinja @@ -0,0 +1,169 @@ +{% extends "projects/_project.jinja" %} + +{% block project_content %} + {{ render_h2(l10n("intro.title", project_id, user_lang)) }} + {{ render_paragraph(l10n("intro.p1", project_id, user_lang)) }} + {{ render_paragraph(l10n("intro.p2", project_id, user_lang)) }} + {{ render_paragraph(l10n("intro.p3", project_id, user_lang)) }} + + {{ render_h2(l10n("requirements.title", project_id, user_lang)) }} + {{ render_list_ul([ + l10n("requirements.1", project_id, user_lang), + [ + l10n("requirements.2", project_id, user_lang), + ], + l10n("requirements.3", project_id, user_lang), + [ + l10n("requirements.4", project_id, user_lang), + ], + ]) }} + + {{ render_h2(l10n("improvements.title", project_id, user_lang)) }} + {{ render_list_ul([ + l10n("improvements.1", project_id, user_lang), + l10n("improvements.2", project_id, user_lang), + l10n("improvements.3", project_id, user_lang), + l10n("improvements.4", project_id, user_lang), + l10n("improvements.5", project_id, user_lang), + ]) }} + + {{ render_h2(l10n("screenshots.title", project_id, user_lang)) }} + {{ render_splide([ + '', + '', + '', + ]) }} + + {{ render_h2(l10n("usage.title", project_id, user_lang)) }} + {{ + render_code_block([ + "lscom.exe [-a|--show-all] [-d|--show-device] [-D |--divider ] [-f|--show-friendly]", + " [-h|--help] [-H|--short-help] [-n|--show-name-raw] [-P|--no-pretty] [-s|--sort]", + " [-S|--sort-reverse] [-t|--tab-padding] [-v|--version] [-V|--version-only]", + "", + "Launch arguments:", + " -a, --show-all Display the complete port's name (Equal to '-dfn')", + " -d, --show-device Displays the port's device name", + " -D , --divider Uses the given string or char as a separator (Can be empty string !)", + " -f, --show-friendly Displays the port's friendly name", + " -h, --help Display this help text", + " -H, --short-help Display the short help text", + " -n, --show-name-raw Displays the port's raw name (See remarks section)", + " -P, --no-pretty Disables the pretty printing format (Equal to -D \" \")", + " -s, --sort Sorts the port based on their raw names in an ascending order", + " -S, --sort-reverse Sorts the port based on their raw names in a descending order", + " -t, --tab-padding Use tabs for padding between the types of names (Overrides '-D')", + " -v, --version Shows the utility's version number and other info", + " -V, --version-only Shows the utility's version number only (Overrides '-v')" + ], None) + }} + + {{ render_h2(l10n("formatting.title", project_id, user_lang)) }} + {{ + render_code_block([ + " *┬> No launch arguments:", + " └──> ${Raw name} => COM1", + " *┬> '-d' or '-f'", + " ├──> ${Device name} => \\Device\\Serial1", + " └──> ${Friendly name} => Communications Port", + " *┬> '-d' and '-f'", + " └──> ${Friendly name} [${Device name}] => Communications Port [\\Device\\Serial1]", + " *┬> '-n' and '-d'", + " └──> ${Raw name} [$DeviceName] => COM1 [\\Device\\Serial1]", + " *┬> '-n' and '-f'", + " └──> ${Raw name} - ${Friendly name} => COM1 - Communications Port", + " *┬> '-ndf' or '-a'", + " └──> ${Raw name} - ${Friendly name} [${Device name}] => COM1 - Communications Port [\\Device\\Serial1]", + " *┬> '-ndfp' or '-ap'", + " └──> ${Raw name} ${Friendly name} ${Device name} => COM1 Communications Port \\Device\\Serial1", + " *┬> '-ndfD \";\"' or '-aD \";\"'", + " └──> ${Raw name};${Friendly name};${Device name} => COM1;Communications Port;\\Device\\Serial1" + ], None) + }} + + {{ render_h2(l10n("packages.title", project_id, user_lang)) }} + {{ render_paragraph(l10n("packages.single.title", project_id, user_lang)) }} + {{ render_paragraph(l10n("packages.single.1", project_id, user_lang)) }} + {{ render_paragraph(l10n("packages.self.title", project_id, user_lang)) }} + {{ render_paragraph(l10n("packages.self.1", project_id, user_lang)) }} + {{ render_paragraph(l10n("packages.msi.title", project_id, user_lang)) }} + {{ render_paragraph(l10n("packages.msi.1", project_id, user_lang)) }} + + {{ render_h2(l10n("version.current", "commons", user_lang)) }} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
{{ l10n("cpu.architecture", "commons", user_lang) }}{{ l10n("requirements", "commons", user_lang) }}{{ l10n("download.multiple", "commons", user_lang) }}
{{ l10n("cpu.any", "commons", user_lang) }}{{ l10n("requirements.text.dotnet", project_id, user_lang) }}...
{{ l10n("cpu.x64", "commons", user_lang) }}{{ l10n("requirements.text.dotnet", project_id, user_lang) }}...
{{ l10n("none.fs", "commons", user_lang) }}...
{{ l10n("cpu.x86", "commons", user_lang) }}{{ l10n("requirements.text.dotnet", project_id, user_lang) }}...
{{ l10n("none.fs", "commons", user_lang) }}...
{{ l10n("cpu.arm64", "commons", user_lang) }}{{ l10n("requirements.text.dotnet", project_id, user_lang) }}...
{{ l10n("none.fs", "commons", user_lang) }}...
{{ l10n("cpu.arm32", "commons", user_lang) }}{{ l10n("requirements.text.dotnet", project_id, user_lang) }}...
{{ l10n("none.fs", "commons", user_lang) }}...
+ + {{ render_h2(l10n("source.code", "commons", user_lang)) }} + + + + + + + + + + + + + +
{{ l10n("version", "commons", user_lang) }}{{ l10n("download.multiple", "commons", user_lang) }}
v3.0.0...
+ + {{ render_h2(l10n("links.title", project_id, user_lang)) }} + {{ render_list_ul([ + "" + + l10n("github", "commons", user_lang) + + "", + ]) }} + +{% endblock %} diff --git a/templates/projects/web-usb-test.jinja b/templates/projects/web-usb-test.jinja new file mode 100644 index 0000000..5808d07 --- /dev/null +++ b/templates/projects/web-usb-test.jinja @@ -0,0 +1,18 @@ +{% extends "projects/_project.jinja" %} + +{% block extra_stylesheets %} + {{ render_applet_head(get_applets()["web-usb-test"], is_standalone) }} +{% endblock %} + +{% block project_content %} + {{ render_h2(l10n("intro.title", project_id, user_lang)) }} + {{ render_paragraph(l10n("intro.1", project_id, user_lang)) }} + {{ render_paragraph(l10n("intro.2", project_id, user_lang)) }} + + {% include 'applets/web-usb-test.jinja' %} + +{% endblock %} + +{% block extra_scripts %} + {{ render_applet_scripts(get_applets()["web-usb-test"], is_standalone) }} +{% endblock %} diff --git a/website/content/__init__.py b/website/content/__init__.py index d560493..00c15a9 100644 --- a/website/content/__init__.py +++ b/website/content/__init__.py @@ -1,5 +1,4 @@ import os -from pathlib import Path from typing import Any from locked_dict.locked_dict import LockedDict @@ -9,11 +8,6 @@ from .dataclasses import * __CONTENT: ContentRoot = ContentRoot() -__CONTENT_APPLETS: LockedDict[str, ContentApplet] = LockedDict() -__CONTENT_ARTICLES: LockedDict = LockedDict() -__CONTENT_PROJECTS: LockedDict[str, ContentProject] = LockedDict() -__CONTENT_TOOLS: LockedDict[str, ContentTool] = LockedDict() - def get_content() -> ContentRoot: return __CONTENT @@ -23,8 +17,8 @@ def get_applets() -> LockedDict[str, ContentApplet]: return __CONTENT.applets -def get_articles() -> LockedDict: - return __CONTENT_ARTICLES +#def get_articles() -> LockedDict: +# return __CONTENT.a def get_projects() -> LockedDict[str, ContentProject]: @@ -39,12 +33,24 @@ def get_projects_by_tags(tags: list[str]) -> dict[Any, ContentProject]: } +def get_projects_by_languages(languages: list[str]) -> dict[Any, ContentProject]: + project_obj: ContentProject + return { + project_key: project_value for project_key, project_value in __CONTENT.projects.items() + if any(language in project_value.metadata.general.languages for language in languages) + } + + +def get_projects_languages() -> list[str]: + return __CONTENT.projects_languages + + def get_tools() -> LockedDict[str, ContentTool]: return __CONTENT.tools def get_tools_by_tags(tags: list[str]) -> dict[Any, ContentProject]: - tool_obj: ContentProject + tool_obj: ContentTool return { tool_key: tool_value for tool_key, tool_value in __CONTENT.tools.items() if any(tag in tool_value.metadata.general.tags for tag in tags) @@ -100,7 +106,7 @@ def load_content_items() -> None: for project_data in projects_data["projects"]: _project = ContentProject(**project_data) __CONTENT.projects[_project.id] = _project - print(_project) + #print(_project) """for project_item in os.listdir(os.path.join(os.getcwd(), "data/projects")): project_item_path = os.path.join(os.getcwd(), "data/projects/", project_item) @@ -143,6 +149,13 @@ def load_content_items() -> None: # FIXME: Check if the required files exist too !""" + # Preparing some more stuff + for project in __CONTENT.projects.values(): + __CONTENT.projects_languages.extend(project.metadata.general.languages) + __CONTENT.projects_languages = list(set(__CONTENT.projects_languages)) + __CONTENT.projects_languages.sort() + #print(__CONTENT.projects_languages) + def validate_content_items() -> bool: pass diff --git a/website/content/dataclasses.py b/website/content/dataclasses.py index 435cdc2..7dbc1ad 100644 --- a/website/content/dataclasses.py +++ b/website/content/dataclasses.py @@ -114,3 +114,4 @@ class ContentRoot: # articles: list[Con] = field(default_factory=list) projects: LockedDict[str, ContentProject] = field(default_factory=LockedDict) tools: LockedDict[str, ContentTool] = field(default_factory=LockedDict) + projects_languages: list[str] = field(default_factory=list) diff --git a/website/renderers/applet.py b/website/renderers/applet.py index bf00c72..bc9a32f 100644 --- a/website/renderers/applet.py +++ b/website/renderers/applet.py @@ -1,29 +1,41 @@ +import os + from flask import url_for from website.content import ContentApplet -def render_applet_head(applet_data: ContentApplet) -> str: +def render_applet_head(applet_data: ContentApplet, is_standalone: bool = False) -> str: applet_style_html = "" for applet_style in applet_data.resources.stylesheets: - applet_style_html += ("") + if is_standalone: + with open(os.path.join("./static/resources/NibblePoker/applets/", applet_data.id, applet_style)) as applet_style_file: + applet_style_html += "" + else: + applet_style_html += ("") return applet_style_html -def render_applet_scripts(applet_data: ContentApplet): +def render_applet_scripts(applet_data: ContentApplet, is_standalone: bool = False): applet_script_html = "" for applet_script in applet_data.resources.scripts: - applet_script_html += ("") + if is_standalone: + with open(os.path.join("./static/resources/NibblePoker/applets/", applet_data.id, applet_script)) as applet_script_file: + applet_script_html += "" if applet_script.endswith(".mjs") else ">") + applet_script_html += applet_script_file.read() + applet_script_html += "" + else: + applet_script_html += ("") return applet_script_html diff --git a/website/renderers/standalone.py b/website/renderers/standalone.py new file mode 100644 index 0000000..fe21f56 --- /dev/null +++ b/website/renderers/standalone.py @@ -0,0 +1,6 @@ + +def get_standalone_common_headers() -> str: + _html = "" + with open("./static/resources/Standalone/nibblepoker.min.css", encoding='utf-8') as f: + _html += "" + return _html