diff --git a/docusaurus/docs/guides/external-auth/formatting-demo.md b/docusaurus/docs/guides/external-auth/formatting-demo.md new file mode 100644 index 000000000..c41b339d7 --- /dev/null +++ b/docusaurus/docs/guides/external-auth/formatting-demo.md @@ -0,0 +1,62 @@ +# Code Block Formatting Demo + +This here is a "plain" code block +``` +results: total 12 +drwxr-xr-x 5 user staff 160 Jan 28 12:34 . +drwxr-xr-x 20 user staff 640 Jan 28 12:34 .. +-rw-r--r-- 1 user staff 0 Jan 28 12:34 file.txt +``` + +This here is a "example" block. an example with just results and no +matching "language" ends up rendering like a plain code block +```example +results: total 12 +drwxr-xr-x 5 user staff 160 Jan 28 12:34 . +drwxr-xr-x 20 user staff 640 Jan 28 12:34 .. +-rw-r--r-- 1 user staff 0 Jan 28 12:34 file.txt +``` + +This here is a "example" block that has bash attached to it but does +not have a command/code section +```example-bash +results: total 12 +drwxr-xr-x 5 user staff 160 Jan 28 12:34 . +drwxr-xr-x 20 user staff 640 Jan 28 12:34 .. +-rw-r--r-- 1 user staff 0 Jan 28 12:34 file.txt +``` + +This is the paragraph leading up to the example. the example +is down below this text. here is more text just to emulate +what a long bit of text before the example might look like. + +```example-python +description: +this is a longer description. it's long. it's trying to be really, +really long just to put some words between you and the upcoming +example. more than the code below it. I'm just typing a bunch of stuff +here so that it is a lot + +command: +ls -l + +code: +print("Hello, World!") # Python +print("Hello, World!") # Python +print("Hello, World!") # Python +results: the results go here +and this is fundd +``` + +```javascript +console.log("Hello, World!"); // JavaScript +``` + +```c +#include +int main() { printf("Hello, World!\n"); return 0; } // C +``` + +```sh +echo "Hello, World!" # Bash +``` diff --git a/docusaurus/docs/guides/external-auth/index.md b/docusaurus/docs/guides/external-auth/index.md new file mode 100644 index 000000000..b277b8ee4 --- /dev/null +++ b/docusaurus/docs/guides/external-auth/index.md @@ -0,0 +1,84 @@ +# External Authentication + +OpenZiti external jwt signers are intended to be used with various external providers. Arguably, the most common type of +provider are external authorization servers, often referred to as IdPs. This guide will focus on this kind of external +provider and will focus on one authorization flow in particular: Authorization Code Flow with PKCE. + +OpenZiti can be configured to delegate authentication to external providers using +[external jwt signers](../../learn/core-concepts/security/authentication/50-external-jwt-signers.md). Configuring +OpenZiti to use external providers can be simple, however if you're new to the concepts (specifically the +[Proof Key for Code Exchange flow](https://www.rfc-editor.org/rfc/rfc7636)) it may be tricky to setup. There are +numerous excellent resources on the internet to learn more about OIDC, OAuth, and the PKCE flow if you need or want to +learn more. + +The guides provided here are meant to get you up and running quickly and guide you through configuring the OpenZiti +Controller to allow clients such as OpenZiti tunnelers to delegate authentication to a centralized provider. + +## Authentication Policies + +The OpenZiti Controller will come with a default authorization policy that allows for all primary authentication +methods: username/password, certificate-based, exteral-jwt-signer. If you are familiar with OpenZiti concepts, +additional auth-policies can be created and the default policy modified. If you are new to OpenZiti, it's recommended +you leave the default [authentication policy](../.. +/learn/core-concepts/security/authentication/30-authentication-policies.md) intact. + +> [!TIP] +> It's often useful to use certificate-based authentication (the "normal", one-time-token enrollment) along with +> an external provider providing a strong two-factor authentication scheme. This would ensure the device in use is +> trusted and would ensure a trusted human is using the device: human + device. + +## Configuring the Controller With an External JWT Signer for OIDC + +Correctly configuring an external JWT signer for use with OIDC requires a few key fields to be supplied. Most of +these fields are discoverable using the openid discovery endpoint. Generally, this will be a URl accessible by +adding `./.well-known/openid-discovery` to your identity provider issuer URL. For example, if you were using +Keycloak as your authorization provider of choice, you might have a Keycloak realm at +https://my.keycloak.openzitiio/realms/example. If that were the case, the openid discovery endpoint would be located at +https://my.keycloak.openzitiio/realms/example/.well-known/openid-configuration. + +Using the `.well-known/openid-configuration` will get much of the information required to successfully configure the +controller with an ext-jwt-signer for OIDC authentication. From this url, gather the following pieces of information: +* issuer +* + +```yaml-table +- CVE: cve-1-here + Advisory: 30 + Notice: New York +- CVE: Bob + Advisory: 25 + Notice: San Francisco +``` + +```yaml +- CVE: cve-1-here + Advisory: 30 + Notice: New York +- CVE: Bob + Advisory: 25 + Notice: San Francisco +``` + +```code +description: desc +--- +command: ls -l +--- +results: +here are the results +they could be super long + +``` + + +```code +description: This command lists all files in the current directory. +command: ls -la +results: total 12 +drwxr-xr-x 5 user staff 160 Jan 28 12:34 . +drwxr-xr-x 20 user staff 640 Jan 28 12:34 .. +-rw-r--r-- 1 user staff 0 Jan 28 12:34 file.txt +``` + + +a \ No newline at end of file diff --git a/docusaurus/docusaurus.config.js b/docusaurus/docusaurus.config.js index bf3a5539d..70b12d0ee 100644 --- a/docusaurus/docusaurus.config.js +++ b/docusaurus/docusaurus.config.js @@ -2,9 +2,8 @@ // Note: type annotations allow type checking and IDEs autocompletion // This docusaurus.config.js is used by GitHub Pages to build the site with baseUrl: '/docusaurus/' +import remarkGithubAdmonitionsToDirectives from "remark-github-admonitions-to-directives"; -//const lightCodeTheme = require('prism-react-renderer/themes/github'); -//const darkCodeTheme = require('prism-react-renderer/themes/dracula'); const { themes: prismThemes } = require('prism-react-renderer'); /** @type {import('@docusaurus/types').Config} */ @@ -37,7 +36,7 @@ const config = { mermaid: true, }, themes: ['@docusaurus/theme-mermaid'], - + plugins: [ [ '@docusaurus/plugin-content-docs', @@ -244,9 +243,12 @@ const config = { sidebarPath: require.resolve('./sidebars.js'), // Please change this to your repo. // Remove this to remove the "edit this page" links. - editUrl: - 'https://github.com/openziti/ziti-doc/tree/main/docusaurus', - + editUrl: 'https://github.com/openziti/ziti-doc/tree/main/docusaurus', + beforeDefaultRemarkPlugins: [remarkGithubAdmonitionsToDirectives, ], + remarkPlugins: [ + require('./src/plugins/remark/remark-yaml-table'), + require('./src/plugins/remark/remark-code-block'), + ], }, theme: { customCss: require.resolve('./src/css/custom.css'), @@ -314,7 +316,7 @@ const config = { sidebar: { hideable: true, autoCollapseCategories: true, - } + }, }, navbar: { title: '', @@ -416,8 +418,11 @@ const config = { copyright: `Copyright © ${new Date().getFullYear()} NetFoundry Inc.`, }, prism: { - theme: prismThemes.github, - darkTheme: prismThemes.dracula, + theme: prismThemes.github, + darkTheme: prismThemes.dracula, + // scala necessary to avoid Cannot set properties of undefined (setting 'triple-quoted-string') + // see https://github.com/Redocly/redoc/issues/2511 + additionalLanguages: ['python', 'java', 'csharp', 'go', 'bash', 'scala'], }, }, }; diff --git a/docusaurus/package.json b/docusaurus/package.json index 677e652c1..6649b105f 100644 --- a/docusaurus/package.json +++ b/docusaurus/package.json @@ -28,6 +28,7 @@ "echarts": "^5.6.0", "echarts-for-react": "^3.0.2", "jquery": "^3.7.1", + "js-yaml": "^4.1.0", "prism-react-renderer": "2.4.1", "react": "^19.0.0", "react-device-detect": "^2.2.3", @@ -35,7 +36,9 @@ "react-github-btn": "^1.4.0", "react-helmet": "^6.1.0", "react-player": "^2.16.0", - "redocusaurus": "^2.2.1" + "redocusaurus": "^2.2.1", + "remark-github-admonitions-to-directives": "^2.1.0", + "unist-util-visit": "^5.0.0" }, "devDependencies": { "@docusaurus/module-type-aliases": "3.7.0", diff --git a/docusaurus/src/css/custom.css b/docusaurus/src/css/custom.css index 88aee84ae..b216ef3f6 100644 --- a/docusaurus/src/css/custom.css +++ b/docusaurus/src/css/custom.css @@ -17,7 +17,13 @@ --ifm-color-primary-light: #a14ded; --ifm-color-primary-lighter: #a85aee; --ifm-color-primary-lightest: #b776f1; - --code-block-text-color: #ddd8e5; + --code-block-text-colora: #ddd8e5; + --code-block-text-colorb: #add8e5; + --code-block-text-color: var(--ifm-heading-color); + --code-block-background-color: #ECEEEF; + --ifm-font-family-monospace: "Consolas", monospace; + --ifm-font-family-sans: "Open Sans", sans-serif; + --ifm-font-family-sans-default: sans-serif; --ifm-heading-color: #002451; --ifm-color-banner: #edac15; --ifm-navbar-item-padding-horizontal: .5em; @@ -41,6 +47,7 @@ --h3-size: 25px; --docusaurus-collapse-button-bg-hover: rgb(0 0 0 / 2%); --ifm-tabs-color-active: var(--ifm-font-color-base); + --ifm-global-shadow-lw: 0 10px 10px 0 rgba(0, 0, 0, 0.2); } /* For readability concerns, you should choose a lighter palette in dark mode. */ @@ -64,6 +71,8 @@ html[data-theme="dark"] { --ifm-heading-color: #94a3b8; --ifm-menu-color: #ffffff; --ifm-toc-link-color: #ffffff; + --code-block-text-color: var(--ifm-heading-color); + --code-block-background-color: #434862; } html[data-theme=dark] #Wizardly code { @@ -289,7 +298,7 @@ input[type="radio"]:checked+label:after { font-size: 16px; font-weight: bold; background: rgba(222, 222, 222, 1); - padding: 0px !important; + padding: 0 !important; cursor: pointer; margin: 20px 20px; text-align:center; @@ -313,7 +322,7 @@ input[type="radio"]:checked+label:after { -o-transition: all .2s ease-in-out; -webkit-transition: all .2s ease-in-out; transition: all .2s ease-in-out; background: whitesmoke; - box-shadow: 0px 0px 10px 0px rgba(50, 50, 50, 0.75); + box-shadow: 0 0 10px 0 rgba(50, 50, 50, 0.75); color:grey; display: flex; flex-direction: column; @@ -326,7 +335,7 @@ input[type="radio"]:checked+label:after { color: black; background: rgba(255, 255, 255, 0.9); font-weight: bold; - box-shadow: 0px 0px 20px 0px rgba(0,104,249,1); + box-shadow: 0 0 20px 0 rgba(0,104,249,1); -moz-transition: all .15s ease-in-out; -o-transition: all .15s ease-in-out; -webkit-transition: all .15s ease-in-out; @@ -346,7 +355,7 @@ input[type="radio"]:checked+label:after { .btn-hover:hover { text-decoration-line: none !important; color: black !important; - box-shadow: 0px 0px 20px 0px rgba(244,4,77,.5); + box-shadow: 0 0 20px 0 rgba(244,4,77,.5); height: 80px; background-position: 100% 0; -moz-transition: all .15s ease-in-out; @@ -443,7 +452,7 @@ float: right; /* highlight sidebar section headings */ .theme-doc-sidebar-item-link-level-1.sidebar-title { margin-top: 20px; - margin-left: 0px; + margin-left: 0; font-weight: bold; } @@ -465,7 +474,7 @@ div > aside > div > nav > ul > li > a:active { /* DFinger Styles */ /* @extend display-flex; */ -display-flex, +.display-flex, .form-flex, .form-date-group, .steps ul, @@ -507,7 +516,7 @@ display-flex, .whitez:hover { filter: brightness(95%); transition: all 0.3s ease; - -webkit-transform: translate3d(0, 0, 0px); + -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } @@ -516,7 +525,7 @@ display-flex, transition: opacity .3s cubic-bezier(.38, .41, .27, 1); transform: scale(.97) !important; transition: all 0.3s ease; - -webkit-transform: translate3d(0, 0, 0px); + -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } @@ -617,13 +626,13 @@ list-type-ulli, .wizardmodal h2 { font-size: 30px; - margin: 0px; + margin: 0; color: #002451; } .wizardmodal h3 { font-size: 24px; - margin: 0px; + margin: 0; } a { @@ -716,8 +725,8 @@ html[data-theme=dark] a:hover { } .wizardmodal fieldset { - padding: 0px; - margin: 0px; + padding: 0; + margin: 0; border: none; padding-left: 45px; padding-right: 55px; @@ -725,7 +734,7 @@ html[data-theme=dark] a:hover { } .wizardmodal p.desc { - margin: 0px; + margin: 0; margin-bottom: 15px; color: #252525; } @@ -759,7 +768,7 @@ input { color: #222; font-weight: bold; font-size: 14px; - font-family: 'Open Sans'; + font-family: var(--ifm-font-family-sans), var(--ifm-font-family-sans-default); border-radius: 7px; } @@ -860,8 +869,8 @@ input { .wizardmodal { position: fixed; - left: 0px; - top: 0px; + left: 0; + top: 0; height: 100vh; width: 100vw; z-index: 1000; @@ -869,17 +878,17 @@ input { } .wizardmodal h1 { - padding-bottom: 0px; + padding-bottom: 0; line-height: 52px; color: #002451; } .wizardmodal .page-wrapper { - top: 0px; + top: 0; min-height: 100vh; position: absolute; - left: 0px; - right: 0px; + left: 0; + right: 0; backdrop-filter: blur(5px); } @@ -1095,7 +1104,7 @@ input { text-decoration: none; border-radius: 8px; transition: all 0.3s ease; - border-width: 0px; + border-width: 0; font-weight: 600; font-size: 15px; cursor: pointer; @@ -1104,7 +1113,7 @@ input { .actions ul li a:hover, .actions ul li button:hover { filter: brightness(95%); - box-shadow: 0px 5px 20px 0px rgba(0, 0, 0, 0.05); + box-shadow: 0 5px 20px 0 rgba(0, 0, 0, 0.05); transition: all 0.3s ease; } @@ -1137,7 +1146,7 @@ input { } .form-radio-flex input+label { - margin: 0px; + margin: 0; width: 223px; height: 133px; box-sizing: border-box; @@ -1172,7 +1181,7 @@ input { label.error { display: block; position: absolute; - top: 0px; + top: 0; right: 0; } @@ -1240,7 +1249,7 @@ input.error { .choose-bank-desc { color: #666; - margin: 0px; + margin: 0; padding-top: 30px; padding-bottom: 35px; } @@ -1254,18 +1263,18 @@ input.error { -o-box-shadow: none; -ms-box-shadow: none; background: #e8e8e8; - border-radius: 0px; - -moz-border-radius: 0px; - -webkit-border-radius: 0px; - -o-border-radius: 0px; - -ms-border-radius: 0px; + border-radius: 0; + -moz-border-radius: 0; + -webkit-border-radius: 0; + -o-border-radius: 0; + -ms-border-radius: 0; position: relative; margin-top: 110px; } #slider-margin .noUi-marker-horizontal.noUi-marker-large, #slider-margin .noUi-marker-horizontal.noUi-marker { - height: 0px; + height: 0; } #slider-margin .noUi-connect { @@ -1273,11 +1282,11 @@ input.error { } #slider-margin .noUi-connects { - border-radius: 0px; - -moz-border-radius: 0px; - -webkit-border-radius: 0px; - -o-border-radius: 0px; - -ms-border-radius: 0px; + border-radius: 0; + -moz-border-radius: 0; + -webkit-border-radius: 0; + -o-border-radius: 0; + -ms-border-radius: 0; } #slider-margin .noUi-handle { @@ -1289,12 +1298,12 @@ input.error { border: none; right: -15px; border: 1px solid #076bfe; - border-radius: 0px; + border-radius: 0; } #slider-margin .noUi-handle:after, #slider-margin .noUi-handle:before { - width: 0px; + width: 0; } #slider-margin .noUi-handle .noUi-tooltip { @@ -1303,7 +1312,7 @@ input.error { background: transparent; font-size: 16px; color: #076bfe; - padding: 0px; + padding: 0; } #slider-margin .noUi-pips { @@ -1331,7 +1340,7 @@ input.error { .your-money { font-size: 16px; color: #222; - margin: 0px; + margin: 0; padding-top: 62px; } @@ -1416,11 +1425,11 @@ input.error { -webkit-flex-direction: column; -o-flex-direction: column; -ms-flex-direction: column; - margin: 0px; + margin: 0; } .form-flex .form-group { - padding: 0px; + padding: 0; } fieldset, @@ -1539,7 +1548,7 @@ ol.linenums li:before { color: #fff !important; } - .token-line { +.disabled-token-line { color: #fff !important; } @@ -1548,7 +1557,7 @@ ol.linenums li:before { height: 40px; width: 40px; position: absolute; - right: 0px; + right: 0; top: 10px; background-size: contain; cursor: pointer; @@ -1742,8 +1751,8 @@ li.L9 {} .modal { position: fixed; - top: 0px; - left: 0px; + top: 0; + left: 0; width: 100vw; height: 100vh; z-index: 100000; @@ -1759,7 +1768,7 @@ li.L9 {} height: 150px; display: block; margin: auto; - box-shadow: 0px 10px 18px 0px rgb(48 48 56 / 5%); + box-shadow: 0 10px 18px 0 rgb(48 48 56 / 5%); transition: all 0.3s ease; transform: translate3d(0, 0, 0); cursor: pointer; @@ -1780,7 +1789,7 @@ li.L9 {} height: 150px; display: block; margin: auto; - box-shadow: 0px 10px 18px 0px rgb(48 48 56 / 5%); + box-shadow: 0 10px 18px 0 rgb(48 48 56 / 5%); transition: all 0.3s ease; transform: translate3d(0, 0, 0); cursor: pointer; @@ -1814,7 +1823,7 @@ li.L9 {} .builderbutton:hover { filter: brightness(95%); transition: all 0.3s ease; - -webkit-transform: translate3d(0, 0, 0px); + -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } @@ -1823,7 +1832,7 @@ li.L9 {} transition: opacity .3s cubic-bezier(.38, .41, .27, 1); transform: scale(.97) !important; transition: all 0.3s ease; - -webkit-transform: translate3d(0, 0, 0px); + -webkit-transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0); } @@ -1842,7 +1851,7 @@ li.L9 {} } code { - font-family: var(--ifm-font-family-monospace); + font-family: var(--ifm-font-family-monospace), monospace; vertical-align: middle; font-size: 87.5%; font-weight: 600; @@ -1850,18 +1859,16 @@ code { border-radius: 7px; padding: 2px 8px 2px 8px; - background-color: rgba(117, 82, 136, 0.2) !important; + /*background-color: rgba(117, 82, 136, 0.2) !important;*/ border: 1px solid rgba(117, 82, 136, 0.98); - color: rgba(117, 82, 136, 0.98); - } code p { - color: var(--code-block-text-color); + } li a code { - font-family: var(--ifm-font-family-monospace); + font-family: var(--ifm-font-family-monospace), monospace; vertical-align: middle; font-size: 87.5%; font-weight: 600; @@ -1888,13 +1895,13 @@ li a code { width: 100%; padding: 20px; border-radius: 7px; - margin-top: 0px; + margin-top: 0; } .tabs__item { background-color: rgba(225,225,225, 0.3); - border-bottom-left-radius: 0px; - border-bottom-right-radius: 0px; + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; margin-right: 10px; pointer-events: all; } @@ -1909,18 +1916,18 @@ li a code { .tabs__item.tabs__item--active { background-color: rgb(142,142,142, .2); - border-bottom-width: 0px; + border-bottom-width: 0; pointer-events: none; } .tabs__item.tabs__item--active:hover { background-color: rgb(142,142,142, .2); - border-bottom-width: 0px; + border-bottom-width: 0; pointer-events: none; } .margin-top--md { - margin-top: 0px !important; + margin-top: 0 !important; } .tabs__item:hover { @@ -1939,9 +1946,6 @@ li a code { --ifm-alert-foreground-color: #b122e326; } -pre code { - background-color: rgba(44,55,65) !important; -} .alert { --ifm-code-background: var(--ifm-alert-background-color-highlight); @@ -2103,7 +2107,7 @@ pre.off { .pagination-nav__link:hover { filter: brightness(85%); - box-shadow: 0px 5px 20px 0px rgba(0, 0, 0, 0.05); + box-shadow: 0 5px 20px 0 rgba(0, 0, 0, 0.05); transition: all 0.3s ease; } @@ -2118,3 +2122,50 @@ pre.off { .language-buttonless div > button { display: none; } + +.large-text { + font-size: 100px; +} + +.admonition.example { + background-color: #f9f5ff; /* Light lavender background */ + border-left: 4px solid #6c63ff; /* Purple border */ + padding: 16px; + border-radius: 8px; + margin: 16px 0; +} + +.admonition.example .admonition-title { + font-size: 18px; + font-weight: bold; + color: #5a54ff; /* Purple text */ + margin-bottom: 8px; +} + +.code-section { + border-left: 3px solid var(--code-block-text-color); + border-top: 1px solid var(--code-block-text-color); + border-right: 1px solid var(--code-block-text-color); + border-bottom: 1px solid var(--code-block-text-color); + margin: 16px 0; + background: var(--code-block-background-color); /* Light gray background */ + padding-left: 12px; + padding-right: 12px; + border-radius: 4px; +} + +.code-section pre { + background: var(--code-block-background-color); + color: var(--code-block-text-color); + _padding: 8px; + border-radius: 4px; +} + +.code-section p { + color: var(--code-block-text-color); +} + +.code-section-desc p { + font-size: 1.1em; + border-bottom: 1px solid #ccc; +} diff --git a/docusaurus/src/plugins/remark/remark-code-block.js b/docusaurus/src/plugins/remark/remark-code-block.js new file mode 100644 index 000000000..a35dfb462 --- /dev/null +++ b/docusaurus/src/plugins/remark/remark-code-block.js @@ -0,0 +1,118 @@ +import { visit } from 'unist-util-visit'; + +module.exports = function remarkCodeSections() { + const desc_text = "description:"; + const command_text = "command:"; + const code_text = "code:"; + const results_text = "results:"; + return (tree) => { + visit(tree, 'code', (node, index, parent) => { + if (node.lang && node.lang.startsWith('example-')) { + const lang = node.lang.replace('example-', '').trim(); // Extract language (e.g., "bash", "go") + const lines = node.value.split('\n'); + let description = '', command = '', code = '', results = ''; + let currentSection = ''; + + lines.forEach((line) => { + if (line.startsWith(desc_text)) { + currentSection = 'description'; + description = line.replace(desc_text, '').trim(); + } else if (line.startsWith(command_text)) { + currentSection = 'command'; + command = line.replace(command_text, '').trim(); + } else if (line.startsWith(code_text)) { + currentSection = 'code'; + code = line.replace(code_text, '').trim(); + } else if (line.startsWith(results_text)) { + currentSection = 'results'; + results = line.replace(results_text, '').trim(); + } else { + if (currentSection === 'description') description += `\n${line}`; + else if (currentSection === 'command') command += `\n${line}`; + else if (currentSection === 'code') code += `\n${line}`; + else if (currentSection === 'results') results += `\n${line}`; + } + }); + + const divWrapper = { + type: 'div', + data: { + hName: 'div', + hProperties: { className: 'code-section' } + }, + children: [], + }; + + if (description) { + const descDiv = { + type: 'div', + data: { + hName: 'div', + hProperties: { className: 'code-section-desc' } + }, + children: [], + }; + descDiv.children.push( + { type: 'paragraph', children: [{ type: 'strong', children: [{ type: 'text', value: 'Description:' }] }] }, + { type: 'paragraph', children: [{ type: 'text', value: description.trim() }], data: { hProperties: { style: "padding-bottom: 10px;" } } }, + ); + divWrapper.children.push(descDiv); + } + + if (command) { + const cmdDiv = { + type: 'div', + data: { + hName: 'div', + hProperties: { className: 'code-section-command' } + }, + children: [], + }; + cmdDiv.children.push( + { type: 'paragraph', children: [{ type: 'strong', children: [{ type: 'text', value: 'Command:' }] }] }, + { type: 'code', lang: 'sh', value: command.trim() } // Treat command as shell script + ); + divWrapper.children.push(cmdDiv); + } + + if (code) { + const codeDiv = { + type: 'div', + data: { + hName: 'div', + hProperties: { className: 'code-section-code' } + }, + children: [], + }; + codeDiv.children.push( + { type: 'paragraph', children: [{ type: 'strong', children: [{ type: 'text', value: 'Code:' }] }] }, + { type: 'code', lang: lang, value: code.trim() } // Uses detected language + ); + divWrapper.children.push(codeDiv); + } + + if (results) { + const resultsDiv = { + type: 'div', + data: { + hName: 'div', + hProperties: { className: 'code-section-results' } + }, + children: [], + }; + resultsDiv.children.push( + { type: 'paragraph', children: [{ type: 'strong', children: [{ type: 'text', value: 'Results:' }] }] }, + { + type: 'code', + lang: 'buttonless', // Apply the buttonless class so docusaurus doesn't provide the 'copy' button + value: results.trim() + } + ); + divWrapper.children.push(resultsDiv); + } + + parent.children.splice(index, 1, divWrapper); + } + }); + }; +}; diff --git a/docusaurus/src/plugins/remark/remark-yaml-table.js b/docusaurus/src/plugins/remark/remark-yaml-table.js new file mode 100644 index 000000000..207f66c2d --- /dev/null +++ b/docusaurus/src/plugins/remark/remark-yaml-table.js @@ -0,0 +1,46 @@ +import { visit } from 'unist-util-visit'; +import yaml from 'js-yaml'; + +module.exports = function remarkYamlTable() { + return (tree) => { + visit(tree, 'code', (node, index, parent) => { + if (node.lang === 'yaml-table') { + try { + const data = yaml.load(node.value); + if (Array.isArray(data) && data.length > 0 && typeof data[0] === 'object') { + const headers = Object.keys(data[0]); + + // Construct Markdown table as AST nodes + const tableRows = data.map((row) => ({ + type: 'tableRow', + children: headers.map((header) => ({ + type: 'tableCell', + children: [{ type: 'text', value: String(row[header] ?? '') }], + })), + })); + + const tableNode = { + type: 'table', + align: headers.map(() => null), + children: [ + { + type: 'tableRow', + children: headers.map((header) => ({ + type: 'tableCell', + children: [{ type: 'text', value: header }], + })), + }, + ...tableRows, + ], + }; + + // Replace the YAML code block with the generated table + parent.children[index] = tableNode; + } + } catch (error) { + console.error('YAML parsing error:', error); + } + } + }); + }; +}; diff --git a/docusaurus/yarn.lock b/docusaurus/yarn.lock index aed8d6325..8c749d08d 100644 --- a/docusaurus/yarn.lock +++ b/docusaurus/yarn.lock @@ -54,6 +54,11 @@ resolved "https://registry.yarnpkg.com/@algolia/client-common/-/client-common-5.19.0.tgz#efddaaf28f0f478117c2aab22d19c99b06f99761" integrity sha512-2ERRbICHXvtj5kfFpY5r8qu9pJII/NAHsdgUXnUitQFwPdPL7wXiupcvZJC7DSntOnE8AE0lM7oDsPhrJfj5nQ== +"@algolia/client-common@5.20.0": + version "5.20.0" + resolved "https://registry.yarnpkg.com/@algolia/client-common/-/client-common-5.20.0.tgz#0b6b96c779d30afada68cf36f20f0c280e3f1273" + integrity sha512-iSTFT3IU8KNpbAHcBUJw2HUrPnMXeXLyGajmCL7gIzWOsYM4GabZDHXOFx93WGiXMti1dymz8k8R+bfHv1YZmA== + "@algolia/client-insights@5.19.0": version "5.19.0" resolved "https://registry.yarnpkg.com/@algolia/client-insights/-/client-insights-5.19.0.tgz#81ff8eb3df724f6dd8ea3f423966b9ef7d36f903" @@ -84,7 +89,7 @@ "@algolia/requester-fetch" "5.19.0" "@algolia/requester-node-http" "5.19.0" -"@algolia/client-search@5.19.0", "@algolia/client-search@^5.19.0": +"@algolia/client-search@5.19.0": version "5.19.0" resolved "https://registry.yarnpkg.com/@algolia/client-search/-/client-search-5.19.0.tgz#04fc5d7e26d41c99144eb33eedb0ea6f9b1c0056" integrity sha512-Ctg3xXD/1VtcwmkulR5+cKGOMj4r0wC49Y/KZdGQcqpydKn+e86F6l3tb3utLJQVq4lpEJud6kdRykFgcNsp8Q== @@ -94,6 +99,16 @@ "@algolia/requester-fetch" "5.19.0" "@algolia/requester-node-http" "5.19.0" +"@algolia/client-search@^5.19.0": + version "5.20.0" + resolved "https://registry.yarnpkg.com/@algolia/client-search/-/client-search-5.20.0.tgz#4b847bda4bef2eee8ba72ef3ce59be612319e8d0" + integrity sha512-KL1zWTzrlN4MSiaK1ea560iCA/UewMbS4ZsLQRPoDTWyrbDKVbztkPwwv764LAqgXk0fvkNZvJ3IelcK7DqhjQ== + dependencies: + "@algolia/client-common" "5.20.0" + "@algolia/requester-browser-xhr" "5.20.0" + "@algolia/requester-fetch" "5.20.0" + "@algolia/requester-node-http" "5.20.0" + "@algolia/events@^4.0.1": version "4.0.1" resolved "https://registry.yarnpkg.com/@algolia/events/-/events-4.0.1.tgz#fd39e7477e7bc703d7f893b556f676c032af3950" @@ -136,6 +151,13 @@ dependencies: "@algolia/client-common" "5.19.0" +"@algolia/requester-browser-xhr@5.20.0": + version "5.20.0" + resolved "https://registry.yarnpkg.com/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.20.0.tgz#998fd5c1123fbc49b664c484c6b0cd7cefc6a1fa" + integrity sha512-t6//lXsq8E85JMenHrI6mhViipUT5riNhEfCcvtRsTV+KIBpC6Od18eK864dmBhoc5MubM0f+sGpKOqJIlBSCg== + dependencies: + "@algolia/client-common" "5.20.0" + "@algolia/requester-fetch@5.19.0": version "5.19.0" resolved "https://registry.yarnpkg.com/@algolia/requester-fetch/-/requester-fetch-5.19.0.tgz#59fe52733a718fc23bde548b377b52baf7228993" @@ -143,6 +165,13 @@ dependencies: "@algolia/client-common" "5.19.0" +"@algolia/requester-fetch@5.20.0": + version "5.20.0" + resolved "https://registry.yarnpkg.com/@algolia/requester-fetch/-/requester-fetch-5.20.0.tgz#fed4f135f22c246ce40cf23c9d6518884be43e5e" + integrity sha512-FHxYGqRY+6bgjKsK4aUsTAg6xMs2S21elPe4Y50GB0Y041ihvw41Vlwy2QS6K9ldoftX4JvXodbKTcmuQxywdQ== + dependencies: + "@algolia/client-common" "5.20.0" + "@algolia/requester-node-http@5.19.0": version "5.19.0" resolved "https://registry.yarnpkg.com/@algolia/requester-node-http/-/requester-node-http-5.19.0.tgz#edbd58158d9dec774d608fbf2b2196d0ca4b257c" @@ -150,6 +179,13 @@ dependencies: "@algolia/client-common" "5.19.0" +"@algolia/requester-node-http@5.20.0": + version "5.20.0" + resolved "https://registry.yarnpkg.com/@algolia/requester-node-http/-/requester-node-http-5.20.0.tgz#920a9488be07c0521951da92f36be61f47c4d0e0" + integrity sha512-kmtQClq/w3vtPteDSPvaW9SPZL/xrIgMrxZyAgsFwrJk0vJxqyC5/hwHmrCraDnStnGSADnLpBf4SpZnwnkwWw== + dependencies: + "@algolia/client-common" "5.20.0" + "@ampproject/remapping@^2.2.0": version "2.3.0" resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.3.0.tgz#ed441b6fa600072520ce18b43d2c8cc8caecc7f4" @@ -2846,13 +2882,20 @@ "@types/history" "^4.7.11" "@types/react" "*" -"@types/react@*", "@types/react@^19.0.7": +"@types/react@*": version "19.0.7" resolved "https://registry.yarnpkg.com/@types/react/-/react-19.0.7.tgz#c451968b999d1cb2d9207dc5ff56496164cf511d" integrity sha512-MoFsEJKkAtZCrC1r6CM8U22GzhG7u2Wir8ons/aCKH6MBdD1ibV24zOSSkdZVUKqN5i396zG5VKLYZ3yaUZdLA== dependencies: csstype "^3.0.2" +"@types/react@^19.0.7": + version "19.0.8" + resolved "https://registry.yarnpkg.com/@types/react/-/react-19.0.8.tgz#7098e6159f2a61e4f4cef2c1223c044a9bec590e" + integrity sha512-9P/o1IGdfmQxrujGbIMDyYaaCykhLKc0NGCtYcECNUr9UAaDe4gwvV9bR6tvd5Br1SG0j+PBpbKr2UYY8CwqSw== + dependencies: + csstype "^3.0.2" + "@types/retry@0.12.0": version "0.12.0" resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d" @@ -9134,6 +9177,16 @@ remark-gfm@^4.0.0: remark-stringify "^11.0.0" unified "^11.0.0" +remark-github-admonitions-to-directives@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/remark-github-admonitions-to-directives/-/remark-github-admonitions-to-directives-2.1.0.tgz#5bdb5deb3e1d6ee7a1427aeea9e27e7b3ea96ff4" + integrity sha512-bI3E4Oj1pKY3ym2IQrrVCdORgEu0+mSrWgpCYFNy8QvytfnLs/nAacVPjkWU/JzDMUiQio2k4nTFP7bsIr9TSA== + dependencies: + "@types/mdast" "^4.0.0" + mdast-util-directive "^3.0.0" + unified "^11.0.0" + unist-util-visit "^5.0.0" + remark-mdx@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/remark-mdx/-/remark-mdx-3.1.0.tgz#f979be729ecb35318fa48e2135c1169607a78343"