diff --git a/docs/apps/build-plane-app.mdx b/docs/apps/build-plane-app.mdx new file mode 100644 index 0000000..8baf13a --- /dev/null +++ b/docs/apps/build-plane-app.mdx @@ -0,0 +1,379 @@ +--- +title: Build a Plane App (BYOA) +sidebar_label: App development +description: Step-by-step development guide to build and integrate an app with Plane using OAuth-based authentication and authorization workflow. + +siderbar_position: 2 +--- + +import Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; + +{frontMatter.description && ( + +

{frontMatter.description}

+)} + +:::info +Plane apps are currently in **Beta**. Please send any feedback to support@plane.so. +::: + +## Introduction +Plane apps seamlessly integrate tools and services with Plane so you can +use them without ever leaving your Workspace. Apps are conveniently available +from our [marketplace](https://plane.so/marketplace/integrations), helping you +stay focused and productive. + +## Why Build a Plane App? + +**Stop doing manual work.** +Plane integrations eliminate repetitive tasks like copying updates between +tools, creating work items from support tickets, and generating status reports. +Instead of spending hours on administrative work, let your app handle it +automatically. + +**Connect everything you already use.** +Your team probably uses dozens of different tools. Plane apps create a unified +workflow by connecting your favorite CRM, time tracking app, CI/CD pipelines, +communication tools, and more, together into Plane. One change in Plane can +trigger updates across your entire tech stack. + +**Build exactly what you need.** +Unlike rigid SaaS platforms, Plane's open core nature means you can create +integrations that fit your specific workflow. + +## Prerequisites + +- A [Plane](https://app.plane.so) workspace +- Admin access to your workspace settings +- Familiarity with OAuth 2.0 concepts (authorization code flow) +- A backend server to handle OAuth token exchange + +## High-Level Workflow + +1. [Register your app on Plane developer portal](/apps/build-plane-app/#registering-your-app) +2. [Implement OAuth flow](/apps/build-plane-app#implement-oauth-flow) +3. [Obtain and store access tokens securely](/apps/build-plane-app#obtain-and-store-access-tokens-securely) +4. [Make authenticated API requests to Plane](/apps/build-plane-app#make-authenticated-api-requests-to-plane) +5. [Handle token refresh](/apps/build-plane-app#handle-token-refresh) + +## Registering Your App + +To build an OAuth application with Plane: + +1. Navigate to `https://app.plane.so//settings/applications/`. +2. Click on the **Build your own** button. +3. Fill out the form with the required details: + + - **Redirect URIs**: Provide the URIs where Plane will send the authorization code after the user consents to the app. + - **Contact Details**: Add your email or other contact information. + - **Setup URL(Optional)**: Provide the URL that users will be redirected to when they click "Install App" from the marketplace. This URL should initiate the OAuth flow for your application. + - **Webhook URL Endpoint(Optional)**: Your service's webhook endpoint. Plane will send an HTTP `POST` request to this endpoint upon every change to the workspace in which your app was installed. + - **Organization Details(Optional)**: Optionally include your contact email, privacy policy URL, terms of service URL, and any other relevant information. This helps Plane validate and approve your application should you choose to [list in the marketplace](#listing-your-app-on-plane-marketplace). + +4. If you're building an agent (with or without using Plane's ADK) capable of performing operations when assigned or mentioned, enable the **Is Mentionable** checkbox during app creation. +5. Once the app is created, securely store the generated **Client ID** and **Client Secret**. You will need these credentials to interact with Plane's API during the OAuth flow and for making authenticated API requests. + +## Implement OAuth Flow + +### Generating Consent URL (Optional) + +This step is optional. This is needed only if the app should be installed from outside Plane's environment, the developer needs to generate the consent URL using the client ID generated during their app creation flow. + +If this flow needs to be triggered from Plane marketplace as well, then provide the URL in "Setup URL" field on application create screen to redirect the user from marketplace on clicking "Install App" button. + +Below are sample implementations: + + + + ```python + import os + from urllib.parse import urlencode + params = { + "client_id": os.getenv("PLANE_CLIENT_ID"), + "response_type": "code", + "redirect_uri": os.getenv("PLANE_REDIRECT_URI"), + } + consent_url = f"https://api.plane.so/auth/o/authorize-app/?{urlencode(params)}" + ``` + + + ```typescript + import { URLSearchParams } from 'url'; + const params = new URLSearchParams({ + client_id: process.env.PLANE_CLIENT_ID!, + response_type: "code", + redirect_uri: process.env.PLANE_REDIRECT_URI!, + }); + const consentUrl = `https://api.plane.so/auth/o/authorize-app/?${params.toString()}`; + ``` + + + +There are two types of authenticated actions your application can perform: + +1. **User-authorized actions**: Actions performed on behalf of a user after they grant permission to your app via OAuth. +2. **App-authorized actions**: Actions that the app can perform independently within the workspace where it is installed (such as responding to webhooks or automation triggers). + +For both these flows, Plane will make a GET request to the Redirect URI with parameters as mentioned in the following sections. + +We will describe how to configure and use each type in the following sections. + +### App-Authorized Actions (Client Credentials Flow) + +When the app is installed, Plane will send an `app_installation_id` as part of the callback to the Redirect URI provided during consent URL generation. You can use this `app_installation_id` to request a bot token for your app. + +Plane will make a GET request to the Redirect URI with below parameters: + +| Parameter | Description | +|-----------|-------------| +| app_installation_id | The unique identifier for the app installation in the workspace | + +#### Examples + + + + ```python + import base64 + import requests + client_id = "your_client_id" + client_secret = "your_client_secret" + basic_auth = base64.b64encode(f"{client_id}:{client_secret}".encode()).decode() + payload = { + "grant_type": "client_credentials", + "app_installation_id": app_installation_id + } + response = requests.post( + url="https://api.plane.so/auth/o/token/", + headers={"Authorization": f"Basic {basic_auth}", "Content-Type": "application/x-www-form-urlencoded"}, + data=payload + ) + response_data = response.json() + bot_token = response_data['access_token'] + expires_in = response_data["expires_in"] + ``` + + + ```typescript + import axios from 'axios'; + const clientId = "your_client_id"; + const clientSecret = "your_client_secret"; + const basicAuth = Buffer.from(`${clientId}:${clientSecret}`).toString('base64'); + const payload = { + grant_type: "client_credentials", + app_installation_id: appInstallationId + }; + const response = await axios.post( + "https://api.plane.so/auth/o/token/", + payload, + { + headers: { + Authorization: `Basic ${basicAuth}`, + "Content-Type": "application/x-www-form-urlencoded" + } + } + ); + const responseData = response.data; + const botToken = responseData.access_token; + const expiresIn = responseData.expires_in; + ``` + + + +### User-Authorized Actions (Authorization Code Flow) + +In this flow, your app exchanges the `code` received as a query parameter on the callback (to your Redirect URI) for an access token and refresh token. The access token is short-lived and must be refreshed using the refresh token when it expires. Both tokens should be securely stored. + +Plane will make a GET request to the Redirect URI with below parameters: + +| Parameter | Description | Required | +|-----------|-------------|----------| +| code | The authorization code that can be exchanged for an access token | Yes | +| state | The state parameter that was passed in the authorization request | No | + +#### Examples + + + + ```python + import requests + code = "authorization_code_from_callback" + client_id = "your_client_id" + client_secret = "your_client_secret" + redirect_uri = "your_redirect_uri" + payload = { + "grant_type": "authorization_code", + "code": code, + "client_id": client_id, + "client_secret": client_secret, + "redirect_uri": redirect_uri + } + response = requests.post( + url="https://api.plane.so/auth/o/token/", + headers={"Content-Type": "application/x-www-form-urlencoded"}, + data=payload + ) + response_data = response.json() + access_token = response_data["access_token"] + refresh_token = response_data["refresh_token"] + expires_in = response_data["expires_in"] + ``` + + + ```typescript + import axios from 'axios'; + const code = "authorization_code_from_callback"; + const clientId = "your_client_id"; + const clientSecret = "your_client_secret"; +const redirectUri = "your_redirect_uri"; +const payload = { + grant_type: "authorization_code", + code: code, + client_id: clientId, + client_secret: clientSecret, + redirect_uri: redirectUri +}; +const response = await axios.post( + "https://api.plane.so/auth/o/token/", + payload, + { + headers: { + "Content-Type": "application/x-www-form-urlencoded" + } + } +); +const responseData = response.data; +const accessToken = responseData.access_token; +const refreshToken = responseData.refresh_token; +const expiresIn = responseData.expires_in; +``` + + + +### Fetching App Installation Details + +In both user-authorized and app-authorized flows, the `app_installation_id` identifies the app's installation within a specific workspace. It is recommended that developers fetch workspace details after OAuth is successfully completed. Plane provides an `app-installation` endpoint that works with both types of tokens. + +#### Examples + + + + ```python + import requests + headers = {"Authorization": f"Bearer {token}"} + response = requests.get( + url=f"https://api.plane.so/auth/o/app-installation/?id={app_installation_id}", + headers=headers + ) + workspace_details = response.data[0] + ``` + + + ```typescript + import axios from 'axios'; + const headers = { Authorization: `Bearer ${token}` }; + const response = await axios.get( + `https://api.plane.so/auth/o/app-installation/?id=${app_installation_id}`, + { headers } + ); + const workspaceDetails = response.data[0]; + ``` + + + +#### Sample Response + +```json +[ + { + "id": "34b97361-8636-43dc-953e-90deedc8498f", + "workspace_detail": { + "name": "sandbox", + "slug": "sandbox", + "id": "7a2e5944-c117-4a7d-b5f4-058fe705d7d1", + "logo_url": null + }, + "created_at": "2025-05-16T13:50:27.865821Z", + "updated_at": "2025-06-23T08:57:26.976742Z", + "deleted_at": null, + "status": "installed", + "workspace": "7a2e5944-c117-4a7d-b5f4-058fe705d7d1", + "application": "ab235529-388a-4f51-a55a-78272251f5f1", + "installed_by": "63333ab1-c605-42fc-82f7-5cd86799eca1", + "app_bot": "7286aaa7-9250-4851-a520-29c904fd7654", + "webhook": "b1f4b7f1-51e8-4919-a84c-0b1143b51d2c" + } +] +``` + +## Obtain and store access tokens securely + +Once you have obtained the access token, you can use it to make authenticated API requests to Plane. +Store the access token and refresh token securely in your database. + + +## Make authenticated API requests to Plane + +For making authenticated API requests to Plane, you can use the access token obtained from the OAuth flow. + +API reference is available at [https://docs.plane.so/api-reference](https://docs.plane.so/api-reference). + +We have official SDKs for the following languages to simplify the OAuth flow and make it easier to call Plane's API. + +| Language | Package Link | Source Code | +|----------|---------|-------------| +| Node.js | [npm i @makeplane/plane-node-sdk](https://www.npmjs.com/package/@makeplane/plane-node-sdk) | [plane-node-sdk](https://github.com/makeplane/plane-node-sdk) | +| Python | [pip install plane-sdk](https://pypi.org/project/plane-sdk/) | [plane-python-sdk](https://github.com/makeplane/plane-python-sdk) | + +## Handle Token Refresh + +When the access token expires, you can use the refresh token to get a new access token. + +#### Examples + + + + ```python + refresh_payload = { + "grant_type": "refresh_token", + "refresh_token": refresh_token, + "client_id": client_id, + "client_secret": client_secret + } + refresh_response = requests.post( + url="https://api.plane.so/auth/o/token/", + headers={"Content-Type": "application/x-www-form-urlencoded"}, + data=refresh_payload + ) + refresh_response_data = refresh_response.json() + access_token = refresh_response_data["access_token"] + ``` + + + ```typescript + const refreshPayload = { + grant_type: "refresh_token", + refresh_token: refreshToken, + client_id: clientId, + client_secret: clientSecret + }; + const refreshResponse = await axios.post( + "https://api.plane.so/auth/o/token/", + refreshPayload, + { + headers: { + "Content-Type": "application/x-www-form-urlencoded" + } + } + ); + const refreshResponseData = refreshResponse.data; + const accessToken = refreshResponseData.access_token; + ``` + + + +## Listing Your App on Plane Marketplace + +Apps built using the OAuth flow can be listed on the Plane Marketplace: [https://plane.so/marketplace/integrations](https://plane.so/marketplace/integrations) + +To list your app, please contact the Plane team at [**support@plane.so**](mailto:support@plane.so). \ No newline at end of file diff --git a/docs/apps/overview.mdx b/docs/apps/overview.mdx index 96bea1d..de9bc54 100644 --- a/docs/apps/overview.mdx +++ b/docs/apps/overview.mdx @@ -43,4 +43,4 @@ integrations that fit your specific workflow. ## Get Started -Next, follow our [quickstart guide](/apps/quickstart) to build your first Plane App. +Next, follow our [quickstart guide](/apps/build-plane-app) to build your first Plane App. diff --git a/docs/apps/quickstart.mdx b/docs/apps/quickstart.mdx deleted file mode 100644 index 552a186..0000000 --- a/docs/apps/quickstart.mdx +++ /dev/null @@ -1,338 +0,0 @@ ---- -title: Build a Plane App -sidebar_label: Quickstart -description: Step-by-step development guide to building a Plane App ---- - -import Tabs from "@theme/Tabs"; -import TabItem from "@theme/TabItem"; - -{frontMatter.description && ( - -

{frontMatter.description}

-)} - -:::tip Beta -Plane Apps are currently in **Beta**. Some aspects of the API may change. Please send -feedback to support@plane.so. -::: - -## Prerequisites - -- A [Plane](https://app.plane.so) workspace -- Admin access to your Plane workspace settings -- Familiarity with OAuth 2.0 concepts -- A backend server to handle OAuth token exchange - -## High-Level Workflow - -1. Register your Plane App -2. Implement OAuth -3. Obtain access tokens -4. Make authenticated API requests to Plane -5. Handle token refresh - -## Register Your Plane App - -1. Go to `https://app.plane.so//settings/applications/`. -2. Click **Build your own**. -3. Fill out the required details: - - **Name** and **Short Description** - - **Redirect URIs** - - **Contact Details** - - **Setup URL (Optional)** - - **Webhook URL Endpoint (Optional)** - - **Organization Details (Optional)** -4. If you're building an agent, enable the **Is Mentionable** checkbox. -5. Once the app is created, securely store the generated **Client ID** and **Client Secret**. - -## Implement OAuth Flow - -### Generating Consent URL (Optional) - -If your app should be installed from outside Plane, generate the consent URL using the -client ID from app creation. Provide this URL in the "Setup URL" field if you want the -flow triggered from the marketplace. - - - - ```python - import os - from urllib.parse import urlencode - params = { - "client_id": os.getenv("PLANE_CLIENT_ID"), - "response_type": "code", - "redirect_uri": os.getenv("PLANE_REDIRECT_URI"), - } - consent_url = f"https://api.plane.so/auth/o/authorize-app/?{urlencode(params)}" - ``` - - - ```typescript - import { URLSearchParams } from 'url'; - const params = new URLSearchParams({ - client_id: process.env.PLANE_CLIENT_ID!, - response_type: "code", - redirect_uri: process.env.PLANE_REDIRECT_URI!, - }); - const consentUrl = `https://api.plane.so/auth/o/authorize-app/?${params.toString()}`; - ``` - - - -There are two types of authenticated actions your app can perform: - -1. **User-authorized actions**: Actions performed on behalf of a user after they grant permission via OAuth. -2. **App-authorized actions**: Actions the app can perform independently within the workspace where it is installed. - -For both flows, Plane will make a GET request to the Redirect URI with parameters as -described below. - -### App-Authorized Actions (Client Credentials Flow) - -When the app is installed, Plane will send an `app_installation_id` as part of the -callback. Use this to request a bot token for your app. - -| Parameter | Description | -| ------------------- | --------------------------------------------------------------- | -| app_installation_id | The unique identifier for the app installation in the workspace | - -#### Example - - - - ```python - import base64 - import requests - client_id = "your_client_id" - client_secret = "your_client_secret" - basic_auth = base64.b64encode(f"{client_id}:{client_secret}".encode()).decode() - payload = { - "grant_type": "client_credentials", - "app_installation_id": app_installation_id - } - response = requests.post( - url="https://api.plane.so/auth/o/token/", - headers={"Authorization": f"Basic {basic_auth}", "Content-Type": "application/x-www-form-urlencoded"}, - data=payload - ) - response_data = response.json() - bot_token = response_data['access_token'] - expires_in = response_data["expires_in"] - ``` - - -```typescript -import axios from 'axios'; -const clientId = "your_client_id"; -const clientSecret = "your_client_secret"; -const basicAuth = Buffer.from(`${clientId}:${clientSecret}`).toString('base64'); -const payload = { - grant_type: "client_credentials", - app_installation_id: appInstallationId -}; -const response = await axios.post( - "https://api.plane.so/auth/o/token/", - payload, - { - headers: { - Authorization: `Basic ${basicAuth}`, - "Content-Type": "application/x-www-form-urlencoded" - } - } -); -const responseData = response.data; -const botToken = responseData.access_token; -const expiresIn = responseData.expires_in; -``` - - - -### User-Authorized Actions (Authorization Code Flow) - -In this flow, your app exchanges the `code` received as a query parameter on the -callback for an access token and refresh token. The access token is short-lived and -must be refreshed using the refresh token when it expires. Store both tokens securely. - -Plane will make a GET request to the Redirect URI with: - -| Parameter | Description | Required | -| --------- | ------------------------------------------------------- | -------- | -| code | The authorization code to exchange for an access token | Yes | -| state | The state parameter passed in the authorization request | No | - -#### Example - - - - ```python - import requests - code = "authorization_code_from_callback" - client_id = "your_client_id" - client_secret = "your_client_secret" - redirect_uri = "your_redirect_uri" - payload = { - "grant_type": "authorization_code", - "code": code, - "client_id": client_id, - "client_secret": client_secret, - "redirect_uri": redirect_uri - } - response = requests.post( - url="https://api.plane.so/auth/o/token/", - headers={"Content-Type": "application/x-www-form-urlencoded"}, - data=payload - ) - response_data = response.json() - access_token = response_data["access_token"] - refresh_token = response_data["refresh_token"] - expires_in = response_data["expires_in"] - ``` - - - ```typescript - import axios from 'axios'; - const code = "authorization_code_from_callback"; - const clientId = "your_client_id"; - const clientSecret = "your_client_secret"; -const redirectUri = "your_redirect_uri"; -const payload = { - grant_type: "authorization_code", - code: code, - client_id: clientId, - client_secret: clientSecret, - redirect_uri: redirectUri -}; -const response = await axios.post( - "https://api.plane.so/auth/o/token/", - payload, - { - headers: { - "Content-Type": "application/x-www-form-urlencoded" - } - } -); -const responseData = response.data; -const accessToken = responseData.access_token; -const refreshToken = responseData.refresh_token; -const expiresIn = responseData.expires_in; -``` - - - -### Fetching App Installation Details - -In both flows, the `app_installation_id` identifies the app's installation within a -workspace. Fetch workspace details after OAuth is completed. Use the -`app-installation` endpoint with either type of token. - - - - ```python - import requests - headers = {"Authorization": f"Bearer {token}"} - response = requests.get( - url=f"https://api.plane.so/auth/o/app-installation/?id={app_installation_id}", - headers=headers - ) - workspace_details = response.data[0] - ``` - - - ```typescript - import axios from 'axios'; - const headers = { Authorization: `Bearer ${token}` }; - const response = await axios.get( - `https://api.plane.so/auth/o/app-installation/?id=${app_installation_id}`, - { headers } - ); - const workspaceDetails = response.data[0]; - ``` - - - -#### Sample Response - -```json -[ - { - "id": "34b97361-8636-43dc-953e-90deedc8498f", - "workspace_detail": { - "name": "sandbox", - "slug": "sandbox", - "id": "7a2e5944-c117-4a7d-b5f4-058fe705d7d1", - "logo_url": null - }, - "created_at": "2025-05-16T13:50:27.865821Z", - "updated_at": "2025-06-23T08:57:26.976742Z", - "deleted_at": null, - "status": "installed", - "workspace": "7a2e5944-c117-4a7d-b5f4-058fe705d7d1", - "application": "ab235529-388a-4f51-a55a-78272251f5f1", - "installed_by": "63333ab1-c605-42fc-82f7-5cd86799eca1", - "app_bot": "7286aaa7-9250-4851-a520-29c904fd7654", - "webhook": "b1f4b7f1-51e8-4919-a84c-0b1143b51d2c" - } -] -``` - -## Obtain and store access tokens securely - -Store the access and refresh tokens securely in your database or secrets manager. - -## Make authenticated API requests to Plane - -Use the access token to make authenticated requests to Plane via the -[Plane API](/api/introduction) or [official SDKs](/sdks/overview). - -## Handle Token Refresh - -When the access token expires, use the refresh token to obtain a new access token. - - - - ```python - refresh_payload = { - "grant_type": "refresh_token", - "refresh_token": refresh_token, - "client_id": client_id, - "client_secret": client_secret - } - refresh_response = requests.post( - url="https://api.plane.so/auth/o/token/", - headers={"Content-Type": "application/x-www-form-urlencoded"}, - data=refresh_payload - ) - refresh_response_data = refresh_response.json() - access_token = refresh_response_data["access_token"] - ``` - - - ```typescript - const refreshPayload = { - grant_type: "refresh_token", - refresh_token: refreshToken, - client_id: clientId, - client_secret: clientSecret - }; - const refreshResponse = await axios.post( - "https://api.plane.so/auth/o/token/", - refreshPayload, - { - headers: { - "Content-Type": "application/x-www-form-urlencoded" - } - } - ); - const refreshResponseData = refreshResponse.data; - const accessToken = refreshResponseData.access_token; - ``` - - - -## Listing Your App on Plane Marketplace - -Apps built using the OAuth flow can be listed on the Plane Marketplace: -[https://plane.so/marketplace/integrations](https://plane.so/marketplace/integrations) - -To list your app, contact the Plane team at [support@plane.so](mailto:support@plane.so). diff --git a/docs/intro.md b/docs/intro.md index 80059d1..30334cb 100644 --- a/docs/intro.md +++ b/docs/intro.md @@ -2,36 +2,37 @@ title: Introduction slug: / sidebar_position: 1 -description: Explore our guides and examples to integrate Plane +description: Complete developer documentation for Plane - self-hosting guides, REST API reference, webhooks, authentication, and tools for building custom integrations and applications. --- -# Plane Developer Documentation +# Self-host, integrate, and extend Plane -{frontMatter.description &&

{frontMatter.description}

} +Welcome to Plane's developer documentation. Here you'll find everything you need to deploy, customize, integrate, and extend Plane's capabilities for your organization. -:::tip Let's Go! -Get Started with Self-Hosting right away by visiting our [QuickStart](/self-hosting/overview). -::: - -Greetings developer! 👋 - -We're happy you're here. This site is focused on making it easy for you to -integrate with Plane. Whether hosting Plane yourself or creating a custom -integration, this is where you'll find all the fun details you need. 🦾 +## What's in here import { Card } from '@site/src/components/Card'; import { CardGroup } from '@site/src/components/CardGroup'; -## Solutions - - - Learn how to self-host Plane. + + Deploy Plane on your own infrastructure with full control over your data. - - Use our API reference to build a custom integration. + + Complete documentation of our REST API. - - Learn how to integrate Plane's webhooks with your service. + + Set up real-time notifications so your other systems know when stuff happens in Plane. + +## Where to start +Not sure where to begin? Here's what we'd suggest: + +If you're setting up Plane for the first time, start with the self-hosting section. We'll get you up and running. + +If you're connecting Plane to something else, jump to the API reference. Get familiar with how our data is structured and try a few calls. + +## When you get stuck +Things break, documentation isn't perfect, and sometimes you just need to talk to a human. +Reach out on Discord - our community is pretty helpful and we're usually around to answer questions. \ No newline at end of file diff --git a/docs/ai-solutions/overview.mdx b/docs/mcp-server/overview.mdx similarity index 97% rename from docs/ai-solutions/overview.mdx rename to docs/mcp-server/overview.mdx index a3b62f4..91548b4 100644 --- a/docs/ai-solutions/overview.mdx +++ b/docs/mcp-server/overview.mdx @@ -1,6 +1,6 @@ --- -title: AI Solutions Overview -sidebar_label: Overview +title: MCP Server +sidebar_label: MCP Server description: Use the Plane MCP server to integrate with Plane sidebar_position: 1 --- diff --git a/docs/sdks/overview.mdx b/docs/sdks/overview.mdx index bdeaab3..8460839 100644 --- a/docs/sdks/overview.mdx +++ b/docs/sdks/overview.mdx @@ -1,6 +1,6 @@ --- -title: SDKs Overview -sidebar_label: Overview +title: SDKs +sidebar_label: SDKs description: Use Plane SDKs to integrate with Plane sidebar_position: 1 --- diff --git a/docs/self-hosting/_category_.yml b/docs/self-hosting/_category_.yml index 5a0d840..1e090f8 100644 --- a/docs/self-hosting/_category_.yml +++ b/docs/self-hosting/_category_.yml @@ -1,2 +1,3 @@ label: "Self-Hosting" position: 2 +collapsed: false diff --git a/docs/self-hosting/editions-and-versions.mdx b/docs/self-hosting/editions-and-versions.mdx index 7a4798f..3b700ce 100644 --- a/docs/self-hosting/editions-and-versions.mdx +++ b/docs/self-hosting/editions-and-versions.mdx @@ -1,6 +1,6 @@ --- title: Understanding Plane's Editions -sidebar_label: Plane Editions +sidebar_label: About Editions sidebar_position: 2 --- @@ -9,12 +9,9 @@ sidebar_position: 2

{frontMatter.description}

)} -Plane is available in three editions, based on how it is deployed. As of 2025, our -Cloud is the only hosted edition. We also offer two unique self-hosted editions -tailored to different needs: the open-source Community Edition and the recommended -Commercial Edition. +Plane comes in three editions by how its deployed. Our Cloud is our only hosted edition as of 2025. Additionally, we offer two unique self-hosted editions tailored to meet two sets of unique needs—the open-source Community Edition and the recommended Commercial Edition. -## About Our Self-Hosted Editions +## About our self-hosted editions ### Community Edition @@ -24,19 +21,14 @@ Built with transparency in mind, the Community Edition: - Allows contributions to the repo through modifications and customizations - Has no code dependencies or restrictions on or from the Commercial Edition -It’s ideal for those who want to try Plane first, audit the code for security, and see -how each service works with the others. Tens of thousands of users have used it, and -many have contributed to it. +It’s ideal for those who want to try Plane first, audit the code for security, and see how each one of services works with the others. Several tens of thousands of uses have used it and a significant number have contributed to it. -The Community Edition matches the Free tier of the Cloud edition in feature -availability. To upgrade to paid plans, you must first switch to the Commercial -Edition. +The Community Edition is at par with the Free tier of the Cloud edition in its feature availability. To upgrade to paid plans, you must first switch to the Commercial Edition. +​ ### Commercial Edition -Designed for teams that need governance, compliance, and privacy controls, the -Commercial Edition is ideal for organizations looking to unlock advanced work -management and security features. +Designed for teams that want governance, compliance, and privacy controls, the Commercial Edition is ideal for teams that want to try Plane with an intent to unlock advanced work management and security features. This edition also comes with a Free tier and allows seamless upgrades to all paid plans. It offers: @@ -45,19 +37,19 @@ plans. It offers: - A bundle of 12 free user seats per workspace, so there are no surprises when you upgrade - An intuitive upgrade flow that automatically calculates the number of seats you need based on the number of users with paid roles in your workspace, so you never have to guess -## Why We Separate Editions +## Why we separate editions We’ve designed Plane’s editions to serve diverse user needs while staying true to the ethos of open source. -- The **Community Edition** is completely open-source, with no restrictions beyond those outlined in the [AGPL v3.0 license](https://github.com/makeplane/plane/blob/preview/LICENSE.txt). This edition is now ranked #1 in our space on GitHub. +- The **Community Edition** is completely open-source, with no restrictions beyond those outlined in the [AGPL v3.0 license](https://github.com/makeplane/plane/blob/preview/LICENSE.txt). This is the edition that is now ranking at #1 in our space on GitHub. - The **Commercial Edition** remains closed-source to offer enterprise-grade features and seamless scalability for businesses. Unlike some open-core companies, we’ve adopted a clean separation to keep things simple and transparent. There’s no hidden code that limits modifications on the Community Edition, and no forced migrations from one edition to another. -## Differences in Versions Between Editions +## Differences in versions between Editions Each of our editions is built on a distinct codebase. Versions differ based on how we ship new code according to our three separate release cycles. This distinction allows diff --git a/docs/self-hosting/govern/_category_.yml b/docs/self-hosting/govern/_category_.yml index 2df6327..2322fb7 100644 --- a/docs/self-hosting/govern/_category_.yml +++ b/docs/self-hosting/govern/_category_.yml @@ -1 +1,2 @@ label: "Configure" +collapsed: false \ No newline at end of file diff --git a/docs/self-hosting/govern/integrations/github.mdx b/docs/self-hosting/govern/integrations/github.mdx index 23fe203..03185d8 100644 --- a/docs/self-hosting/govern/integrations/github.mdx +++ b/docs/self-hosting/govern/integrations/github.mdx @@ -3,124 +3,253 @@ title: Configure GitHub App for Plane integration sidebar_label: GitHub --- -import Tabs from "@theme/Tabs"; -import TabItem from "@theme/TabItem"; +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; -{frontMatter.description && ( +{frontMatter.description &&

{frontMatter.description}

} -

{frontMatter.description}

-)} - -This guide walks you through setting up a GitHub App to enable GitHub integration for -your Plane workspace on a self-hosted instance. Since self-hosted environments don’t -come pre-configured for GitHub, you’ll need to set up authentication, permissions, -and webhooks to ensure smooth integration. +This guide walks you through setting up a GitHub App to enable GitHub integration for your Plane workspace on a self-hosted instance. Since self-hosted environments don’t come pre-configured for GitHub, you’ll need to set up the necessary authentication, permissions, and webhooks to ensure smooth integration. This guide covers configuration for both: -- **[GitHub Cloud](/self-hosting/govern/integrations/github?edition=github-cloud#create-github-app)** — The standard cloud-hosted GitHub service -- **[GitHub Enterprise Server](/self-hosting/govern/integrations/github?edition=github-enterprise#create-github-app)** — Self-hosted GitHub instances for organizations with specific compliance or security requirements +- **[GitHub Cloud](/self-hosting/govern/integrations/github?edition=github-cloud#create-github-app)** +The standard cloud-hosted GitHub service -In this guide, you’ll: +- **[GitHub Enterprise Server](/self-hosting/govern/integrations/github?edition=github-enterprise#create-github-app)** +Self-hosted GitHub instances for organizations with specific compliance or security requirements -1. [Create and configure a GitHub App](#create-github-app) -2. [Configure your Plane instance](#configure-plane-instance) +In this guide, you’ll: +1. [Create and configure a GitHub App](/self-hosting/govern/integrations/github#create-github-app) +2. [Set up permissions and events](/self-hosting/govern/integrations/github#set-up-permissions-and-events) +3. [Configure your Plane instance](/self-hosting/govern/integrations/github#configure-plane-instance) :::warning IMPORTANT **Activate GitHub integration** -After creating and configuring the GitHub app and configuring the instance as -detailed on this page, you'll need to -[set up the GitHub integration](https://docs.plane.so/integrations/github) within -Plane. +After creating and configuring the GitHub app and configuring the instance as detailed on this page, you'll need to [setup the GitHub integration](https://docs.plane.so/integrations/github) within Plane. ::: ## Create GitHub App -To configure GitHub integration, create a GitHub App within your organization. +To configure GitHub integration, you'll need to create a GitHub App within your organization. - 1. Go to **Settings > Developer Settings > GitHub Apps** in your GitHub organization. - 2. Click **New GitHub App**. - ![Create GitHub App](/images/integrations/github/create-github-app.webp#center) + 1. Go to **Settings > Developer Settings > GitHub Apps** in your GitHub organization. - 3. In the **Register new GitHub App** page, provide a **GitHub App name** and **Homepage URL**. - ![App name and homepage URL](/images/integrations/github/app-name-homepage-url.webp#center) + 2. Click **New GitHub App**. + ![Create GitHub App](/images/integrations/github/create-github-app.webp#center) - 4. In the **Identifying and authorizing users** section, add the following **Callback URLs**: - ```bash - https:///silo/api/github/auth/callback - https:///silo/api/github/auth/user/callback - ``` - These URLs allow Plane to verify and enable workspace connection with the GitHub App. - ![Add Callback URL](/images/integrations/github/add-callback-url.webp#center) + 3. In the **Register new GitHub App** page, provide a **GitHub App name** and **Homepage URL**. + ![App name and homepage URL](/images/integrations/github/app-name-homepage-url.webp#center) - 5. In the **Post installation** section, add this **Setup URL** and check the box for **Redirect on update**. - ![Add setup URL](/images/integrations/github/add-setup-url.webp#center) + 4. In the **Identifying and authorizing users** section, add the following **Callback URLS**. - 6. In the **Webhook** section, add the following **Webhook URL** to allow Plane to receive updates from GitHub repositories: - ```bash - https:///silo/api/github/github-webhook - ``` - ![Add Webhook URL](/images/integrations/github/add-webhook-url.webp#center) + ```bash + https:///silo/api/github/auth/callback + https:///silo/api/github/auth/user/callback + ``` + + These URLs allow Plane to verify and enable workspace connection with the Github App. + + ![Add Callback URL](/images/integrations/github/add-callback-url.webp#center) + + 5. In the **Post installation** section, add the below **Setup URL**. + + ```bash + https:///silo/api/github/auth/callback + ``` + + Redirects users to this URL after GitHub app installation. + ![Add setup URL](/images/integrations/github/add-setup-url.webp#center) + + 6. Turn on **Redirect on update**. + + 7. In the **Webhook** section, add the following **Webhook URL** to allow Plane to receive updates from GitHub repositories. + + ```bash + https:///silo/api/github/github-webhook + ``` + ![Add Webhook URL](/images/integrations/github/add-webhook-url.webp#center) + - 1. Go to **Settings > Developer Settings > GitHub Apps** in your GitHub organization. - + 1. Go to **Settings \> Developer Settings \> GitHub Apps** in your GitHub organization. 2. Click **New GitHub App**. + ![Create GitHub App](/images/integrations/github/create-github-app.webp#center) 3. In the **Register new GitHub App** page, provide a **GitHub App name** and **Homepage URL**. - ![App name and homepage URL](/images/integrations/github/app-name-homepage-url.webp#center) - 4. In the **Identifying and authorizing users** section, add the following **Callback URLs**: + ![App name and homepage URL](/images/integrations/github/app-name-homepage-url.webp#center) + + 4. In the **Identifying and authorizing users** section, add the following + **Callback URLs** to allow Plane to verify and enable workspace connections + with the Github App. **For Plane cloud** - ```bash - https://silo.plane.so/api/oauth/github-enterprise/auth/callback - https://silo.plane.so/api/oauth/github-enterprise/auth/user/callback - ``` + + ```bash + https://silo.plane.so/api/oauth/github-enterprise/auth/callback + https://silo.plane.so/api/oauth/github-enterprise/auth/user/callback + ``` + **For Plane self-hosted instance** - ```bash - https:///silo/api/oauth/github-enterprise/auth/callback - https:///silo/api/oauth/github-enterprise/auth/user/callback - ``` - ![Add Callback URL](/images/integrations/github/add-callback-url.webp#center) - 5. In the **Post installation** section: - - Add the appropriate **Setup URL**, based on your hosting type: - - **Plane cloud**: `https://silo.plane.so/api/oauth/github-enterprise/auth/callback` - - **Self-hosted**: `https:///silo/api/oauth/github-enterprise/auth/callback` - - Check the box for **Redirect on update** - ![Add setup URL](/images/integrations/github/add-setup-url.webp#center) + ```bash + https:///silo/api/oauth/github-enterprise/auth/callback + https:///silo/api/oauth/github-enterprise/auth/user/callback + ``` - 6. In the **Webhook** section, add the following **Webhook URL** to allow Plane to receive updates from your GitHub repositories: + ![Add Callback URL](/images/integrations/github/add-callback-url.webp#center) + + 5. In the **Post installation** section, add the below **Setup URL**. + + **For Plane cloud** + ```bash + https://silo.plane.so/api/oauth/github-enterprise/auth/callback + ``` + + **For Plane self-hosted instance** + + ```bash + https:///silo/api/oauth/github-enterprise/auth/callback + ``` + + Redirects users to this URL after GitHub app installation. + ![Add setup URL](/images/integrations/github/add-setup-url.webp) + + 6. Turn on **Redirect on update**. + + 7. In the **Webhook** section, add the following **Webhook URL** to allow Plane to receive updates from your GitHub repositories. **For Plane cloud** + ```bash - https://silo.plane.so/api/github/github-webhook + https://silo.plane.so/api/github-enterprise/github-webhook ``` + **For Plane self-hosted instance** + ```bash - https:///silo/api/github/github-webhook + https:///silo/api/github-enterprise/github-webhook ``` - ![Add Webhook URL](/images/integrations/github/add-webhook-url.webp#center) + ![Add Webhook URL](/images/integrations/github/add-webhook-url.webp#center) +### Set up permissions and events + +1. Add repository and account permissions by setting the **Access** dropdown next to each permission, as shown in the tables below. + + ![Setup permissions](/images/integrations/github/setup-permissions.webp#center) + + **Repository permissions** + + | Permission | Access level | Purpose | + | ----------------- | ---------------- | --------- | + | Commit statuses | Read-only | Allows the GitHub app to read and update commit statuses, indicating whether a commit has passed checks (e.g., CI/CD pipelines). | + | Contents | Read and write | Grants access to read and modify repository contents, including reading files, creating commits, and updating files. | + | Issues | Read and write | Enables reading, creating, updating, closing, and commenting on issues within the repository. | + | Merge queues | Read-only | Allows interaction with merge queues to manage the order of pull request merges. | + | Metadata | Read-only | Provides read-only access to repository metadata, such as its name, description, and visibility. | + | Pull requests | Read and write | Allows reading, creating, updating, merging, and commenting on pull requests. | + + **Account permissions** + + | Permission | Access level | Purpose | + | ---------------- | ---------------- | -------------------------------------------------------------------------------------------------------------------------------- | + | Email addresses | Read-only | Grants access to users' email addresses, typically for notifications or communication. | + | Profile | Read and write | Enables access to user profile details like name, username, and avatar. | + +2. In the **Subscribe to events** section, turn on all the required events below. + + ![Subscribe to events](/images/integrations/github/subscribe-to-events.webp#center) + + | Event | Purpose | + | ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | + | Installation target | This is where the repositories or organizations where your GitHub App is installed. This determines which repositories Plane can sync with. | + | Meta | Includes metadata about the app's configuration and setup. This is essential for maintaining integration stability. | + | Issue comment | Triggers when a comment is added, edited, or deleted on an issue. Useful for keeping comments synced between Plane and GitHub. | + | Issues | Triggers when an issue is created, updated, closed, reopened, assigned, labeled, or transferred. Ensures issue status and details remain consistent between Plane and GitHub. | + | Pull request | Fires when a pull request is opened, closed, merged, edited, or labeled. Essential for tracking development progress. | + | Pull request review | Activates when a review is submitted, edited, or dismissed. Keeps review activities aligned between Plane and GitHub. | + | Pull request review comment | Fires when a review comment is added, modified, or removed. Ensures feedback is reflected across both platforms. | + | Pull request review thread | Triggers when a review discussion thread is resolved or reopened. Helps maintain visibility on code review discussions. | + | Push | Activates when new commits are pushed to a repository. Useful for tracking code updates and changes. | + | Repository sub issues | Tracks issues within a repository that are linked to or managed by another issue. Ensures accurate synchronization of related issues. | + +3. Click the **Create GitHub App** button at the bottom of the page. + ## Configure Plane instance -After creating your GitHub App: + + + 1. Go back to **Settings \> Developer Settings \> GitHub Apps**. + 2. Click **Edit** on the GitHub you created. + 3. In the **General** tab, under the **Client secrets** section, click **Generate a new client secret**. + ![General tab](/images/integrations/github/general-tab.webp#center) + 4. Scroll down to the **Private keys** section. + ![Private keys](/images/integrations/github/private-keys.webp#center) + 5. Click **Generate a private key**. + 6. Retrieve the following details from the **General** tab: + - App ID + - Client ID + - Client secret + - GitHub App name + - Private key + + 7. Before adding the Private key as an environment variable, you’ll need to convert it to base64. Since private keys are typically multi-line, they can cause parsing errors or issues when setting environment variables. To avoid this, run the following command to convert the key to base64: + + ```bash + cat private_key.pem | base64 -w 0 + ``` + + 8. Add these environment variables with the values to your Plane instance's `.env` file. -1. Copy the **Client ID** and **Client Secret** from the app settings. -2. Add these environment variables to your Plane instance's `.env` file: - ```bash - GITHUB_CLIENT_ID= - GITHUB_CLIENT_SECRET= - ``` -3. Save the file and restart the instance. -4. Once you've completed the instance configuration, [activate the GitHub integration in Plane](https://docs.plane.so/integrations/github). + ```bash + GITHUB_CLIENT_ID= + GITHUB_CLIENT_SECRET= + GITHUB_APP_NAME= + GITHUB_APP_ID= + GITHUB_PRIVATE_KEY= + ``` + + 9. Save the file and restart the instance. + + 10. Once you've completed the instance configuration, [activate the GitHub integration in Plane](https://docs.plane.so/integrations/github). + + + + 1. Go back to **Settings \> Developer Settings \> GitHub Apps**. + + 2. Click **Edit** on the GitHub you created. + + 3. In the **General** tab, under the **Client secrets** section, click **Generate a new client secret**. + + ![General tab](/images/integrations/github/general-tab.webp) + + 4. Scroll down to the **Private keys** section. + + ![Private keys](/images/integrations/github/private-keys.webp) + + 5. Click **Generate a private key**. + + 6. Retrieve the following details from the **General** tab: + - App ID + - App Slug (You can find this in browser url) + - Client ID + - Client secret + - Private key + + 7. Convert the Private key to convert it to base64. Since private keys are typically multi-line, they can cause parsing errors or issues when setting environment variables. To avoid this, run the following command to convert the key to base64: + ```bash + cat private_key.pem | base64 -w 0 + ``` + + 8. Once you've created the app, [activate the GitHub Enterprise integration in Plane](https://docs.plane.so/integrations/github?edition=github-enterprise#connect-github-organization). + + diff --git a/docs/self-hosting/manage/_category_.yml b/docs/self-hosting/manage/_category_.yml index 41258e0..9ebeee1 100644 --- a/docs/self-hosting/manage/_category_.yml +++ b/docs/self-hosting/manage/_category_.yml @@ -1 +1,2 @@ label: "Manage" +collapsed: false \ No newline at end of file diff --git a/docs/self-hosting/methods/_category_.yml b/docs/self-hosting/methods/_category_.yml index 1108738..ff278e9 100644 --- a/docs/self-hosting/methods/_category_.yml +++ b/docs/self-hosting/methods/_category_.yml @@ -1,2 +1,3 @@ label: "Install" -position: 2 +position: 3 +collapsed: false diff --git a/docs/self-hosting/methods/airgapped-edition.mdx b/docs/self-hosting/methods/airgapped-edition.mdx index 0ca04fa..a2f0eff 100644 --- a/docs/self-hosting/methods/airgapped-edition.mdx +++ b/docs/self-hosting/methods/airgapped-edition.mdx @@ -1,6 +1,7 @@ --- title: Deploy Plane Commercial Airgapped Edition sidebar_label: Airgapped Edition +sidebar_position: 4 --- {frontMatter.description && ( diff --git a/docs/self-hosting/methods/coolify.mdx b/docs/self-hosting/methods/coolify.mdx index 1bf3a8e..7a8eb2e 100644 --- a/docs/self-hosting/methods/coolify.mdx +++ b/docs/self-hosting/methods/coolify.mdx @@ -1,6 +1,7 @@ --- title: Deploy Plane with Coolify • Commercial Edition sidebar_label: Coolify +sidebar_position: 5 --- {frontMatter.description && ( diff --git a/docs/self-hosting/methods/podman-quadlets.mdx b/docs/self-hosting/methods/podman-quadlets.mdx index 920b253..aebb52b 100644 --- a/docs/self-hosting/methods/podman-quadlets.mdx +++ b/docs/self-hosting/methods/podman-quadlets.mdx @@ -1,6 +1,7 @@ --- title: Deploy Plane with Podman Quadlets • Commercial Edition sidebar_label: Podman Quadlets +sidebar_position: 6 --- {frontMatter.description && ( diff --git a/docs/self-hosting/methods/portainer.mdx b/docs/self-hosting/methods/portainer.mdx index e2353f2..68f13fe 100644 --- a/docs/self-hosting/methods/portainer.mdx +++ b/docs/self-hosting/methods/portainer.mdx @@ -1,6 +1,7 @@ --- title: Deploy Plane with Portainer • Commercial Edition sidebar_label: Portainer +sidebar_position: 7 --- {frontMatter.description && ( diff --git a/docs/self-hosting/troubleshoot/_category_.yml b/docs/self-hosting/troubleshoot/_category_.yml index a4d8760..683b24e 100644 --- a/docs/self-hosting/troubleshoot/_category_.yml +++ b/docs/self-hosting/troubleshoot/_category_.yml @@ -1 +1,2 @@ label: "Troubleshoot" +collapsed: false \ No newline at end of file diff --git a/docs/webhooks/overview.mdx b/docs/webhooks/intro-webhooks.mdx similarity index 99% rename from docs/webhooks/overview.mdx rename to docs/webhooks/intro-webhooks.mdx index a396b99..7007774 100644 --- a/docs/webhooks/overview.mdx +++ b/docs/webhooks/intro-webhooks.mdx @@ -1,6 +1,6 @@ --- title: Webhooks Overview -sidebar_label: Overview +sidebar_label: Webhooks description: Use webhooks to receive real-time notifications about events in Plane. --- diff --git a/docusaurus.config.ts b/docusaurus.config.ts index a7e7f37..2f4e293 100644 --- a/docusaurus.config.ts +++ b/docusaurus.config.ts @@ -83,67 +83,80 @@ const config: Config = { }, }, navbar: { - title: "Developers", + title: 'Developers', logo: { - alt: "Plane", - src: "/img/logo/symbol-black.png", - srcDark: "/img/logo/symbol-white.png", + alt: 'Plane', + src: 'https://media.docs.plane.so/logo/new-logo-white.png', + srcDark: 'https://media.docs.plane.so/logo/new-logo-dark.png', }, items: [ { - type: "search", - position: "left", + type: 'docSidebar', + sidebarId: 'sidebar', + position: 'left', + label: 'Docs', }, { - href: "https://docs.plane.so", - "aria-label": "Plane Docs", - position: "right", - className: "navbar--plane-docs-link", + type: 'docSidebar', + sidebarId: 'apiSidebar', + position: 'left', + label: 'API', }, { - href: "https://discord.com/invite/A92xrEGCge", - "aria-label": "Discord", - position: "right", - className: "navbar--discord-link", + type: 'docSidebar', + sidebarId: 'devToolsSidebar', + position: 'left', + label: 'Dev Tools', }, { - href: "https://github.com/makeplane/plane", - "aria-label": "GitHub", - position: "right", - className: "navbar--github-link", + type: 'search', + position: 'left', }, { - href: "https://app.plane.so/sign-in", - label: "Sign in", - position: "right", + href: 'https://discord.com/invite/A92xrEGCge', + 'aria-label': 'Discord', + position: 'right', + className: 'navbar--discord-link', + }, + { + href: 'https://github.com/makeplane/plane', + 'aria-label': 'GitHub', + position: 'right', + className: 'navbar--github-link', + }, + { + href: 'https://app.plane.so/sign-in', + label: 'Sign in', + position: 'right', }, ], }, +/* footer: { - style: "dark", + style: 'dark', links: [ { - title: "Docs", + title: 'Docs', items: [ { - label: "Self-Hosting", - to: "/self-hosting/overview", + label: 'Self-Hosting', + to: '/self-hosting/overview', }, { - label: "SDKs", - to: "/sdks/overview", + label: 'SDKs', + to: '/sdks/overview', }, { - label: "AI Solutions", - to: "/ai-solutions/overview", + label: 'AI Solutions', + to: '/ai-solutions/overview', }, { - label: "Webhooks", - to: "/webhooks/overview", + label: 'Webhooks', + to: '/webhooks/overview', }, { - label: "API Reference", - to: "/api", + label: 'API Reference', + to: '/api', }, ], }, @@ -169,113 +182,126 @@ const config: Config = { ], }, { - title: "More", + title: 'More', items: [ { - label: "Blog", - href: "https://plane.so/blog", + label: 'Blog', + href: 'https://plane.so/blog', }, { - label: "GitHub", - href: "https://github.com/makeplane", + label: 'GitHub', + href: 'https://github.com/makeplane', }, ], }, ], // copyright: false, }, +*/ prism: { theme: prismThemes.github, darkTheme: prismThemes.dracula, - additionalLanguages: ["ruby", "csharp", "php", "java", "powershell", "json", "bash", "dart", "objectivec", "r"], + additionalLanguages: [ + 'ruby', + 'csharp', + 'php', + 'java', + 'powershell', + 'json', + 'bash', + 'dart', + 'objectivec', + 'r', + ], }, metadata: [ { - property: "og:image", - content: "https://media.docs.plane.so/logo/docs-og.webp", + property: 'og:image', + content: 'https://media.docs.plane.so/logo/docs-og.webp', }, { - name: "twitter:image", - content: "https://media.docs.plane.so/logo/docs-og.webp", + name: 'twitter:image', + content: 'https://media.docs.plane.so/logo/docs-og.webp', }, { - name: "keywords", - content: "project management, issue tracking, sprint management, agile, scrum, create projects, track sprints", + name: 'keywords', + content: + 'project management, issue tracking, sprint management, agile, scrum, create projects, track sprints', }, ], colorMode: { - defaultMode: "light", + defaultMode: 'light', }, languageTabs: [ { - highlight: "bash", - language: "curl", + highlight: 'bash', + language: 'curl', }, { - highlight: "python", - language: "python", + highlight: 'python', + language: 'python', }, { - highlight: "javascript", - language: "nodejs", - logoClass: "nodejs", + highlight: 'javascript', + language: 'nodejs', + logoClass: 'nodejs', }, { - highlight: "go", - language: "go", + highlight: 'go', + language: 'go', }, { - highlight: "ruby", - language: "ruby", + highlight: 'ruby', + language: 'ruby', }, { - highlight: "java", - language: "java", - variant: "unirest", + highlight: 'java', + language: 'java', + variant: 'unirest', }, { - highlight: "rust", - language: "rust", + highlight: 'rust', + language: 'rust', }, { - highlight: "php", - language: "php", + highlight: 'php', + language: 'php', }, { - highlight: "csharp", - language: "csharp", + highlight: 'csharp', + language: 'csharp', }, { - highlight: "powershell", - language: "powershell", + highlight: 'powershell', + language: 'powershell', }, { - highlight: "dart", - language: "dart", + highlight: 'dart', + language: 'dart', }, { - highlight: "javascript", - language: "javascript", + highlight: 'javascript', + language: 'javascript', }, { - highlight: "c", - language: "c", + highlight: 'c', + language: 'c', }, { - highlight: "objective-c", - language: "objective-c", + highlight: 'objective-c', + language: 'objective-c', }, { - highlight: "r", - language: "r", + highlight: 'r', + language: 'r', }, { - highlight: "swift", - language: "swift", + highlight: 'swift', + language: 'swift', }, { - highlight: "kotlin", - language: "kotlin", + highlight: 'kotlin', + language: 'kotlin', }, ], } satisfies Preset.ThemeConfig, @@ -292,7 +318,7 @@ const config: Config = { tagName: "link", attributes: { rel: "preload", - href: "/fonts/nacelle/nacelle-regular.otf", + href: "/fonts/nacelle/nacelle-light.otf", as: "font", type: "font/otf", crossorigin: "anonymous", @@ -302,7 +328,7 @@ const config: Config = { tagName: "link", attributes: { rel: "preload", - href: "/fonts/nacelle/nacelle-light.otf", + href: "/fonts/nacelle/nacelle-regular.otf", as: "font", type: "font/otf", crossorigin: "anonymous", @@ -318,26 +344,76 @@ const config: Config = { crossorigin: "anonymous", }, }, + { + tagName: "link", + attributes: { + rel: "preload", + href: "/fonts/nacelle/nacelle-bold.otf", + as: "font", + type: "font/otf", + crossorigin: "anonymous", + }, + }, + { + tagName: "link", + attributes: { + rel: "preload", + href: "/fonts/ibm/ibmplexmono-light.ttf", + as: "font", + type: "font/ttf", + crossorigin: "anonymous", + }, + }, + { + tagName: "link", + attributes: { + rel: "preload", + href: "/fonts/ibm/ibmplexmono-regular.ttf", + as: "font", + type: "font/ttf", + crossorigin: "anonymous", + }, + }, + { + tagName: "link", + attributes: { + rel: "preload", + href: "/fonts/ibm/ibmplexmono-semibold.ttf", + as: "font", + type: "font/ttf", + crossorigin: "anonymous", + }, + }, + { + tagName: "link", + attributes: { + rel: "preload", + href: "/fonts/ibm/ibmplexmono-bold.ttf", + as: "font", + type: "font/ttf", + crossorigin: "anonymous", + }, + }, ], plugins: [ [ - "@docusaurus/plugin-google-tag-manager", + '@docusaurus/plugin-google-tag-manager', { - containerId: process.env.GOOGLE_TAG_MANAGER_ID || "fake", + containerId: process.env.GOOGLE_TAG_MANAGER_ID || 'fake', }, ], [ - "docusaurus-plugin-openapi-docs", + 'docusaurus-plugin-openapi-docs', { - id: "api", // plugin id - docsPluginId: "classic", // configured for preset-classic + id: 'api', // plugin id + docsPluginId: 'classic', // configured for preset-classic config: { plane: { - specPath: "api/schema.yaml", - outputDir: "docs/api", + specPath: 'api/schema.yaml', + outputDir: 'docs/api', sidebarOptions: { - groupPathsBy: "tag", + groupPathsBy: 'tag', }, } satisfies OpenApiPlugin.Options, }, @@ -347,15 +423,15 @@ const config: Config = { themes: [ [ - require.resolve("@easyops-cn/docusaurus-search-local"), + require.resolve('@easyops-cn/docusaurus-search-local'), /** @type {import("@easyops-cn/docusaurus-search-local").PluginOptions} */ { hashed: true, indexBlog: false, - docsRouteBasePath: "/", + docsRouteBasePath: '/', }, ], - "docusaurus-theme-openapi-docs", + 'docusaurus-theme-openapi-docs', ], }; diff --git a/sidebars.ts b/sidebars.ts index cd34e09..3756dd1 100644 --- a/sidebars.ts +++ b/sidebars.ts @@ -1,84 +1,121 @@ -import type { SidebarsConfig } from "@docusaurus/plugin-content-docs"; +import type { SidebarsConfig } from '@docusaurus/plugin-content-docs'; // This runs in Node.js - Don't use client-side code here (browser APIs, JSX...) -import apiSidebar from "./docs/api/sidebar.js"; +import apiSidebar from './docs/api/sidebar.js'; // Substitute our custom intro page b/c it's nicer than the generated one. -const indexPage = apiSidebar[0]; -if (!(indexPage.type === "doc" && indexPage.id === "api/the-plane-rest-api")) { - throw new Error("Could not find API index page. Aborting."); +const indexPage = apiSidebar[0] +if (!(indexPage.type === 'doc' && indexPage.id === 'api/the-plane-rest-api')) { + throw new Error('Could not find API index page. Aborting.'); } -apiSidebar[0].id = "api/introduction"; +apiSidebar[0].id = 'api/introduction'; const sidebars: SidebarsConfig = { + sidebar: [ { - type: "doc", - id: "intro", + type: 'doc', + id: 'intro', }, { - type: "category", - label: "Self-Hosting", + type: "link", + label: "Plane Documentation", + href: "https://docs.plane.so/", + }, + + { + type: 'category', + collapsed: false, + label: 'Self-host Plane', items: [ - { - type: "autogenerated", - dirName: "self-hosting", - }, + "self-hosting/overview", + "self-hosting/editions-and-versions" ], }, { - type: "category", - label: "SDKs", + type: 'category', + collapsed: false, + label: 'Install', items: [ { - type: "autogenerated", - dirName: "sdks", + type: 'autogenerated', + dirName: 'self-hosting/methods', }, ], }, - { - type: "category", - label: "AI Solutions", + { + type: 'category', + collapsed: false, + label: 'Configure', items: [ { - type: "autogenerated", - dirName: "ai-solutions", + type: 'autogenerated', + dirName: 'self-hosting/govern', }, ], }, - { - type: "category", - label: "Apps", + { + type: 'category', + collapsed: false, + label: 'Manage', items: [ { - type: "autogenerated", - dirName: "apps", + type: 'autogenerated', + dirName: 'self-hosting/manage', }, ], }, - { - type: "category", - label: "Webhooks", + { + type: 'category', + collapsed: false, + label: 'Troubleshoot', items: [ { - type: "autogenerated", - dirName: "webhooks", + type: 'autogenerated', + dirName: 'self-hosting/troubleshoot', }, ], - }, - { - type: "category", - label: "API", + } + ], + + apiSidebar: [ + { + type: 'category', + label: 'API Reference', link: { - type: "generated-index", - title: "Plane API", + type: 'generated-index', + title: 'Plane API', description: - "Learn how to interact with Plane programmatically, including authentication, endpoints, and data structures.", - slug: "/api", + 'Learn how to interact with Plane programmatically, including authentication, endpoints, and data structures.', + slug: '/api', }, items: apiSidebar, + } + ], + + devToolsSidebar: [ + + { + type: 'doc', + id: 'webhooks/intro-webhooks', }, + { + type: 'doc', + id: 'apps/build-plane-app', + }, + { + type: 'doc', + id: 'mcp-server/overview', + }, + { + type: 'doc', + id: 'sdks/overview', + } + ], + }; + + export default sidebars; diff --git a/src/css/custom.css b/src/css/custom.css index 3141061..fee7573 100644 --- a/src/css/custom.css +++ b/src/css/custom.css @@ -4,7 +4,7 @@ * work well for content-centric websites. */ -@import "./fonts.css"; +@import './fonts.css'; /* You can override the default Infima variables here. */ :root { @@ -16,23 +16,23 @@ --ifm-color-primary-darker: #195c80; --ifm-color-primary-darkest: #113d55; --ifm-code-font-size: 95%; - --ifm-font-family-base: "Nacelle", system-ui, -apple-system, sans-serif; - --ifm-heading-font-family: "Nacelle", system-ui, -apple-system, sans-serif; - --ifm-font-family-monospace: "IBM Mono"; + --ifm-font-family-base: 'Nacelle', system-ui, -apple-system, sans-serif; + --ifm-heading-font-family: 'Nacelle', system-ui, -apple-system, sans-serif; + --ifm-font-family-monospace: 'IBM Mono'; --plane-text-color: rgb(255, 255, 255, 0.87); - --color-neutral-400: oklch(71.7% 0.0116 230.89); - --color-neutral-500: oklch(58.24% 0.0161 231.06); - --color-neutral-600: oklch(47.52% 0.0127 231.05); - --color-brand-25: oklch(98.54% 0.0085 230.78); - --color-brand-50: oklch(97.09% 0.017 230.91); - --color-brand-300: oklch(84.11% 0.0949 233.04); - --color-brand-400: oklch(76.44% 0.1392 236.52); - --color-brand-500: oklch(62.27% 0.1255 238.15); - --color-brand-600: oklch(48.1% 0.1154 242.59); - --color-brand-700: oklch(37.56% 0.0879 241.26); - --color-brand-800: oklch(27.52% 0.0617 238.68); - --color-brand-900: oklch(20.77% 0.044 234.86); - --color-brand-950: oklch(17.92% 0.0365 231.95); + --color-neutral-400: oklch(71.7% .0116 230.89); + --color-neutral-500: oklch(58.24% .0161 231.06); + --color-neutral-600: oklch(47.52% .0127 231.05); + --color-brand-25: oklch(98.54% .0085 230.78); + --color-brand-50: oklch(97.09% .017 230.91); + --color-brand-300: oklch(84.11% .0949 233.04); + --color-brand-400: oklch(76.44% .1392 236.52); + --color-brand-500: oklch(62.27% .1255 238.15); + --color-brand-600: oklch(48.1% .1154 242.59); + --color-brand-700: oklch(37.56% .0879 241.26); + --color-brand-800: oklch(27.52% .0617 238.68); + --color-brand-900: oklch(20.77% .044 234.86); + --color-brand-950: oklch(17.92% .0365 231.95); --ifm-footer-background-color: var(--color-brand-950); --plane-footer-background-color: var(--color-brand-950); @@ -40,12 +40,12 @@ --plane-call-to-action-hover-color: var(--color-brand-600); --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1); --ifm-navbar-search-input-icon: url('data:image/svg+xml;utf8,'); - --default-transition-duration: 0.15s; - --default-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + --default-transition-duration: .15s; + --default-transition-timing-function: cubic-bezier(.4, 0, .2, 1); } /* For readability concerns, you should choose a lighter palette in dark mode. */ -html[data-theme="dark"] { +html[data-theme='dark'] { --ifm-color-primary-lighter: var(--color-brand-300); --ifm-color-primary-light: var(--color-brand-400); --ifm-color-primary: var(--color-brand-500); @@ -69,7 +69,64 @@ html[data-theme="dark"] { padding-bottom: 40px; } +.navbar__logo img { + width: 100px; + height: auto; +} +@media (max-width: 996px) { + .navbar__inner { + align-items: center !important; + display: flex !important; + } + .navbar__logo { + display: flex; + align-items: center; + height: 48px; /* or the height of your navbar */ + } + .navbar__logo img { + height: auto; /* adjust as needed for your logo */ + width: auto; + } +} + +/* Add divider after logo */ +.navbar__brand::after { + content: '|'; + margin-left: 1rem; + color: var(--ifm-color-emphasis-500); +} + +.navbar__brand{ + margin-right: 0px !important; +} + +h1 { + font-size: 2rem; + line-height: 120%; +} +h2 { + font-size: 1.5rem; + line-height: 140%; +} +h3 { + font-size: 1.25rem; + line-height: 140%; +} + +h4{ + line-height: 140%; +} + +h5{ + line-height: 140%; +} + +h6{ + line-height: 140%; +} + @media (min-width: 1536px) { + body, .navbar { display: flex; @@ -79,13 +136,13 @@ html[data-theme="dark"] { } } -[data-theme="light"] body, -[data-theme="light"] .navbar { +[data-theme='light'] body, +[data-theme='light'] .navbar { background-color: rgb(250 250 250); } -[data-theme="dark"] body, -[data-theme="dark"] .navbar { +[data-theme='dark'] body, +[data-theme='dark'] .navbar { background-color: var(--ifm-background-color); } @@ -109,18 +166,18 @@ html[data-theme="dark"] { padding-block-end: 0.5rem; } -.theme-doc-sidebar-item-category-level-1 > .menu__list-item-collapsible > .menu__link--sublist { +.theme-doc-sidebar-item-category-level-1>.menu__list-item-collapsible>.menu__link--sublist { font-size: 13px; font-weight: 600; line-height: 20px; color: #213547; } -[data-theme="dark"] .theme-doc-sidebar-item-category-level-1 > .menu__list-item-collapsible > .menu__link--sublist { +[data-theme='dark'] .theme-doc-sidebar-item-category-level-1>.menu__list-item-collapsible>.menu__link--sublist { color: rgb(255, 255, 255, 0.87); } -[data-theme="dark"] .menu__link { +[data-theme='dark'] .menu__link { color: var(--color-neutral-400); font-size: 13px; font-weight: 500; @@ -158,10 +215,7 @@ html[data-theme="dark"] { color: var(--ifm-color-primary); } -[data-theme="dark"] - .theme-doc-sidebar-item-category-level-2:has(> ul.menu__list > li > a.menu__link--active) - > .menu__list-item-collapsible - > a.menu__link { +[data-theme='dark'] .theme-doc-sidebar-item-category-level-2:has(> ul.menu__list > li > a.menu__link--active)>.menu__list-item-collapsible>a.menu__link { border-left-style: none; border-left-width: 0px; color: rgb(235, 235, 235, 0.6); @@ -175,9 +229,7 @@ html[data-theme="dark"] { padding: 6px 12px !important; } -.theme-doc-sidebar-item-category-level-2:has(> ul.menu__list > li > a.menu__link--active) - > .menu__list-item-collapsible - > a.menu__link { +.theme-doc-sidebar-item-category-level-2:has(> ul.menu__list > li > a.menu__link--active)>.menu__list-item-collapsible>a.menu__link { border-left-style: none; border-left-width: 0px; color: rgba(60, 60, 60, 0.7); @@ -191,26 +243,21 @@ html[data-theme="dark"] { padding: 6px 12px !important; } -[data-theme="dark"] .menu__link:not(.menu__link--active):hover { +[data-theme='dark'] .menu__link:not(.menu__link--active):hover { color: rgb(255, 255, 255); font-size: 13px; font-weight: 600; transition: color 0.5s; } -[data-theme="dark"] - .theme-doc-sidebar-item-category-level-2:has(> ul.menu__list > li > a.menu__link--active) - > .menu__list-item-collapsible - > a.menu__link:hover { +[data-theme='dark'] .theme-doc-sidebar-item-category-level-2:has(> ul.menu__list > li > a.menu__link--active)>.menu__list-item-collapsible>a.menu__link:hover { color: rgb(255, 255, 255); font-size: 13px; font-weight: 600; transition: color 0.5s; } -.theme-doc-sidebar-item-category-level-2:has(> ul.menu__list > li > a.menu__link--active) - > .menu__list-item-collapsible - > a.menu__link:hover { +.theme-doc-sidebar-item-category-level-2:has(> ul.menu__list > li > a.menu__link--active)>.menu__list-item-collapsible>a.menu__link:hover { color: #213447; font-size: 13px; font-weight: 600; @@ -226,8 +273,7 @@ html[data-theme="dark"] { .theme-doc-sidebar-menu .menu__list-item .menu__caret:before, .theme-doc-sidebar-menu .menu__list-item .menu__link--sublist:after { - background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTEyIDEwIDggNmwtNCA0IiBzdHJva2U9IiMxQjI3MzgiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz48L3N2Zz4=) - 50% no-repeat; + background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTEyIDEwIDggNmwtNCA0IiBzdHJva2U9IiMxQjI3MzgiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz48L3N2Zz4=) 50% no-repeat; height: 1.5rem; min-height: 29.5px; min-width: 1.5rem; @@ -235,11 +281,7 @@ html[data-theme="dark"] { background-size: 12px 12px; } -.theme-doc-sidebar-item-category-level-1 - > .menu__list-item-collapsible - > .menu__link--sublist.theme-doc-sidebar-menu - .menu__list-item - .menu__caret:before, +.theme-doc-sidebar-item-category-level-1>.menu__list-item-collapsible>.menu__link--sublist.theme-doc-sidebar-menu .menu__list-item .menu__caret:before, .theme-doc-sidebar-menu .menu__list-item .menu__link--sublist:after { background: none; height: 1.5rem; @@ -248,14 +290,9 @@ html[data-theme="dark"] { width: 1.5rem; } -.theme-doc-sidebar-item-category-level-3 - > .menu__list-item-collapsible - > .menu__link--sublist.theme-doc-sidebar-menu - .menu__list-item - .menu__caret:before, +.theme-doc-sidebar-item-category-level-3>.menu__list-item-collapsible>.menu__link--sublist.theme-doc-sidebar-menu .menu__list-item .menu__caret:before, .theme-doc-sidebar-menu .menu__list-item .menu__link--sublist:after { - background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTEyIDEwIDggNmwtNCA0IiBzdHJva2U9IiMxQjI3MzgiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz48L3N2Zz4=) - 50% no-repeat; + background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTEyIDEwIDggNmwtNCA0IiBzdHJva2U9IiMxQjI3MzgiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz48L3N2Zz4=) 50% no-repeat; height: 1.5rem; min-height: 29.5px; min-width: 1.5rem; @@ -264,17 +301,17 @@ html[data-theme="dark"] { background-size: 12px 12px; } -.theme-doc-sidebar-item-category-level-2 > .menu__list-item-collapsible > .menu__link--sublist { +.theme-doc-sidebar-item-category-level-2>.menu__list-item-collapsible>.menu__link--sublist { margin-bottom: -6px; margin-top: -6px; } -.theme-doc-sidebar-item-category-level-3 > .menu__list-item-collapsible > .menu__link--sublist { +.theme-doc-sidebar-item-category-level-3>.menu__list-item-collapsible>.menu__link--sublist { margin-bottom: -6px; margin-top: -6px; } -.theme-doc-sidebar-item-category-level-4 > .menu__list-item-collapsible > .menu__link--sublist { +.theme-doc-sidebar-item-category-level-4>.menu__list-item-collapsible>.menu__link--sublist { margin-bottom: -6px; margin-top: -6px; } @@ -329,7 +366,7 @@ h3.description { margin-top: 0px; } -#__docusaurus > nav > div.navbar__inner > div.navbar__items.navbar__items--right > a:nth-child(4) { +#__docusaurus>nav>div.navbar__inner>div.navbar__items.navbar__items--right>a:nth-child(4) { background-color: var(--plane-call-to-action-color); border-radius: 8px; color: #f3f5f7; @@ -343,15 +380,15 @@ h3.description { line-height: 1.25rem; } -[data-theme="dark"] #__docusaurus > nav > div.navbar__inner > div.navbar__items.navbar__items--right > a:nth-child(4) { +[data-theme="dark"] #__docusaurus>nav>div.navbar__inner>div.navbar__items.navbar__items--right>a:nth-child(4) { color: var(--ifm-color-emphasis-800); } -#__docusaurus > nav > div.navbar__inner > div.navbar__items.navbar__items--right > a:nth-child(4):hover { +#__docusaurus>nav>div.navbar__inner>div.navbar__items.navbar__items--right>a:nth-child(4):hover { background-color: var(--plane-call-to-action-hover-color); } -#__docusaurus > nav > div.navbar__inner > div.navbar__items.navbar__items--right > a > svg { +#__docusaurus>nav>div.navbar__inner>div.navbar__items.navbar__items--right>a>svg { display: none; } @@ -374,16 +411,14 @@ h3.description { } .navbar--github-link:before { - content: ""; + content: ''; height: 100%; display: block; - background: url("data:image/svg+xml,%3Csvg fill='%23505050' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E") - no-repeat; + background: url("data:image/svg+xml,%3Csvg fill='%23505050' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E") no-repeat; } -html[data-theme="dark"] .navbar--github-link:before { - background: url("data:image/svg+xml,%3Csvg fill='%23ebebeb99' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E") - no-repeat; +html[data-theme='dark'] .navbar--github-link:before { + background: url("data:image/svg+xml,%3Csvg fill='%23ebebeb99' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E") no-repeat; } .navbar--discord-link { @@ -401,13 +436,13 @@ html[data-theme="dark"] .navbar--github-link:before { } .navbar--discord-link:before { - content: ""; + content: ''; height: 100%; display: block; background: url("data:image/svg+xml,%3Csvg fill='%23505050' viewBox='0 0 32 32' version='1.1' xmlns='http://www.w3.org/2000/svg'%3E%3Ctitle%3Ediscord%3C/title%3E%3Cpath d='M20.992 20.163c-1.511-0.099-2.699-1.349-2.699-2.877 0-0.051 0.001-0.102 0.004-0.153l-0 0.007c-0.003-0.048-0.005-0.104-0.005-0.161 0-1.525 1.19-2.771 2.692-2.862l0.008-0c1.509 0.082 2.701 1.325 2.701 2.847 0 0.062-0.002 0.123-0.006 0.184l0-0.008c0.003 0.050 0.005 0.109 0.005 0.168 0 1.523-1.191 2.768-2.693 2.854l-0.008 0zM11.026 20.163c-1.511-0.099-2.699-1.349-2.699-2.877 0-0.051 0.001-0.102 0.004-0.153l-0 0.007c-0.003-0.048-0.005-0.104-0.005-0.161 0-1.525 1.19-2.771 2.692-2.862l0.008-0c1.509 0.082 2.701 1.325 2.701 2.847 0 0.062-0.002 0.123-0.006 0.184l0-0.008c0.003 0.048 0.005 0.104 0.005 0.161 0 1.525-1.19 2.771-2.692 2.862l-0.008 0zM26.393 6.465c-1.763-0.832-3.811-1.49-5.955-1.871l-0.149-0.022c-0.005-0.001-0.011-0.002-0.017-0.002-0.035 0-0.065 0.019-0.081 0.047l-0 0c-0.234 0.411-0.488 0.924-0.717 1.45l-0.043 0.111c-1.030-0.165-2.218-0.259-3.428-0.259s-2.398 0.094-3.557 0.275l0.129-0.017c-0.27-0.63-0.528-1.142-0.813-1.638l0.041 0.077c-0.017-0.029-0.048-0.047-0.083-0.047-0.005 0-0.011 0-0.016 0.001l0.001-0c-2.293 0.403-4.342 1.060-6.256 1.957l0.151-0.064c-0.017 0.007-0.031 0.019-0.040 0.034l-0 0c-2.854 4.041-4.562 9.069-4.562 14.496 0 0.907 0.048 1.802 0.141 2.684l-0.009-0.11c0.003 0.029 0.018 0.053 0.039 0.070l0 0c2.14 1.601 4.628 2.891 7.313 3.738l0.176 0.048c0.008 0.003 0.018 0.004 0.028 0.004 0.032 0 0.060-0.015 0.077-0.038l0-0c0.535-0.72 1.044-1.536 1.485-2.392l0.047-0.1c0.006-0.012 0.010-0.027 0.010-0.043 0-0.041-0.026-0.075-0.062-0.089l-0.001-0c-0.912-0.352-1.683-0.727-2.417-1.157l0.077 0.042c-0.029-0.017-0.048-0.048-0.048-0.083 0-0.031 0.015-0.059 0.038-0.076l0-0c0.157-0.118 0.315-0.24 0.465-0.364 0.016-0.013 0.037-0.021 0.059-0.021 0.014 0 0.027 0.003 0.038 0.008l-0.001-0c2.208 1.061 4.8 1.681 7.536 1.681s5.329-0.62 7.643-1.727l-0.107 0.046c0.012-0.006 0.025-0.009 0.040-0.009 0.022 0 0.043 0.008 0.059 0.021l-0-0c0.15 0.124 0.307 0.248 0.466 0.365 0.023 0.018 0.038 0.046 0.038 0.077 0 0.035-0.019 0.065-0.046 0.082l-0 0c-0.661 0.395-1.432 0.769-2.235 1.078l-0.105 0.036c-0.036 0.014-0.062 0.049-0.062 0.089 0 0.016 0.004 0.031 0.011 0.044l-0-0.001c0.501 0.96 1.009 1.775 1.571 2.548l-0.040-0.057c0.017 0.024 0.046 0.040 0.077 0.040 0.010 0 0.020-0.002 0.029-0.004l-0.001 0c2.865-0.892 5.358-2.182 7.566-3.832l-0.065 0.047c0.022-0.016 0.036-0.041 0.039-0.069l0-0c0.087-0.784 0.136-1.694 0.136-2.615 0-5.415-1.712-10.43-4.623-14.534l0.052 0.078c-0.008-0.016-0.022-0.029-0.038-0.036l-0-0z'%3E%3C/path%3E%3C/svg%3E"); } -html[data-theme="dark"] .navbar--discord-link:before { +html[data-theme='dark'] .navbar--discord-link:before { background: url("data:image/svg+xml,%3Csvg fill='%23ebebeb99' viewBox='0 -28.5 256 256' version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' preserveAspectRatio='xMidYMid'%3E%3Cg%3E%3Cpath d='M216.856339 16.5966031 C200.285002 8.84328665 182.566144 3.2084988 164.041564 0 C161.766523 4.11318106 159.108624 9.64549908 157.276099 14.0464379 C137.583995 11.0849896 118.072967 11.0849896 98.7430163 14.0464379 C96.9108417 9.64549908 94.1925838 4.11318106 91.8971895 0 C73.3526068 3.2084988 55.6133949 8.86399117 39.0420583 16.6376612 C5.61752293 67.146514 -3.4433191 116.400813 1.08711069 164.955721 C23.2560196 181.510915 44.7403634 191.567697 65.8621325 198.148576 C71.0772151 190.971126 75.7283628 183.341335 79.7352139 175.300261 C72.104019 172.400575 64.7949724 168.822202 57.8887866 164.667963 C59.7209612 163.310589 61.5131304 161.891452 63.2445898 160.431257 C105.36741 180.133187 151.134928 180.133187 192.754523 160.431257 C194.506336 161.891452 196.298154 163.310589 198.110326 164.667963 C191.183787 168.842556 183.854737 172.420929 176.223542 175.320965 C180.230393 183.341335 184.861538 190.991831 190.096624 198.16893 C211.238746 191.588051 232.743023 181.531619 254.911949 164.955721 C260.227747 108.668201 245.831087 59.8662432 216.856339 16.5966031 Z M85.4738752 135.09489 C72.8290281 135.09489 62.4592217 123.290155 62.4592217 108.914901 C62.4592217 94.5396472 72.607595 82.7145587 85.4738752 82.7145587 C98.3405064 82.7145587 108.709962 94.5189427 108.488529 108.914901 C108.508531 123.290155 98.3405064 135.09489 85.4738752 135.09489 Z M170.525237 135.09489 C157.88039 135.09489 147.510584 123.290155 147.510584 108.914901 C147.510584 94.5396472 157.658606 82.7145587 170.525237 82.7145587 C183.391518 82.7145587 193.761324 94.5189427 193.539891 108.914901 C193.539891 123.290155 183.391518 135.09489 170.525237 135.09489 Z' fill-rule='nonzero'%3E%3C/path%3E%3C/g%3E%3C/svg%3E"); } @@ -426,14 +461,14 @@ html[data-theme="dark"] .navbar--discord-link:before { } .navbar--plane-docs-link:before { - content: ""; + content: ''; height: 100%; display: block; background-image: url("data:image/svg+xml,%3Csvg fill='%23505050' stroke-width='0' viewBox='0 0 448 512' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath%20d=%22M448%20360V24c0-13.3-10.7-24-24-24H96C43%200%200%2043%200%2096v320c0%2053%2043%2096%2096%2096h328c13.3%200%2024-10.7%2024-24v-16c0-7.5-3.5-14.3-8.9-18.7-4.2-15.4-4.2-59.3%200-74.7%205.4-4.3%208.9-11.1%208.9-18.6zM128%20134c0-3.3%202.7-6%206-6h212c3.3%200%206%202.7%206%206v20c0%203.3-2.7%206-6%206H134c-3.3%200-6-2.7-6-6v-20zm0%2064c0-3.3%202.7-6%206-6h212c3.3%200%206%202.7%206%206v20c0%203.3-2.7%206-6%206H134c-3.3%200-6-2.7-6-6v-20zm253.4%20250H96c-17.7%200-32-14.3-32-32%200-17.6%2014.4-32%2032-32h285.4c-1.9%2017.1-1.9%2046.9%200%2064z%22%3E%3C/path%3E%3C/svg%3E"); background-repeat: no-repeat; } -html[data-theme="dark"] .navbar--plane-docs-link:before { +html[data-theme='dark'] .navbar--plane-docs-link:before { background-image: url("data:image/svg+xml,%3Csvg fill='%23ebebeb99' stroke-width='0' viewBox='0 0 448 512' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath%20d=%22M448%20360V24c0-13.3-10.7-24-24-24H96C43%200%200%2043%200%2096v320c0%2053%2043%2096%2096%2096h328c13.3%200%2024-10.7%2024-24v-16c0-7.5-3.5-14.3-8.9-18.7-4.2-15.4-4.2-59.3%200-74.7%205.4-4.3%208.9-11.1%208.9-18.6zM128%20134c0-3.3%202.7-6%206-6h212c3.3%200%206%202.7%206%206v20c0%203.3-2.7%206-6%206H134c-3.3%200-6-2.7-6-6v-20zm0%2064c0-3.3%202.7-6%206-6h212c3.3%200%206%202.7%206%206v20c0%203.3-2.7%206-6%206H134c-3.3%200-6-2.7-6-6v-20zm253.4%20250H96c-17.7%200-32-14.3-32-32%200-17.6%2014.4-32%2032-32h285.4c-1.9%2017.1-1.9%2046.9%200%2064z%22%3E%3C/path%3E%3C/svg%3E"); background-repeat: no-repeat; } @@ -575,8 +610,8 @@ html[data-theme="dark"] .one { } /* fix text ellipses for docusaurus index cards */ -.card p.text--truncate[class*="cardDescription_"] { - width: 100%; +.card p.text--truncate[class*='cardDescription_'] { + width: 100%; } html { @@ -589,6 +624,57 @@ code { font-family: var(--ifm-font-family-monospace), sans-serif; } +.navbar { + font-family: var(--ifm-font-family-base); +} + +.navbar__title { + font-family: var(--ifm-font-family-base); + font-weight: 600; +} + +.menu__link { + font-family: var(--ifm-font-family-base); +} + +.markdown { + font-family: var(--ifm-font-family-base); +} + +* { + font-family: var(--ifm-font-family-base); +} + +.theme-doc-markdown { + font-family: var(--ifm-font-family-base); +} + +pre { + font-family: var(--ifm-font-family-monospace); +} + +pre code { + font-family: var(--ifm-font-family-monospace); +} + +/* Inline code */ +.markdown code { + font-family: var(--ifm-font-family-monospace); +} + +/* Code blocks in different contexts */ +.prism-code { + font-family: var(--ifm-font-family-monospace); +} + +.codeBlockContainer_node_modules-\@docusaurus-theme-classic-lib-theme-CodeBlock-Container-styles-module { + font-family: var(--ifm-font-family-monospace); +} + +code, kbd, pre, samp { + font-family: var(--ifm-font-family-monospace) !important; +} + strong { color: rgb(18 27 46); } @@ -602,32 +688,32 @@ h6 { color: rgb(18 27 46); } -[data-theme="dark"] h1, -[data-theme="dark"] h2, -[data-theme="dark"] h3, -[data-theme="dark"] h4, -[data-theme="dark"] h5, -[data-theme="dark"] h6 { +[data-theme='dark'] h1, +[data-theme='dark'] h2, +[data-theme='dark'] h3, +[data-theme='dark'] h4, +[data-theme='dark'] h5, +[data-theme='dark'] h6 { color: var(--color-brand-25); } -html[data-theme="dark"] { +html[data-theme='dark'] { color: var(--color-brand-25); } -[data-theme="dark"] blockquote { +[data-theme='dark'] blockquote { color: var(--color-brand-25); } -[data-theme="dark"] code { +[data-theme='dark'] code { color: rgb(237 240 246); } -[data-theme="dark"] strong { +[data-theme='dark'] strong { color: rgb(243 246 251); } -[data-theme="dark"] .table-of-contents a { +[data-theme='dark'] .table-of-contents a { color: var(--color-neutral-400); } @@ -636,7 +722,8 @@ html[data-theme="dark"] { } @media (min-width: 768px) { - [class^="navbarSearchContainer_"], + + [class^='navbarSearchContainer_'], .navbarSearchContainer_node_modules-\@docusaurus-theme-classic-lib-theme-Navbar-Search-styles-module { margin-left: auto; margin-right: auto; @@ -653,16 +740,17 @@ html[data-theme="dark"] { border-radius: 0.5rem; } -[data-theme="dark"] .navbar__search-input { +[data-theme='dark'] .navbar__search-input { width: 25rem; background-color: var(--docsearch-searchbox-background); border: 1px solid #9e9e9e33 !important; } -[data-theme="dark"] .navbar__search-input::placeholder { +[data-theme='dark'] .navbar__search-input::placeholder { color: rgb(235, 235, 235, 0.6); } + .DocSearch-Button { border-radius: 0.5em !important; border-color: rgb(158 158 158 / 20%) !important; @@ -671,7 +759,7 @@ html[data-theme="dark"] { background-color: #fafafa !important; } -[data-theme="dark"] .DocSearch-Button { +[data-theme='dark'] .DocSearch-Button { border-radius: 0.5em !important; border-color: rgb(158 158 158 / 20%) !important; border-width: 1px !important; @@ -697,7 +785,7 @@ html[data-theme="dark"] { box-shadow: none; } -[data-theme="dark"] .navbar { +[data-theme='dark'] .navbar { border-bottom: 1px solid rgb(206 206 206 / 6%); box-shadow: none; } @@ -740,11 +828,10 @@ html[data-theme="dark"] { font-weight: 400; --tw-numeric-spacing: tabular-nums; width: 100%; - font-variant-numeric: var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) - var(--tw-numeric-fraction); + font-variant-numeric: var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction); } -[data-theme="dark"] table tr:nth-child(2n) { +[data-theme='dark'] table tr:nth-child(2n) { background-color: rgb(27 27 29); } @@ -775,7 +862,7 @@ table thead tr { margin: 10px; } -img[src*="#center"], +img[src*='#center'], img.center { display: block; border-radius: 0.75rem; @@ -789,8 +876,8 @@ img.center { background-color: rgb(226 226 226); } -[data-theme="dark"] img[src*="#center"], -[data-theme="dark"] img.center { +[data-theme='dark'] img[src*='#center'], +[data-theme='dark'] img.center { display: block; border-radius: 0.75rem; float: none; @@ -838,7 +925,7 @@ html:not([data-theme]) .themedComponent--light_NVdE { background: none; } -[data-theme="dark"] .clean-btn { +[data-theme='dark'] .clean-btn { color: rgb(159 160 160); } @@ -852,12 +939,13 @@ a.footer__link-item:hover { text-decoration: underline; } -[data-theme="dark"] .footer--dark { +[data-theme='dark'] .footer--dark { --ifm-footer-background-color: var(--plane-footer-background-color); --ifm-footer-link-color: var(--plane-footer-link-color); } @media (min-width: 1455px) { + body, .navbar { width: 100%; @@ -873,7 +961,7 @@ a.footer__link-item:hover { padding-bottom: 1rem; } -.openapi-params__list-item > strong { +.openapi-params__list-item>strong { display: inline-block; padding-top: 0.5rem; } @@ -901,3 +989,134 @@ a.footer__link-item:hover { .openapi-explorer__request-form .openapi-explorer__request-btn:hover { background-color: var(--plane-call-to-action-hover-color); } + + + +@font-face { + font-family: 'Nacelle'; + src: url('/fonts/nacelle/nacelle-light.otf') format('opentype'); + font-weight: 300; + font-style: normal; + font-display: swap; + } + + @font-face { + font-family: 'Nacelle'; + src: url('/fonts/nacelle/nacelle-regular.otf') format('opentype'); + font-weight: 400; + font-style: normal; + font-display: swap; + } + + @font-face { + font-family: 'Nacelle'; + src: url('/fonts/nacelle/nacelle-semibold.otf') format('opentype'); + font-weight: 500; + font-style: normal; + font-display: swap; + } + + @font-face { + font-family: 'Nacelle'; + src: url('/fonts/nacelle/nacelle-bold.otf') format('opentype'); + font-weight: 600; + font-style: normal; + font-display: swap; + } + + @font-face { + font-family: 'IBM Mono'; + src: url('/fonts/ibm/ibmplexmono-light.ttf') format('truetype'); + font-weight: 300; + font-style: normal; + font-display: swap; + } + + @font-face { + font-family: 'IBM Mono'; + src: url('/fonts/ibm/ibmplexmono-regular.ttf') format('truetype'); + font-weight: 400; + font-style: normal; + font-display: swap; +} + +@font-face { + font-family: 'IBM Mono'; + src: url('/fonts/ibm/ibmplexmono-semibold.ttf') format('truetype'); + font-weight: 500; + font-style: normal; + font-display: swap; +} + +@font-face { + font-family: 'IBM Mono'; + src: url('/fonts/ibm/ibmplexmono-bold.ttf') format('truetype'); + font-weight: 600; + font-style: normal; + font-display: swap; +} + + :root { + --ifm-font-family-base: 'Nacelle'; + --ifm-heading-font-family: 'Nacelle'; + --ifm-font-family-monospace: 'IBM Mono'; + } + + #__docusaurus>nav>div.navbar__inner>div.navbar__items.navbar__items--right>a:nth-child(3) { + background-color: #006399; + border-radius: 8px; + color: #f3f5f7; + font-size: .75rem; + font-weight: 500; + line-height: 1.25rem; + margin-left: .5rem; + margin-right: .5rem; + padding: .375rem 1rem; + text-transform: capitalize; + white-space: nowrap; +} + +@media (min-width: 768px) { + .navbarSearchContainer_node_modules-\@docusaurus-theme-classic-lib-theme-Navbar-Search-styles-module { + margin-left: auto; + margin-right: auto; + } + + .navbarSearchContainer_Bca1 { + margin-left: auto; + margin-right: auto; + } + + .navbar__search-input { + width: 400px; + } +} + +.navbar__search-input { + border-radius: 0.5em !important; + border-color: rgb(158 158 158 / 20%) !important; + border-width: 1px !important; + border-style: solid !important; + background-color: #fafafa !important; +} + +[data-theme="dark"] .navbar__search-input { + border-radius: 0.5em !important; + border-color: rgb(158 158 158 / 20%) !important; + border-width: 1px !important; + border-style: solid !important; + background-color: #1b1b1d !important; +} + +.DocSearch-Search-Icon { + width: 14px; + height: 14px; +} + +.DocSearch-Button-Container { + gap: 0.25rem; +} + +.DocSearch-Button-Placeholder { + font-size: 0.85rem !important; +} \ No newline at end of file diff --git a/src/css/fonts.css b/src/css/fonts.css index 512139e..4114420 100644 --- a/src/css/fonts.css +++ b/src/css/fonts.css @@ -1,55 +1,55 @@ @font-face { font-display: swap; - font-family: "Nacelle"; + font-family: 'Nacelle'; font-style: normal; font-weight: 300; - src: url("/fonts/nacelle/nacelle-light.otf") format("opentype"); + src: url('/fonts/nacelle/nacelle-light.otf') format('opentype'); } @font-face { font-display: swap; - font-family: "Nacelle"; + font-family: 'Nacelle'; font-style: normal; font-weight: 400; - src: url("/fonts/nacelle/nacelle-regular.otf") format("opentype"); + src: url('/fonts/nacelle/nacelle-regular.otf') format('opentype'); } @font-face { font-display: swap; - font-family: "Nacelle"; + font-family: 'Nacelle'; font-style: normal; font-weight: 600; - src: url("/fonts/nacelle/nacelle-semibold.otf") format("opentype"); + src: url('/fonts/nacelle/nacelle-semibold.otf') format('opentype'); } @font-face { font-display: swap; - font-family: "IBM Mono"; + font-family: 'IBM Mono'; font-style: normal; font-weight: 300; - src: url("/fonts/ibm/ibmplexmono-light.ttf") format("truetype"); + src: url('/fonts/ibm/ibmplexmono-light.ttf') format('truetype'); } @font-face { font-display: swap; - font-family: "IBM Mono"; + font-family: 'IBM Mono'; font-style: normal; font-weight: 400; - src: url("/fonts/ibm/ibmplexmono-regular.ttf") format("truetype"); + src: url('/fonts/ibm/ibmplexmono-regular.ttf') format('truetype'); } @font-face { font-display: swap; - font-family: "IBM Mono"; + font-family: 'IBM Mono'; font-style: normal; font-weight: 500; - src: url("/fonts/ibm/ibmplexmono-semibold.ttf") format("truetype"); + src: url('/fonts/ibm/ibmplexmono-semibold.ttf') format('truetype'); } @font-face { font-display: swap; - font-family: "IBM Mono"; + font-family: 'IBM Mono'; font-style: normal; font-weight: 600; - src: url("/fonts/ibm/ibmplexmono-bold.ttf") format("truetype"); + src: url('/fonts/ibm/ibmplexmono-bold.ttf') format('truetype'); } diff --git a/vercel.json b/vercel.json index d4ae5d7..fbe3992 100644 --- a/vercel.json +++ b/vercel.json @@ -34,13 +34,13 @@ "source": "/self-hosting/manage/upgrade-plane", "destination": "/self-hosting/manage/update/to-latest-version" }, - { - "source": "/webhooks/intro-webhooks", - "destination": "/webhooks/overview" - }, { "source": "/api-reference/(.*)", "destination": "/api" + }, + { + "source": "/api-reference/byoa/build-plane-app", + "destination": "/apps/build-plane-app" } ] }