diff --git a/babel.config.cjs b/babel.config.cjs new file mode 100644 index 0000000..47fe5c9 --- /dev/null +++ b/babel.config.cjs @@ -0,0 +1,3 @@ +module.exports = { + presets: [require.resolve('@docusaurus/core/lib/babel/preset')], +} diff --git a/blog/2023-07-03-new-frontend.md b/blog/2023-07-03-new-frontend.md new file mode 100644 index 0000000..cb19c30 --- /dev/null +++ b/blog/2023-07-03-new-frontend.md @@ -0,0 +1,21 @@ +--- +slug: new-frontend +title: We launched a new frontend +authors: + name: chris48s + title: Shields.io Core Team + url: https://github.com/chris48s + image_url: https://avatars.githubusercontent.com/u/6025893 +tags: [] +--- + +Alongside the general visual refresh and improvements to look and feel, our new frontend has allowed us to address a number of long-standing feature requests and enhancements: + +- Clearer and more discoverable documentation for our [static](https://shields.io/badges/static-badge), dynamic [json](https://shields.io/badges/dynamic-json-badge)/[xml](https://shields.io/badges/dynamic-xml-badge)/[yaml](https://shields.io/badges/dynamic-yaml-badge) and [endpoint](https://shields.io/badges/endpoint-badge) badges +- Improved badge builder interface, with all optional query parameters included in the builder for each badge +- Each badge now has its own documentation page, which we can link to. e.g: [https://shields.io/badges/discord](https://shields.io/badges/discord) +- Light/dark mode themes +- Improved search +- Documentation for individual path and query parameters + +The new site also comes with big maintenance benefits for the core team. We rely heavily on [docusaurus](https://docusaurus.io/), [docusaurus-openapi](https://github.com/cloud-annotations/docusaurus-openapi), and [docusaurus-search-local](https://github.com/easyops-cn/docusaurus-search-local). This moves us to a mostly declarative setup, massively reducing the amount of custom frontend code we maintain ourselves. diff --git a/blog/2023-07-29-tag-filter.md b/blog/2023-07-29-tag-filter.md new file mode 100644 index 0000000..88abd32 --- /dev/null +++ b/blog/2023-07-29-tag-filter.md @@ -0,0 +1,19 @@ +--- +slug: tag-filter +title: Applying filters to GitHub Tag and Release badges +authors: + name: chris48s + title: Shields.io Core Team + url: https://github.com/chris48s + image_url: https://avatars.githubusercontent.com/u/6025893 +tags: [] +--- + +We recently shipped a feature which allows you to pass an arbitrary filter to the GitHub tag and release badges. The `filter` param can be used to apply a filter to the project's tag or release names before selecting the latest from the list. Two constructs are available: `*` is a wildcard matching zero or more characters, and if the pattern starts with a `!`, the whole pattern is negated. + +To give an example of how this might be useful, we create two types of tags on our GitHub repo: https://github.com/badges/shields/tags There are tags in the format `major.minor.patch` which correspond to our [NPM package releases](https://www.npmjs.com/package/badge-maker?activeTab=versions) and tags in the format `server-YYYY-MM-DD` that correspond to our [docker snapshot releases](https://registry.hub.docker.com/r/shieldsio/shields/tags?page=1&ordering=last_updated). + +In our case, this would allow us to make a badge that applies the filter `!server-*` to filter out the snapshot tags and just select the latest package tag. + +- ![tag badge without filter](https://img.shields.io/github/v/tag/badges/shields) - https://img.shields.io/github/v/tag/badges/shields +- ![tag badge with filter](https://img.shields.io/github/v/tag/badges/shields?filter=%21server-%2A) - https://img.shields.io/github/v/tag/badges/shields?filter=%21server-%2A diff --git a/blog/2023-11-29-simpleicons10.md b/blog/2023-11-29-simpleicons10.md new file mode 100644 index 0000000..2d49227 --- /dev/null +++ b/blog/2023-11-29-simpleicons10.md @@ -0,0 +1,14 @@ +--- +slug: simple-icons-10 +title: Simple Icons 10 +authors: + name: chris48s + title: Shields.io Core Team + url: https://github.com/chris48s + image_url: https://avatars.githubusercontent.com/u/6025893 +tags: [] +--- + +Logos on Shields.io are provided by SimpleIcons. We've recently upgraded to SimpleIcons 10. This release removes 45 icons. A full list of the removals can be found in the [release notes](https://github.com/simple-icons/simple-icons/releases/tag/10.0.0). + +Please remember that we are just consumers of SimpleIcons. Decisions about changes and removals are made by the [SimpleIcons](https://github.com/simple-icons/simple-icons) project. diff --git a/blog/2024-01-13-simpleicons11.md b/blog/2024-01-13-simpleicons11.md new file mode 100644 index 0000000..08daadc --- /dev/null +++ b/blog/2024-01-13-simpleicons11.md @@ -0,0 +1,21 @@ +--- +slug: simple-icons-11 +title: Simple Icons 11 +authors: + name: chris48s + title: Shields.io Core Team + url: https://github.com/chris48s + image_url: https://avatars.githubusercontent.com/u/6025893 +tags: [] +--- + +Logos on Shields.io are provided by SimpleIcons. We've recently upgraded to SimpleIcons 11. This release removes the following 4 icons: + +- Babylon.js +- Hulu +- Pepsi +- Uno + +More details can be found in the [release notes](https://github.com/simple-icons/simple-icons/releases/tag/11.0.0). + +Please remember that we are just consumers of SimpleIcons. Decisions about changes and removals are made by the [SimpleIcons](https://github.com/simple-icons/simple-icons) project. diff --git a/categories/.gitkeep b/categories/.gitkeep new file mode 100644 index 0000000..473a0f4 diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..3f6c657 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,13 @@ +--- +sidebar_position: 1 +--- + +# Intro + +Shields.io is a service for concise, consistent, and legible badges, which can easily be included in GitHub readmes or any other web page. The service supports dozens of continuous integration services, package registries, distributions, app stores, social networks, code coverage services, and code analysis services. It is used by some of the world's most popular open-source projects. + +Browse a [complete list of badges](/badges) and locate a particular badge by using the search bar or by browsing the categories. + +Use the builder to fill in required path parameters for that badge type (like your username or repo) and optionally customize (label, colors etc.). And it's ready for use! Copy your badge url or code snippet, which can then be added to places like your GitHub readme files or other web pages. + +![screenshot of the badge builder](/img/builder.png) diff --git a/docs/logos.md b/docs/logos.md new file mode 100644 index 0000000..4df5400 --- /dev/null +++ b/docs/logos.md @@ -0,0 +1,37 @@ +--- +sidebar_position: 2 +--- + +# Logos + +## SimpleIcons + +We support a wide range of logos via [SimpleIcons](https://simpleicons.org/). All simple-icons are referenced using icon slugs. e.g: + +![](https://img.shields.io/npm/v/npm.svg?logo=nodedotjs) - https://img.shields.io/npm/v/npm.svg?logo=nodedotjs + +You can click the icon title on simple-icons to copy the slug or they can be found in the slugs.md file in the simple-icons repository. NB - the Simple Icons site and slugs.md page may at times contain new icons that haven't yet been pulled into Shields.io yet. More information on how and when we incorporate icon updates can be found [here](https://github.com/badges/shields/discussions/5369). + +## Shields logos + +We also maintain a small number of custom logos for a handful of services: https://github.com/badges/shields/tree/master/logo They can also be referenced by name and take preference to SimpleIcons e.g: + +![](https://img.shields.io/npm/v/npm.svg?logo=npm) - https://img.shields.io/npm/v/npm.svg?logo=npm + +## Custom Logos + +Any custom logo can be passed in a URL parameter by base64 encoding it. e.g: + +![](https://img.shields.io/badge/play-station-blue.svg?logo=data:image/svg%2bxml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZlcnNpb249IjEiIHdpZHRoPSI2MDAiIGhlaWdodD0iNjAwIj48cGF0aCBkPSJNMTI5IDExMWMtNTUgNC05MyA2Ni05MyA3OEwwIDM5OGMtMiA3MCAzNiA5MiA2OSA5MWgxYzc5IDAgODctNTcgMTMwLTEyOGgyMDFjNDMgNzEgNTAgMTI4IDEyOSAxMjhoMWMzMyAxIDcxLTIxIDY5LTkxbC0zNi0yMDljMC0xMi00MC03OC05OC03OGgtMTBjLTYzIDAtOTIgMzUtOTIgNDJIMjM2YzAtNy0yOS00Mi05Mi00MmgtMTV6IiBmaWxsPSIjZmZmIi8+PC9zdmc+) - https://img.shields.io/badge/play-station-blue.svg?logo=data:image/svg%2bxml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZlcnNpb249IjEiIHdpZHRoPSI2MDAiIGhlaWdodD0iNjAwIj48cGF0aCBkPSJNMTI5IDExMWMtNTUgNC05MyA2Ni05MyA3OEwwIDM5OGMtMiA3MCAzNiA5MiA2OSA5MWgxYzc5IDAgODctNTcgMTMwLTEyOGgyMDFjNDMgNzEgNTAgMTI4IDEyOSAxMjhoMWMzMyAxIDcxLTIxIDY5LTkxbC0zNi0yMDljMC0xMi00MC03OC05OC03OGgtMTBjLTYzIDAtOTIgMzUtOTIgNDJIMjM2YzAtNy0yOS00Mi05Mi00MmgtMTV6IiBmaWxsPSIjZmZmIi8+PC9zdmc+ + +## logoColor parameter + +The `logoColor` param can be used to set the color of the logo. Hex, rgb, rgba, hsl, hsla and css named colors can all be used. For SimpleIcons named logos (which are monochrome), the color will be applied to the SimpleIcons logo. + +- ![](https://img.shields.io/badge/logo-javascript-blue?logo=javascript) - https://img.shields.io/badge/logo-javascript-blue?logo=javascript +- ![](https://img.shields.io/badge/logo-javascript-blue?logo=javascript&logoColor=f5f5f5) - https://img.shields.io/badge/logo-javascript-blue?logo=javascript&logoColor=f5f5f5 + +In the case where Shields hosts a custom multi-colored logo, if the `logoColor` param is passed, the corresponding SimpleIcons logo will be substituted and colored. + +- ![](https://img.shields.io/badge/logo-gitlab-blue?logo=gitlab) - https://img.shields.io/badge/logo-gitlab-blue?logo=gitlab +- ![](https://img.shields.io/badge/logo-gitlab-blue?logo=gitlab&logoColor=white) - https://img.shields.io/badge/logo-gitlab-blue?logo=gitlab&logoColor=white diff --git a/docs/static-badges.md b/docs/static-badges.md new file mode 100644 index 0000000..583356f --- /dev/null +++ b/docs/static-badges.md @@ -0,0 +1,13 @@ +# Static Badges + +It is possible to use shields.io to make a wide variety of badges displaying static text and/or logos. For example: + +- ![any text you like](https://img.shields.io/badge/any%20text-you%20like-blue) - https://img.shields.io/badge/any%20text-you%20like-blue +- ![just the message](https://img.shields.io/badge/just%20the%20message-8A2BE2) - https://img.shields.io/badge/just%20the%20message-8A2BE2 +- !['for the badge' style](https://img.shields.io/badge/%27for%20the%20badge%27%20style-20B2AA?style=for-the-badge) - https://img.shields.io/badge/%27for%20the%20badge%27%20style-20B2AA?style=for-the-badge +- ![with a logo](https://img.shields.io/badge/with%20a%20logo-grey?style=for-the-badge&logo=javascript) - https://img.shields.io/badge/with%20a%20logo-grey?style=for-the-badge&logo=javascript + +For more info, see: + +- [Static badge builder](/badges/static-badge), including full documentation of styles and parameters +- [Logos](/docs/logos) diff --git a/docusaurus.config.cjs b/docusaurus.config.cjs new file mode 100644 index 0000000..b9b76e4 --- /dev/null +++ b/docusaurus.config.cjs @@ -0,0 +1,134 @@ +const lightCodeTheme = require('prism-react-renderer').themes.github +const darkCodeTheme = require('prism-react-renderer').themes.dracula +const stripCodeBlockLinks = require('./src/plugins/strip-code-block-links') + +/** @type {import('@docusaurus/types').Config} */ +const config = { + title: 'Shields.io', + tagline: 'Concise, consistent, and legible badges', + url: 'https://shields.io', + baseUrl: '/', + onBrokenLinks: 'throw', + onBrokenMarkdownLinks: 'warn', + favicon: 'img/favicon.ico', + organizationName: 'badges', + projectName: 'shields', + + themes: [ + [ + require.resolve('@easyops-cn/docusaurus-search-local'), + /** @type {import("@easyops-cn/docusaurus-search-local").PluginOptions} */ + ({ + hashed: true, + indexPages: true, + }), + ], + ], + + markdown: { + mdx1Compat: { + comments: true, + admonitions: true, + headingIds: true, + }, + }, + + presets: [ + [ + 'docusaurus-preset-openapi', + /** @type {import('docusaurus-preset-openapi').Options} */ + ({ + docs: { + sidebarPath: require.resolve('./sidebars.cjs'), + editUrl: 'https://github.com/badges/shields/tree/master/frontend', + }, + blog: { + showReadingTime: true, + editUrl: 'https://github.com/badges/shields/tree/master/frontend', + }, + theme: { + customCss: require.resolve('./src/css/custom.css'), + }, + api: { + path: 'categories', + routeBasePath: 'badges', + rehypePlugins: [stripCodeBlockLinks], + }, + }), + ], + ], + + themeConfig: + /** @type {import('docusaurus-preset-openapi').ThemeConfig} */ + ({ + languageTabs: [], + navbar: { + title: 'Shields.io', + logo: { + alt: 'Shields Logo', + src: 'img/logo.png', + }, + items: [ + { to: '/badges', label: 'Badges', position: 'left' }, + { + to: '/docs', + label: 'Documentation', + position: 'left', + }, + { to: '/community', label: 'Community', position: 'left' }, + { to: '/blog', label: 'Blog', position: 'left' }, + { + href: 'https://github.com/badges/shields', + label: 'GitHub', + position: 'right', + }, + ], + }, + footer: { + style: 'dark', + links: [ + { + title: 'Community', + items: [ + { + label: 'GitHub', + href: 'https://github.com/badges/shields', + }, + { + label: 'Open Collective', + href: 'https://opencollective.com/shields', + }, + { + label: 'Discord', + href: 'https://discord.gg/HjJCwm5', + }, + { + label: 'Awesome Badges', + href: 'https://github.com/badges/awesome-badges', + }, + ], + }, + { + title: 'Stats', + items: [ + { + label: 'Service Status', + href: 'https://stats.uptimerobot.com/PjXogHB5p', + }, + { + label: 'Metrics dashboard', + href: 'https://metrics.shields.io/', + }, + ], + }, + ], + copyright: `Copyright © ${new Date().getFullYear()} Shields.io. Built with Docusaurus.`, + }, + prism: { + theme: lightCodeTheme, + darkTheme: darkCodeTheme, + }, + }), +} + +module.exports = config diff --git a/package.json b/package.json new file mode 100644 index 0000000..4f2b34c --- /dev/null +++ b/package.json @@ -0,0 +1,16 @@ +{ + "name": "badge-frontend", + "version": "0.0.0", + "description": "Shields.io frontend", + "private": true, + "homepage": "https://shields.io", + "license": "CC0-1.0", + "repository": { + "type": "git", + "url": "git+https://github.com/badges/shields.git", + "directory": "frontend" + }, + "scripts": { + "test": "echo 'Run tests from parent dir'; false" + } +} diff --git a/sidebars.cjs b/sidebars.cjs new file mode 100644 index 0000000..1d0254f --- /dev/null +++ b/sidebars.cjs @@ -0,0 +1,31 @@ +/** + * Creating a sidebar enables you to: + * - create an ordered group of docs + * - render a sidebar for each doc of that group + * - provide next/previous navigation + * + * The sidebars can be generated from the filesystem, or explicitly defined here. + * + * Create as many sidebars as you want. + */ + +// @ts-check + +/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ +const sidebars = { + // By default, Docusaurus generates a sidebar from the docs folder structure + tutorialSidebar: [{ type: 'autogenerated', dirName: '.' }], + + // But you can create a sidebar manually + /* + tutorialSidebar: [ + { + type: 'category', + label: 'Tutorial', + items: ['hello'], + }, + ], + */ +} + +module.exports = sidebars diff --git a/src/components/homepage-features.js b/src/components/homepage-features.js new file mode 100644 index 0000000..a320f1e --- /dev/null +++ b/src/components/homepage-features.js @@ -0,0 +1,108 @@ +import React from 'react' +import clsx from 'clsx' +import styles from './homepage-features.module.css' + +const FeatureList = [ + { + title: 'Dynamic badges', + description: ( + <> + build:passing +
+ Show metrics for your project. We've got badges for hundreds of + services. + + ), + }, + { + title: 'Static Badges', + description: ( + <> + Create a badge with +
+ any text you like + + ), + }, + { + title: 'Badge-Maker NPM library', + description: ( + <> + Render badges in your own application using our{' '} + + NPM library + +
+ npm install badge-maker + + ), + }, + { + title: 'Host your own instance', + description: ( + <> + Host a shields instance behind your firewall with our{' '} + + docker image + +
+ docker pull shieldsio/shields + + ), + }, + { + title: 'Love Shields?', + description: ( + <> + Please consider{' '} + + donating + {' '} + to sustain our activities + + ), + }, +] + +function Feature({ title, description }) { + return ( +
+
+

{title}

+

{description}

+
+
+ ) +} + +export default function HomepageFeatures() { + return ( +
+
+
+ {FeatureList.map((props, idx) => ( + + ))} +
+
+
+ ) +} diff --git a/src/components/homepage-features.module.css b/src/components/homepage-features.module.css new file mode 100644 index 0000000..aa5859e --- /dev/null +++ b/src/components/homepage-features.module.css @@ -0,0 +1,11 @@ +.features { + display: flex; + align-items: center; + padding: 2rem 0; + width: 100%; +} + +.featureSvg { + height: 200px; + width: 200px; +} diff --git a/src/css/custom.css b/src/css/custom.css new file mode 100644 index 0000000..cf4f03c --- /dev/null +++ b/src/css/custom.css @@ -0,0 +1,52 @@ +/** + * Any CSS included here will be global. The classic template + * bundles Infima by default. Infima is a CSS framework designed to + * work well for content-centric websites. + */ + +/* You can override the default Infima variables here. */ +:root { + --ifm-color-primary: #25c2a0; + --ifm-color-primary-dark: rgb(33, 175, 144); + --ifm-color-primary-darker: rgb(31, 165, 136); + --ifm-color-primary-darkest: rgb(26, 136, 112); + --ifm-color-primary-light: rgb(70, 203, 174); + --ifm-color-primary-lighter: rgb(102, 212, 189); + --ifm-color-primary-lightest: rgb(146, 224, 208); + --ifm-code-font-size: 95%; +} + +.docusaurus-highlight-code-line { + background-color: rgba(0, 0, 0, 0.1); + display: block; + margin: 0 calc(-1 * var(--ifm-pre-padding)); + padding: 0 var(--ifm-pre-padding); +} + +html[data-theme="dark"] .docusaurus-highlight-code-line { + background-color: rgba(0, 0, 0, 0.3); +} + +.opencollective-image { + color-scheme: initial; +} + +/* +TODO: remove these three styles when +we can upgrade to docusaurus-theme-openapi@0.6.5 +*/ + +input[type="text"], :not(#fakeID#fakeId#fakeID) select { + border-color: var(--ifm-color-primary-lightest); + border-style: solid; + border-width: 1px; +} + +div.api-code-tab-group { + justify-content: center; + flex-wrap: wrap; +} + +div.api-code-tab-group button.api-code-tab { + width: unset; +} diff --git a/src/pages/community.md b/src/pages/community.md new file mode 100644 index 0000000..54db53d --- /dev/null +++ b/src/pages/community.md @@ -0,0 +1,67 @@ +# Community + +Shields.io is possible thanks to the people and companies who donate money, services or time to keep the project running. + +## Sponsors + +❤️ These companies help us by donating their services to shields: + + + +💵 These organisations help keep shields running by donating on OpenCollective. Your organisation can support this project by becoming a sponsor . Your logo will show up here with a link to your website. + +

+ +

+ +## Backers + +💵 Thank you to all our backers who help keep shields running by donating on OpenCollective. You can support this project by becoming a backer. + +

+ + +

+ +## Contributors + +🙏 This project exists thanks to all the nice people who contribute their time to work on the project. + +

+ +

+ +✨ Shields is helped by these companies which provide a free plan for their product or service: + + diff --git a/src/pages/index.js b/src/pages/index.js new file mode 100644 index 0000000..e20aca5 --- /dev/null +++ b/src/pages/index.js @@ -0,0 +1,39 @@ +import React from 'react' +import clsx from 'clsx' +import Layout from '@theme/Layout' +import Link from '@docusaurus/Link' +import useDocusaurusContext from '@docusaurus/useDocusaurusContext' +import HomepageFeatures from '../components/homepage-features' +import styles from './index.module.css' + +function HomepageHeader() { + const { siteConfig } = useDocusaurusContext() + return ( +
+
+

{siteConfig.title}

+

{siteConfig.tagline}

+
+ + Get started + +
+
+
+ ) +} + +export default function Home() { + const { siteConfig } = useDocusaurusContext() + return ( + + +
+ +
+
+ ) +} diff --git a/src/pages/index.module.css b/src/pages/index.module.css new file mode 100644 index 0000000..dc85b3f --- /dev/null +++ b/src/pages/index.module.css @@ -0,0 +1,23 @@ +/** + * CSS files with the .module.css suffix will be treated as CSS modules + * and scoped locally. + */ + +.heroBanner { + padding: 4rem 0; + text-align: center; + position: relative; + overflow: hidden; +} + +@media screen and (max-width: 966px) { + .heroBanner { + padding: 2rem; + } +} + +.buttons { + display: flex; + align-items: center; + justify-content: center; +} diff --git a/src/plugins/strip-code-block-links.js b/src/plugins/strip-code-block-links.js new file mode 100644 index 0000000..278ac54 --- /dev/null +++ b/src/plugins/strip-code-block-links.js @@ -0,0 +1,30 @@ +const { visit } = require('unist-util-visit') + +function stripCodeBlockLinks() { + /* + Docusaurus 3 uses [remark-gfm](https://github.com/remarkjs/remark-gfm) + One of the "features" of remark-gfm is that it automatically looks for URLs + and email addresses, and automatically wraps them in tags. + + This happens even if the URL is inside a block. + This behaviour is + a) mostly unhelpful and + b) non-configurable + + This plugin removes tags which appear inside a block. + */ + return tree => { + visit(tree, ['mdxJsxTextElement', 'mdxJsxFlowElement', 'element'], node => { + if (node.name === 'code' || node.tagName === 'code') { + const links = node.children.filter(child => child.tagName === 'a') + links.forEach(link => { + const linkText = link.children.map(child => child.value).join('') + const linkIndex = node.children.indexOf(link) + node.children.splice(linkIndex, 1, { type: 'text', value: linkText }) + }) + } + }) + } +} + +module.exports = stripCodeBlockLinks diff --git a/src/theme/ApiDemoPanel/Curl/index.js b/src/theme/ApiDemoPanel/Curl/index.js new file mode 100644 index 0000000..ee6afd2 --- /dev/null +++ b/src/theme/ApiDemoPanel/Curl/index.js @@ -0,0 +1,300 @@ +import React, { useRef, useState, useEffect } from 'react' +import useDocusaurusContext from '@docusaurus/useDocusaurusContext' +import clsx from 'clsx' +import codegen from 'postman-code-generators' +import { Highlight } from 'prism-react-renderer' +import { useTypedSelector } from '@theme/ApiDemoPanel/hooks' +import buildPostmanRequest from '@theme/ApiDemoPanel/buildPostmanRequest' +import FloatingButton from '@theme/ApiDemoPanel/FloatingButton' +import styles from 'docusaurus-theme-openapi/lib/theme/ApiDemoPanel/Curl/styles.module.css' + +const languageSet = [ + { + tabName: 'cURL', + highlight: 'bash', + language: 'curl', + variant: 'curl', + options: { + longFormat: false, + followRedirect: true, + trimRequestBody: true, + }, + }, + { + tabName: 'Node', + highlight: 'javascript', + language: 'nodejs', + variant: 'axios', + options: { + ES6_enabled: true, + followRedirect: true, + trimRequestBody: true, + }, + }, + { + tabName: 'Go', + highlight: 'go', + language: 'go', + variant: 'native', + options: { + followRedirect: true, + trimRequestBody: true, + }, + }, + { + tabName: 'Python', + highlight: 'python', + language: 'python', + variant: 'requests', + options: { + followRedirect: true, + trimRequestBody: true, + }, + }, +] +const languageTheme = { + plain: { + color: 'var(--ifm-code-color)', + }, + styles: [ + { + types: ['inserted', 'attr-name'], + style: { + color: 'var(--openapi-code-green)', + }, + }, + { + types: ['string', 'url'], + style: { + color: 'var(--openapi-code-green)', + }, + }, + { + types: ['builtin', 'char', 'constant', 'function'], + style: { + color: 'var(--openapi-code-blue)', + }, + }, + { + types: ['punctuation', 'operator'], + style: { + color: 'var(--openapi-code-dim)', + }, + }, + { + types: ['class-name'], + style: { + color: 'var(--openapi-code-orange)', + }, + }, + { + types: ['tag', 'arrow', 'keyword'], + style: { + color: 'var(--openapi-code-purple)', + }, + }, + { + types: ['boolean'], + style: { + color: 'var(--openapi-code-red)', + }, + }, + ], +} + +function getBaseUrl() { + /* + This is a special case for production. + + We want to be able to build the front end with no value set for + `BASE_URL` so that staging, prod and self hosting users + can all use the same docker image. + + When deployed to staging, we want the frontend on + https://staging.shields.io/ to generate badges with the base + https://staging.shields.io/ + (and we want similar behaviour for users hosting their own instance) + + When we promote to production we want https://shields.io/ and + https://www.shields.io/ to both generate badges with the base + https://img.shields.io/ + + For local dev, we can deal with setting the api and front-end + being on different ports using the BASE_URL env var + */ + const { protocol, hostname, port } = window.location + if (['shields.io', 'www.shields.io'].includes(hostname)) { + return 'https://img.shields.io' + } + if (!port) { + return `${protocol}//${hostname}` + } + return `${protocol}//${hostname}:${port}` +} + +function getServer() { + return { + url: getBaseUrl(), + variables: {}, + } +} + +function Curl({ postman, codeSamples }) { + // TODO: match theme for vscode. + const { siteConfig } = useDocusaurusContext() + const [copyText, setCopyText] = useState('Copy') + const contentType = useTypedSelector(state => state.contentType.value) + const accept = useTypedSelector(state => state.accept.value) + const server = useTypedSelector(state => state.server.value) || getServer() + const body = useTypedSelector(state => state.body) + const pathParams = useTypedSelector(state => state.params.path) + const queryParams = useTypedSelector(state => state.params.query) + const cookieParams = useTypedSelector(state => state.params.cookie) + const headerParams = useTypedSelector(state => state.params.header) + const auth = useTypedSelector(state => state.auth) + + const langs = [ + ...(siteConfig?.themeConfig?.languageTabs ?? languageSet), + ...codeSamples, + ] + const [language, setLanguage] = useState(langs[0]) + const [codeText, setCodeText] = useState('') + useEffect(() => { + const postmanRequest = buildPostmanRequest(postman, { + queryParams, + pathParams, + cookieParams, + contentType, + accept, + headerParams, + body, + server, + auth, + }) + if (language && !!language.options) { + codegen.convert( + language.language, + language.variant, + postmanRequest, + language.options, + (error, snippet) => { + if (error) { + return + } + + setCodeText(snippet) + }, + ) + } else if (language && !!language.source) { + setCodeText( + language.source.replace('$url', postmanRequest.url.toString()), + ) + } else { + setCodeText('') + } + }, [ + accept, + body, + contentType, + cookieParams, + headerParams, + language, + pathParams, + postman, + queryParams, + server, + auth, + ]) + const ref = useRef(null) + + const handleCurlCopy = () => { + setCopyText('Copied') + setTimeout(() => { + setCopyText('Copy') + }, 2000) + + if (ref.current?.innerText) { + navigator.clipboard.writeText(ref.current.innerText) + } + } + + if (language === undefined) { + return null + } + + return ( + <> +
+ {langs.map(lang => ( + + ))} +
+ + + {({ className, tokens, getLineProps, getTokenProps }) => ( + +
+              
+                {tokens.map((line, i) => (
+                  // this  does have a key but eslint fails
+                  // to detect it because it is an arg to getLineProps()
+                  // eslint-disable-next-line react/jsx-key
+                  
+                    {line.map((token, key) => {
+                      if (token.types.includes('arrow')) {
+                        token.types = ['arrow']
+                      }
+
+                      return (
+                        // this  does have a key but eslint fails
+                        // to detect it because it is an arg to getLineProps()
+                        // eslint-disable-next-line react/jsx-key
+                        
+                      )
+                    })}
+                    {'\n'}
+                  
+                ))}
+              
+            
+
+ )} +
+ + ) +} + +export default Curl diff --git a/src/theme/ApiDemoPanel/Response/index.js b/src/theme/ApiDemoPanel/Response/index.js new file mode 100644 index 0000000..898e657 --- /dev/null +++ b/src/theme/ApiDemoPanel/Response/index.js @@ -0,0 +1,66 @@ +import React from 'react' +import { useTypedDispatch, useTypedSelector } from '@theme/ApiDemoPanel/hooks' +import FloatingButton from '@theme/ApiDemoPanel/FloatingButton' +import { clearResponse } from 'docusaurus-theme-openapi/lib/theme/ApiDemoPanel/Response/slice' + +function formatXml(xml) { + const tab = ' ' + let formatted = '' + let indent = '' + xml.split(/>\s* { + if (node.match(/^\/\w/)) { + // decrease indent by one 'tab' + indent = indent.substring(tab.length) + } + + formatted += `${indent}<${node}>\r\n` + + if (node.match(/^]*[^/]$/)) { + // increase indent + indent += tab + } + }) + return formatted.substring(1, formatted.length - 3) +} + +function Response() { + const response = useTypedSelector(state => state.response.value) + const dispatch = useTypedDispatch() + + if (response === undefined) { + return null + } + + let prettyResponse = response + + try { + prettyResponse = JSON.stringify(JSON.parse(response), null, 2) + } catch { + if (response.startsWith(' dispatch(clearResponse())}> + {(response.startsWith(' + )) || ( +
+          {prettyResponse || 'No Response'}
+        
+ )} + + ) +} + +export default Response diff --git a/src/theme/DocPaginator/index.js b/src/theme/DocPaginator/index.js new file mode 100644 index 0000000..7096842 --- /dev/null +++ b/src/theme/DocPaginator/index.js @@ -0,0 +1,3 @@ +export default function DocPaginator(props) { + return '' +} diff --git a/static/.nojekyll b/static/.nojekyll new file mode 100644 index 0000000..473a0f4 diff --git a/static/img/builder.png b/static/img/builder.png new file mode 100644 index 0000000..4316ba2 Binary files /dev/null and b/static/img/builder.png differ diff --git a/static/img/favicon.ico b/static/img/favicon.ico new file mode 100644 index 0000000..cf73624 Binary files /dev/null and b/static/img/favicon.ico differ diff --git a/static/img/logo.png b/static/img/logo.png new file mode 100644 index 0000000..0e33bbd Binary files /dev/null and b/static/img/logo.png differ