diff --git a/docs/docs/api.mdx b/docs/docs/api.mdx index 4b5c581..dff1da2 100644 --- a/docs/docs/api.mdx +++ b/docs/docs/api.mdx @@ -56,7 +56,7 @@ FastAPI provides built in support for both [Swagger UI](https://swagger.io/tools ## Example -```yaml +```yaml title="hyperglass.yaml" docs: base_url: https://lg.example.net description: "" diff --git a/docs/docs/cache.mdx b/docs/docs/cache.mdx index 4145b2c..25a33e5 100644 --- a/docs/docs/cache.mdx +++ b/docs/docs/cache.mdx @@ -24,7 +24,7 @@ hyperglass caches every query response to a Redis database, and always responds ## Example -```yaml +```yaml title="hyperglass.yaml" cache: database: 0 host: localhost diff --git a/docs/docs/commands.mdx b/docs/docs/commands.mdx index e3ff59f..6b3b032 100644 --- a/docs/docs/commands.mdx +++ b/docs/docs/commands.mdx @@ -44,7 +44,7 @@ Only the command you specify will be overridden. Each command definition carries the following structure: -```yaml +```yaml title="commands.yaml" command_name: ipv4_default: ... bgp_route: ... diff --git a/docs/docs/configuration.mdx b/docs/docs/configuration.mdx index 14a6059..1bdb014 100644 --- a/docs/docs/configuration.mdx +++ b/docs/docs/configuration.mdx @@ -61,7 +61,7 @@ The `netmiko_delay_factor` parameter should only be used if you're experiencing #### Example -```yaml +```yaml title="hyperglass.yaml" debug: false developer_mode: false org_name: Beloved Hyperglass User @@ -92,7 +92,7 @@ From the top level, the following subsections may be defined and configured: To add, as an example, a Cisco router, add the following to your `devices.yaml`, with the relevant details changed for your device: -```yaml +```yaml title="devices.yaml" routers: - name: router01.pop01 address: 10.0.0.1 diff --git a/docs/docs/devices.mdx b/docs/docs/devices.mdx index 3a804a3..6c7bc89 100644 --- a/docs/docs/devices.mdx +++ b/docs/docs/devices.mdx @@ -16,22 +16,22 @@ import Code from "../src/components/JSXCode"; ## All Device Parameters -| Parameter | Type | Description | -| :------------------ | :-----: | :-------------------------------------------------------------------------------------------------------------------------------- | -| `name` | String | Device hostname. This is not user-facing. | -| `address` | String | Device management hostname or IP address. | -| `network` | String | Primary network this device is a member of. Used for device grouping. Usually something like 'AS65000'. | -| `display_name` | String | Device's user-facing name. | -| `port` | Integer | TCP port used to connect to the device. | -| `nos` | String | Network Operating System. Must be a supported platform. | -| `credential` | | Device Credential Configuration | -| `vrfs` | | Device VRF Configuration | -| `proxy` | | SSH Proxy Configuration | -| `ssl` | | SSL Configuration for devices using [hyperglass-agent](https://github.com/checktheroads/hyperglass-agent). | +| Parameter | Type | Description | +| :------------------ | :-----: | :----------------------------------------------------------------------------------------------------------------- | +| `name` | String | Device hostname. This is not user-facing. | +| `address` | String | Device management hostname or IP address. | +| `network` | String | Primary network this device is a member of. Used for device grouping. Usually something like 'AS65000'. | +| `display_name` | String | Device's user-facing name. | +| `port` | Integer | TCP port used to connect to the device. | +| `nos` | String | Network Operating System. Must be a supported platform. | +| `credential` | | [Device Credential Configuration](#credential) | +| `vrfs` | | [Device VRF Configuration](#vrf) | +| `proxy` | | [SSH Proxy Configuration](#proxy) | +| `ssl` | | [SSL Configuration](#ss;) for devices using [hyperglass-agent](https://github.com/checktheroads/hyperglass-agent). | ### `proxy` -Any device that uses SSH (see platforms for breakdown) can be accessed through an intermediary SSH "proxy". The process is nearly identical to using local SSH tunneling, e.g. `ssh -L local_port:remote_device:remote_port username@proxy_server -p proxy_port`. +Any device that uses SSH (see [platforms](platforms.mdx) for breakdown) can be accessed through an intermediary SSH "proxy". The process is nearly identical to using local SSH tunneling, e.g. `ssh -L local_port:remote_device:remote_port username@proxy_server -p proxy_port`. | Parameter | Type | Default | Description | | :---------------- | :-----: | :------------ | :--------------------------------------------------------------------------------------------------------------------- | @@ -128,7 +128,7 @@ Each VRF may enable, disable, or customize the contextual help menu for each ena For example: -```yaml +```yaml title="devices.yaml" info: bgp_route: enable: true @@ -151,7 +151,7 @@ Telnet support is provided via the underlying device connection handling framewo For example: -```yaml {3-4} +```yaml {3-4} title="devices.yaml" routers: - name: router01 nos: cisco_ios_telnet @@ -163,7 +163,7 @@ routers: Below is a full example with nearly every available knob turned: -```yaml +```yaml title="devices.yaml" routers: # HTTP/hyperglass-agent device - name: router01 diff --git a/docs/docs/logging.mdx b/docs/docs/logging.mdx index 57cfeb8..5d5bb36 100644 --- a/docs/docs/logging.mdx +++ b/docs/docs/logging.mdx @@ -105,7 +105,7 @@ If the `provider` field is set to `'generic'`, the webhook will POST JSON data i ## Full example -```yaml +```yaml title="hyperglass.yaml" logging: directory: /var/log format: json diff --git a/docs/docs/messages.mdx b/docs/docs/messages.mdx index f7dff73..19df58c 100644 --- a/docs/docs/messages.mdx +++ b/docs/docs/messages.mdx @@ -32,7 +32,7 @@ All user-facing status messages can be customized if needed. ## Example -```yaml +```yaml title="hyperglass.yaml" messages: acl_denied: "{target} is a member of {denied_network}, which is not allowed." acl_not_allowed: "{target} is not allowed." diff --git a/docs/docs/production.mdx b/docs/docs/production.mdx index 70771f1..1353bda 100644 --- a/docs/docs/production.mdx +++ b/docs/docs/production.mdx @@ -13,7 +13,7 @@ You'll want to run hyperglass behind a reverse proxy in production to serve the The following file can be placed anywhere, and referenced at runtime with `sudo caddy run -config `. The highlighted lines should be replaced with your installation's specific variables. -```text {1,2,4,5,8,11} +```text {1,2,4,5,8,11} title="Caddy" lg.example.com:443 { tls person@example.com file_server { @@ -38,7 +38,7 @@ The `tls` directive will tell Caddy to automatically use Let's Encrypt to genera The following file can be placed at `/etc/nginx/sites-enabled/hyperglass`. It supports IPv6, and will automatically redirect to HTTPS. The highlighted lines should be replaced with your installation's specific variables. -```nginx {4,9,10,17,19} +```nginx {4,9,10,17,19} title="NGINX" server { listen 80; listen [::]:80; diff --git a/docs/docs/queries.mdx b/docs/docs/queries.mdx index 02ed12f..789443b 100644 --- a/docs/docs/queries.mdx +++ b/docs/docs/queries.mdx @@ -24,7 +24,7 @@ Each query type may be disabled, enabled, or customized. The `display_name` para #### Example -```yaml +```yaml title="hyperglass.yaml" queries: bgp_route: display_name: BGP Route @@ -53,7 +53,7 @@ If using `select` mode, you may define a list of communities the users can choos #### Example -```yaml +```yaml title="hyperglass.yaml" queries: bgp_community: enable: true @@ -80,7 +80,7 @@ If using `input` mode, hyperglass allows you to override the default regular exp #### Example -```yaml +```yaml title="hyperglass.yaml" queries: bgp_community: enable: true @@ -106,7 +106,7 @@ Regular expression patterns must be enclosed in single quotes, e.g. `'^.*$'` #### Example -```yaml +```yaml title="hyperglass.yaml" queries: bgp_aspath: display_name: BGP AS Path @@ -137,7 +137,7 @@ AS Path regular expression patterns may also be customized, should you wish to m #### Example -```yaml +```yaml title="hyperglass.yaml" queries: ping: display_name: Ping @@ -154,7 +154,7 @@ queries: #### Example -```yaml +```yaml title="hyperglass.yaml" queries: traceroute: display_name: Traceroute diff --git a/docs/docs/ui.mdx b/docs/docs/ui.mdx index c904b92..84bac95 100644 --- a/docs/docs/ui.mdx +++ b/docs/docs/ui.mdx @@ -169,7 +169,7 @@ Currently, only [Google Fonts](https://fonts.google.com/) are supported. ## Example -```yaml +```yaml title="hyperglass.yaml" web: credit: enable: true diff --git a/docs/package.json b/docs/package.json index 2766523..d61019e 100755 --- a/docs/package.json +++ b/docs/package.json @@ -9,9 +9,9 @@ "deploy": "docusaurus deploy" }, "dependencies": { - "@docusaurus/core": "^2.0.0-alpha.43", - "@docusaurus/plugin-sitemap": "^2.0.0-alpha.37", - "@docusaurus/preset-classic": "^2.0.0-alpha.43", + "@docusaurus/core": "^2.0.0-alpha.50", + "@docusaurus/plugin-sitemap": "^2.0.0-alpha.50", + "@docusaurus/preset-classic": "^2.0.0-alpha.50", "classnames": "^2.2.6", "prismjs": "^1.19.0", "prop-types": "^15.7.2", diff --git a/docs/src/theme/CodeBlock/index.js b/docs/src/theme/CodeBlock/index.js old mode 100755 new mode 100644 index 66ed028..1401bbe --- a/docs/src/theme/CodeBlock/index.js +++ b/docs/src/theme/CodeBlock/index.js @@ -1,10 +1,12 @@ /** - * Copyright (c) 2017-present, Facebook, Inc. + * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ +/* eslint-disable jsx-a11y/no-noninteractive-tabindex */ + import React, { useEffect, useState, useRef } from "react"; import classnames from "classnames"; import Highlight, { defaultProps } from "prism-react-renderer"; @@ -13,9 +15,79 @@ import defaultTheme from "./dracula"; import Clipboard from "clipboard"; import rangeParser from "parse-numeric-range"; import useDocusaurusContext from "@docusaurus/useDocusaurusContext"; +import useThemeContext from "@theme/hooks/useThemeContext"; + import styles from "./styles.module.css"; const highlightLinesRangeRegex = /{([\d,-]+)}/; +const getHighlightDirectiveRegex = ( + languages = ["js", "jsBlock", "jsx", "python", "html"] +) => { + // supported types of comments + const comments = { + js: { + start: "\\/\\/", + end: "", + }, + jsBlock: { + start: "\\/\\*", + end: "\\*\\/", + }, + jsx: { + start: "\\{\\s*\\/\\*", + end: "\\*\\/\\s*\\}", + }, + python: { + start: "#", + end: "", + }, + html: { + start: "", + }, + }; + // supported directives + const directives = [ + "highlight-next-line", + "highlight-start", + "highlight-end", + ].join("|"); + // to be more reliable, the opening and closing comment must match + const commentPattern = languages + .map( + (lang) => + `(?:${comments[lang].start}\\s*(${directives})\\s*${comments[lang].end})` + ) + .join("|"); + // white space is allowed, but otherwise it should be on it's own line + return new RegExp(`^\\s*(?:${commentPattern})\\s*$`); +}; +// select comment styles based on language +const highlightDirectiveRegex = (lang) => { + switch (lang) { + case "js": + case "javascript": + case "ts": + case "typescript": + return getHighlightDirectiveRegex(["js", "jsBlock"]); + + case "jsx": + case "tsx": + return getHighlightDirectiveRegex(["js", "jsBlock", "jsx"]); + + case "html": + return getHighlightDirectiveRegex(["js", "jsBlock", "html"]); + + case "python": + case "py": + return getHighlightDirectiveRegex(["python"]); + + default: + // all comment types + return getHighlightDirectiveRegex(); + } +}; +const codeBlockTitleRegex = /title=".*"/; export default ({ children, className: languageClassName, metastring }) => { (typeof global !== "undefined" ? global : window).Prism = Prism; @@ -26,10 +98,29 @@ export default ({ children, className: languageClassName, metastring }) => { themeConfig: { prism = {} }, }, } = useDocusaurusContext(); - // const [showCopied, setShowCopied] = useState(false); + + const [showCopied, setShowCopied] = useState(false); + const [mounted, setMounted] = useState(false); + // The Prism theme on SSR is always the default theme but the site theme + // can be in a different mode. React hydration doesn't update DOM styles + // that come from SSR. Hence force a re-render after mounting to apply the + // current relevant styles. There will be a flash seen of the original + // styles seen using this current approach but that's probably ok. Fixing + // the flash will require changing the theming approach and is not worth it + // at this point. + useEffect(() => { + setMounted(true); + }, []); + const target = useRef(null); const button = useRef(null); let highlightLines = []; + let codeBlockTitle = ""; + + const { isDarkTheme } = useThemeContext(); + const lightModeTheme = prism.theme || defaultTheme; + const darkModeTheme = prism.darkTheme || lightModeTheme; + const prismTheme = isDarkTheme ? darkModeTheme : lightModeTheme; if (metastring && highlightLinesRangeRegex.test(metastring)) { const highlightLinesRange = metastring.match(highlightLinesRangeRegex)[1]; @@ -38,6 +129,13 @@ export default ({ children, className: languageClassName, metastring }) => { .filter((n) => n > 0); } + if (metastring && codeBlockTitleRegex.test(metastring)) { + codeBlockTitle = metastring + .match(codeBlockTitleRegex)[0] + .split("title=")[1] + .replace(/"+/g, ""); + } + useEffect(() => { let clipboard; @@ -61,53 +159,113 @@ export default ({ children, className: languageClassName, metastring }) => { language = prism.defaultLanguage; } - // const handleCopyCode = () => { - // window.getSelection().empty(); - // setShowCopied(true); + // only declaration OR directive highlight can be used for a block + let code = children.replace(/\n$/, ""); + if (highlightLines.length === 0 && language !== undefined) { + let range = ""; + const directiveRegex = highlightDirectiveRegex(language); + // go through line by line + const lines = children.replace(/\n$/, "").split("\n"); + let blockStart; + // loop through lines + for (let index = 0; index < lines.length; ) { + const line = lines[index]; + // adjust for 0-index + const lineNumber = index + 1; + const match = line.match(directiveRegex); + if (match !== null) { + const directive = match + .slice(1) + .reduce((final, item) => final || item, undefined); + switch (directive) { + case "highlight-next-line": + range += `${lineNumber},`; + break; - // setTimeout(() => setShowCopied(false), 2000); - // }; + case "highlight-start": + blockStart = lineNumber; + break; + + case "highlight-end": + range += `${blockStart}-${lineNumber - 1},`; + break; + + default: + break; + } + lines.splice(index, 1); + } else { + // lines without directives are unchanged + index += 1; + } + } + highlightLines = rangeParser.parse(range); + code = lines.join("\n"); + } + + const handleCopyCode = () => { + window.getSelection().empty(); + setShowCopied(true); + + setTimeout(() => setShowCopied(false), 2000); + }; return ( {({ className, style, tokens, getLineProps, getTokenProps }) => ( -
-
-            {tokens.map((line, i) => {
-              const lineProps = getLineProps({ line, key: i });
+        <>
+          {codeBlockTitle && (
+            
+ {codeBlockTitle} +
+ )} +
+ {/* */} +
+
+ {tokens.map((line, i) => { + if (line.length === 1 && line[0].content === "") { + line[0].content = "\n"; // eslint-disable-line no-param-reassign + } - if (highlightLines.includes(i + 1)) { - lineProps.className = `${lineProps.className} docusaurus-highlight-code-line`; - } + const lineProps = getLineProps({ line, key: i }); - return ( -
- {line.map((token, key) => ( - - ))} -
- ); - })} -
- {/* */} -
+ if (highlightLines.includes(i + 1)) { + lineProps.className = `${lineProps.className} docusaurus-highlight-code-line`; + } + + return ( +
+ {line.map((token, key) => ( + + ))} +
+ ); + })} + + + + )}
); diff --git a/docs/src/theme/CodeBlock/styles.module.css b/docs/src/theme/CodeBlock/styles.module.css old mode 100755 new mode 100644 index 83c9e16..dda5caf --- a/docs/src/theme/CodeBlock/styles.module.css +++ b/docs/src/theme/CodeBlock/styles.module.css @@ -1,31 +1,41 @@ /** - * Copyright (c) 2017-present, Facebook, Inc. + * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ -.codeBlock { - margin-bottom: 0; - overflow: hidden; - overflow-wrap: break-word; - white-space: pre-wrap; -} - -.codeBlockWrapper { +.codeBlockContent { position: relative; } -.codeBlockWrapper:hover > .copyButton { - visibility: visible; - opacity: 1; +.codeBlockTitle { + font-family: var(--ifm-font-family-monospace); + font-weight: bold; + padding: var(--ifm-pre-padding); + border-bottom: 1px solid var(--ifm-color-emphasis-200); + width: 100%; + border-top-left-radius: var(--ifm-global-radius); + border-top-right-radius: var(--ifm-global-radius); +} + +.codeBlock { + overflow: auto; + border-radius: var(--ifm-global-radius); +} + +.codeBlockWithTitle { + border-top-left-radius: 0; + border-top-right-radius: 0; + border-bottom-left-radius: var(--ifm-global-radius); + border-bottom-right-radius: var(--ifm-global-radius); } .copyButton { - background: rgb(1, 22, 39); - border: 1px solid rgb(214, 222, 235); + background: rgba(0, 0, 0, 0.3); + border: none; border-radius: var(--ifm-global-radius); - color: rgb(214, 222, 235); + color: var(--ifm-color-content); cursor: pointer; line-height: 12px; opacity: 0; @@ -38,3 +48,23 @@ transition: opacity 200ms ease-in-out, visibility 200ms ease-in-out, bottom 200ms ease-in-out; } + +.copyButtonWithTitle { + top: calc(var(--ifm-pre-padding)); +} + +.codeBlockTitle:hover + .codeBlockContent .copyButton, +.codeBlockContent:hover > .copyButton { + visibility: visible; + opacity: 1; +} + +.codeBlockLines { + font-family: var(--ifm-font-family-monospace); + font-size: inherit; + line-height: var(--ifm-pre-line-height); + white-space: pre; + float: left; + min-width: 100%; + padding: var(--ifm-pre-padding); +} diff --git a/docs/yarn.lock b/docs/yarn.lock index e4b4642..df214ca 100644 --- a/docs/yarn.lock +++ b/docs/yarn.lock @@ -901,7 +901,7 @@ resolved "https://registry.yarnpkg.com/@csstools/convert-colors/-/convert-colors-1.4.0.tgz#ad495dc41b12e75d588c6db8b9834f08fa131eb7" integrity sha512-5a6wqoJV/xEdbRNKVo6I4hO3VjyDq//8q2f9I6PBAvMesJHFauXDorcNCsr9RzvsZnaWi5NYCcfyqP1QeFHFbw== -"@docusaurus/core@^2.0.0-alpha.43": +"@docusaurus/core@^2.0.0-alpha.50": version "2.0.0-alpha.50" resolved "https://registry.yarnpkg.com/@docusaurus/core/-/core-2.0.0-alpha.50.tgz#4631efd9956a95aff29a6dfc9b3afffbcf6939e4" integrity sha512-lNvft+di325Ww3/S/kgbJmDmZF6OgJc3gM5zE8Lcl2Hqa6gZ6INIsGhnGxIX5Of1apliWGRVSojEF+mxf0BE1g== @@ -1026,14 +1026,6 @@ resolved "https://registry.yarnpkg.com/@docusaurus/plugin-google-gtag/-/plugin-google-gtag-2.0.0-alpha.50.tgz#4b69c23ef6818f96e6a13588c0e53f43ff264611" integrity sha512-fdW6ycdnLbWah31vx1e8/n4oopwmo5H89zzC9xVsJB0snBPQnMNbuZ82ob9TXYN57JcnK5bZeVPR/RYiAh0CWQ== -"@docusaurus/plugin-sitemap@^2.0.0-alpha.37": - version "2.0.0-alpha.37" - resolved "https://registry.yarnpkg.com/@docusaurus/plugin-sitemap/-/plugin-sitemap-2.0.0-alpha.37.tgz#174fa03e7184db2971a491849b27e33f577a26b4" - integrity sha512-IF4uk2dcm1TFm9Lu9OOOk1QaLEeoUb6LXvvweKP+Usuw4OFfUcEd7KBJi2/mDSm+Ud3EenoQOENywNbKiLSzvg== - dependencies: - "@docusaurus/types" "^2.0.0-alpha.37" - sitemap "^3.2.2" - "@docusaurus/plugin-sitemap@^2.0.0-alpha.50": version "2.0.0-alpha.50" resolved "https://registry.yarnpkg.com/@docusaurus/plugin-sitemap/-/plugin-sitemap-2.0.0-alpha.50.tgz#a9ab066286e96ef7ef51cdadb0429562d95514e0" @@ -1042,7 +1034,7 @@ "@docusaurus/types" "^2.0.0-alpha.50" sitemap "^3.2.2" -"@docusaurus/preset-classic@^2.0.0-alpha.43": +"@docusaurus/preset-classic@^2.0.0-alpha.50": version "2.0.0-alpha.50" resolved "https://registry.yarnpkg.com/@docusaurus/preset-classic/-/preset-classic-2.0.0-alpha.50.tgz#56a3ca2b7d7a01c410009309fed7d57a67bb8788" integrity sha512-SrVE95VNOY/X5Cc7ZL175TiCNjW50PEVoGVp/Y86/RAQEq7Wm5Bi32jFAcsjndbTOqKaEhRikSkqEQipbrF8GA== @@ -1081,15 +1073,6 @@ classnames "^2.2.6" docsearch.js "^2.6.3" -"@docusaurus/types@^2.0.0-alpha.37": - version "2.0.0-alpha.37" - resolved "https://registry.yarnpkg.com/@docusaurus/types/-/types-2.0.0-alpha.37.tgz#e14b8758cfefab5235c5ec190c489dd32cb7a8b8" - integrity sha512-t0bwfUQLEY6FGZX1OGhB29y7OQFAzqmNfP/UewUWOb/OiG5HBZ/r7OfwOH1dfmbjRJD99eHA3r4br8jwuvQfPg== - dependencies: - "@types/webpack" "^4.41.0" - commander "^4.0.1" - querystring "0.2.0" - "@docusaurus/types@^2.0.0-alpha.50": version "2.0.0-alpha.50" resolved "https://registry.yarnpkg.com/@docusaurus/types/-/types-2.0.0-alpha.50.tgz#0ea5dde077faa2e70a694eb7562bcdf21f35bc2e"