From dea6a4fd1d4731663b8fec4e2279a090fc04905f Mon Sep 17 00:00:00 2001 From: vihar Date: Sun, 16 Aug 2026 22:38:33 +0530 Subject: [PATCH 1/2] docs: extract shared Plane docs theme (plane/) and align toolchain with docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Unify the look of developers.plane.so and docs.plane.so: - Move the theme into docs/.vitepress/theme/plane/ — the shared Plane docs theme, byte-identical with makeplane/docs (tokens, fonts, PlaneHeader, layout, Card/CardGroup/Tags, Copy page menu, cookie consent, createPlaneTheme()). theme/index.ts is now a thin createPlaneTheme() call with this site's branding + API components; site.css keeps the home hero. - Doc headings now render in Inter (VoidZero's APK Protocol was leaking through --font-heading); ::: details blocks are neutral; [!CAUTION] is styled; --vp-c-brand-2 is the hover color; hero-image corner variants render correctly. - PlaneHeader is generalized: header buttons come from nav items flagged planeButton: "primary" | "secondary" (was a docs.plane.so regex) and the mobile wordmark comes from options. Drop PlaneSearchButton.vue + its Vite alias (dead: the vendored VPNavBarSearch imports the button relatively) and the inert header data-theme head script. - theme-color meta #3b82f6 → #006399, editLink → master, home prev/next: false. - Toolchain: lucide-vue-next ^0.577.0, engines node >=24, CI node 24; prettier override for plane/** (printWidth 100, trailingComma all) so the shared files are byte-identical under docs' oxfmt too. - Guards: check:theme-sync (sha256 diff of plane/ against makeplane/docs) wired into CI; tsconfig/env.d.ts point at the shared type shims. Co-Authored-By: Claude Fable 5 --- .github/workflows/check-format.yml | 16 +- .prettierrc | 11 +- AGENTS.md | 20 +- docs/.vitepress/config.mts | 23 +- docs/.vitepress/env.d.ts | 13 +- docs/.vitepress/theme/Layout.d.vue.ts | 4 - .../theme/components/ApiParam.d.vue.ts | 4 - .../.vitepress/theme/components/Card.d.vue.ts | 4 - docs/.vitepress/theme/components/Card.vue | 225 ----- .../theme/components/CardGroup.d.vue.ts | 4 - .../.vitepress/theme/components/CardGroup.vue | 33 - .../theme/components/CodePanel.d.vue.ts | 4 - .../theme/components/CookieConsent.d.vue.ts | 4 - .../theme/components/PlaneSearchButton.vue | 72 -- .../theme/components/ResponsePanel.d.vue.ts | 4 - .../theme/components/card-brand-icons.js | 27 - docs/.vitepress/theme/index.ts | 218 +---- docs/.vitepress/theme/plane-overrides.css | 526 ----------- docs/.vitepress/theme/plane-ui.css | 892 ------------------ docs/.vitepress/theme/plane/README.md | 16 + .../theme/plane/components/Card.vue | 81 ++ .../theme/plane/components/CardGroup.vue | 20 + .../{ => plane}/components/CookieConsent.vue | 3 +- .../{ => plane}/components/CopyPageMenu.vue | 24 +- .../{ => plane}/components/PlaneHeader.vue | 123 ++- .../theme/{ => plane}/components/Tags.vue | 16 +- .../plane/components/card-brand-icons.ts | 25 + .../{ => plane}/components/copy-page-icons.ts | 0 docs/.vitepress/theme/plane/css/api.css | 124 +++ docs/.vitepress/theme/plane/css/base.css | 123 +++ .../.vitepress/theme/plane/css/components.css | 596 ++++++++++++ docs/.vitepress/theme/plane/css/fonts.css | 41 + docs/.vitepress/theme/plane/css/index.css | 21 + docs/.vitepress/theme/plane/css/layout.css | 442 +++++++++ docs/.vitepress/theme/plane/css/tokens.css | 202 ++++ docs/.vitepress/theme/plane/index.ts | 201 ++++ .../theme/{ => plane/layout}/Layout.vue | 23 +- .../layout}/default-layout.ts | 0 .../{voidzero => plane/layout}/doc-layout.vue | 0 .../{voidzero => plane/layout}/header.ts | 0 .../theme/{voidzero => plane/layout}/slots.ts | 0 .../{voidzero => plane/layout}/top-banner.ts | 0 docs/.vitepress/theme/plane/manifest.json | 35 + docs/.vitepress/theme/plane/options.ts | 30 + .../theme/plane/scripts/check-theme-sync.mjs | 121 +++ .../plane/types/shims.d.ts} | 9 +- .../theme/plane/types/vitepress-augment.d.ts | 20 + .../{ => theme/plane}/types/voidzero-theme.ts | 5 +- .../plane}/types/vp-theme-modules.d.ts | 5 + docs/.vitepress/theme/site.css | 30 + docs/.vitepress/theme/styles.css | 274 ------ docs/.vitepress/theme/tsconfig.json | 4 - docs/.vitepress/tsconfig.json | 12 +- docs/.vitepress/types/vitest-theme.ts | 4 - docs/index.md | 2 + package.json | 12 +- pnpm-lock.yaml | 317 ++++--- 57 files changed, 2508 insertions(+), 2557 deletions(-) delete mode 100644 docs/.vitepress/theme/Layout.d.vue.ts delete mode 100644 docs/.vitepress/theme/components/ApiParam.d.vue.ts delete mode 100644 docs/.vitepress/theme/components/Card.d.vue.ts delete mode 100644 docs/.vitepress/theme/components/Card.vue delete mode 100644 docs/.vitepress/theme/components/CardGroup.d.vue.ts delete mode 100644 docs/.vitepress/theme/components/CardGroup.vue delete mode 100644 docs/.vitepress/theme/components/CodePanel.d.vue.ts delete mode 100644 docs/.vitepress/theme/components/CookieConsent.d.vue.ts delete mode 100644 docs/.vitepress/theme/components/PlaneSearchButton.vue delete mode 100644 docs/.vitepress/theme/components/ResponsePanel.d.vue.ts delete mode 100644 docs/.vitepress/theme/components/card-brand-icons.js delete mode 100644 docs/.vitepress/theme/plane-overrides.css delete mode 100644 docs/.vitepress/theme/plane-ui.css create mode 100644 docs/.vitepress/theme/plane/README.md create mode 100644 docs/.vitepress/theme/plane/components/Card.vue create mode 100644 docs/.vitepress/theme/plane/components/CardGroup.vue rename docs/.vitepress/theme/{ => plane}/components/CookieConsent.vue (98%) rename docs/.vitepress/theme/{ => plane}/components/CopyPageMenu.vue (95%) rename docs/.vitepress/theme/{ => plane}/components/PlaneHeader.vue (86%) rename docs/.vitepress/theme/{ => plane}/components/Tags.vue (50%) create mode 100644 docs/.vitepress/theme/plane/components/card-brand-icons.ts rename docs/.vitepress/theme/{ => plane}/components/copy-page-icons.ts (100%) create mode 100644 docs/.vitepress/theme/plane/css/api.css create mode 100644 docs/.vitepress/theme/plane/css/base.css create mode 100644 docs/.vitepress/theme/plane/css/components.css create mode 100644 docs/.vitepress/theme/plane/css/fonts.css create mode 100644 docs/.vitepress/theme/plane/css/index.css create mode 100644 docs/.vitepress/theme/plane/css/layout.css create mode 100644 docs/.vitepress/theme/plane/css/tokens.css create mode 100644 docs/.vitepress/theme/plane/index.ts rename docs/.vitepress/theme/{ => plane/layout}/Layout.vue (71%) rename docs/.vitepress/theme/{voidzero => plane/layout}/default-layout.ts (100%) rename docs/.vitepress/theme/{voidzero => plane/layout}/doc-layout.vue (100%) rename docs/.vitepress/theme/{voidzero => plane/layout}/header.ts (100%) rename docs/.vitepress/theme/{voidzero => plane/layout}/slots.ts (100%) rename docs/.vitepress/theme/{voidzero => plane/layout}/top-banner.ts (100%) create mode 100644 docs/.vitepress/theme/plane/manifest.json create mode 100644 docs/.vitepress/theme/plane/options.ts create mode 100644 docs/.vitepress/theme/plane/scripts/check-theme-sync.mjs rename docs/.vitepress/{types/ambient-modules.d.ts => theme/plane/types/shims.d.ts} (78%) create mode 100644 docs/.vitepress/theme/plane/types/vitepress-augment.d.ts rename docs/.vitepress/{ => theme/plane}/types/voidzero-theme.ts (69%) rename docs/.vitepress/{ => theme/plane}/types/vp-theme-modules.d.ts (96%) create mode 100644 docs/.vitepress/theme/site.css delete mode 100644 docs/.vitepress/theme/styles.css delete mode 100644 docs/.vitepress/theme/tsconfig.json delete mode 100644 docs/.vitepress/types/vitest-theme.ts diff --git a/.github/workflows/check-format.yml b/.github/workflows/check-format.yml index f1648c07..0f81879e 100644 --- a/.github/workflows/check-format.yml +++ b/.github/workflows/check-format.yml @@ -22,7 +22,7 @@ jobs: - uses: actions/setup-node@v4 with: - node-version: 22 + node-version: 24 cache: pnpm - run: pnpm install --frozen-lockfile @@ -39,7 +39,7 @@ jobs: - uses: actions/setup-node@v4 with: - node-version: 22 + node-version: 24 cache: pnpm - run: pnpm install --frozen-lockfile @@ -47,3 +47,15 @@ jobs: - run: pnpm check:types - run: pnpm build + + theme-sync: + name: Shared theme in sync with makeplane/docs + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: 24 + + - run: node docs/.vitepress/theme/plane/scripts/check-theme-sync.mjs --sibling makeplane/docs diff --git a/.prettierrc b/.prettierrc index 1cca3514..5108741c 100644 --- a/.prettierrc +++ b/.prettierrc @@ -3,5 +3,14 @@ "singleQuote": false, "tabWidth": 2, "trailingComma": "es5", - "printWidth": 120 + "printWidth": 120, + "overrides": [ + { + "files": ["docs/.vitepress/theme/plane/**"], + "options": { + "printWidth": 100, + "trailingComma": "all" + } + } + ] } diff --git a/AGENTS.md b/AGENTS.md index a0a016bb..e70d36bb 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -18,6 +18,7 @@ pnpm preview # Preview production build pnpm check:format # Check Prettier formatting pnpm fix:format # Auto-fix Prettier formatting pnpm check:types # Type-check the VitePress config and theme +pnpm check:theme-sync # Verify docs/.vitepress/theme/plane/ is identical to makeplane/docs (THEME_SIBLING_PATH=../docs for a local checkout) ``` **CI checks on PRs** (to `master`): Prettier formatting + VitePress build must pass. @@ -26,7 +27,8 @@ pnpm check:types # Type-check the VitePress config and theme - **`docs/`** — All documentation content and VitePress config - **`docs/.vitepress/config.mts`** — Main VitePress config: navigation, sidebar structure, SEO, Algolia search, analytics. This is a large file that defines the entire site structure. - - **`docs/.vitepress/theme/`** — Custom theme (extends `@voidzero-dev/vitepress-theme` via `extendConfig`) with Vue components and global styles + - **`docs/.vitepress/theme/`** — `index.ts` calls `createPlaneTheme({...})` from `./plane` (this site's branding + API components); `site.css` holds site-only CSS + - **`docs/.vitepress/theme/plane/`** — **shared Plane docs theme**, byte-identical with `makeplane/docs` (tokens, fonts, header, layout, Card/CardGroup/Tags, Copy page menu, cookie consent). Edit in one repo, copy the folder to the sibling, run `pnpm check:theme-sync` in both; add new files to `plane/manifest.json`. Header buttons come from `themeConfig.nav` items flagged `planeButton: "primary" | "secondary"`. - **`docs/api-reference/`** — REST API endpoint docs (180+ endpoints across 30+ resource categories) - **`docs/self-hosting/`** — Deployment and configuration guides - **`docs/dev-tools/`** — Webhooks, OAuth apps, agents, MCP server docs @@ -69,15 +71,15 @@ docs/ ## Custom Vue Components -Used directly in markdown files — defined in `docs/.vitepress/theme/components/`: +Used directly in markdown files — API components in `docs/.vitepress/theme/components/`, shared ones in `docs/.vitepress/theme/plane/components/`: -| Component | Usage | -| ---------------------- | ----------------------------------------------------------------- | -| `` | API parameter with name, type, required badge, expandable details | -| `` | Multi-language code tabs (cURL, Python, JavaScript) | -| `` | Syntax-highlighted API response JSON | -| `` | Feature card with icon, title, description | -| `` | Responsive grid layout (2, 3, or 4 columns) | +| Component | Usage | +| ---------------------- | ---------------------------------------------------------------------------------------------------- | +| `` | API parameter with name, type, required badge, expandable details | +| `` | Multi-language code tabs (cURL, Python, JavaScript) | +| `` | Syntax-highlighted API response JSON | +| `` | Card: `title`, `icon` (brand key or Lucide), `href`/`link`, `description` or slot, `cta`/`link-text` | +| `` | Responsive grid layout (2, 3, or 4 columns) | ## API Documentation Pattern diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index d9402339..b162c61c 100644 --- a/docs/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -6,7 +6,6 @@ import { extendConfig } from "@voidzero-dev/vitepress-theme/config"; import llmstxt from "vitepress-plugin-llms"; import { readFileSync, readdirSync, statSync, mkdirSync, copyFileSync } from "node:fs"; import { resolve, join, relative, dirname } from "node:path"; -import { fileURLToPath } from "node:url"; function loadEnvVar(key: string): string | undefined { // process.env takes precedence (CI/hosting platforms set vars here) @@ -92,13 +91,6 @@ export default extendConfig( ], }), ], - resolve: { - alias: { - "@vp-default/VPNavBarSearchButton.vue": fileURLToPath( - new URL("./theme/components/PlaneSearchButton.vue", import.meta.url) - ), - }, - }, optimizeDeps: { include: [ "lucide-vue-next", @@ -170,13 +162,6 @@ export default extendConfig( }, ], - // Sync OSS header data-theme with html.dark after hydration - [ - "script", - {}, - `(function(){function s(){var d=document.documentElement.classList.contains("dark");document.querySelectorAll("header.wrapper").forEach(function(h){d?h.setAttribute("data-theme","dark"):h.removeAttribute("data-theme")})}s();new MutationObserver(s).observe(document.documentElement,{attributes:true,attributeFilter:["class"]})})();`, - ], - // Google Analytics with Consent Mode v2 ["script", { async: "", src: "https://www.googletagmanager.com/gtag/js?id=G-JF828SKW90" }], [ @@ -209,7 +194,7 @@ export default extendConfig( "plane, api, self-hosting, project management, developer documentation, kubernetes, docker, rest api, webhooks, plane api", }, ], - ["meta", { name: "theme-color", content: "#3b82f6" }], + ["meta", { name: "theme-color", content: "#006399" }], // Open Graph meta tags ["meta", { property: "og:type", content: "website" }], @@ -277,8 +262,8 @@ export default extendConfig( { text: "Self-hosting", link: "/self-hosting/overview" }, { text: "API Reference", link: "/api-reference/introduction" }, { text: "Build and extend", link: "/dev-tools/build-plane-app/overview" }, - { text: "Plane Docs", link: "https://docs.plane.so", noIcon: true }, - { text: "Sign in", link: "https://app.plane.so/sign-in" }, + { text: "Plane Docs", link: "https://docs.plane.so", noIcon: true, planeButton: "secondary" }, + { text: "Sign in", link: "https://app.plane.so/sign-in", noIcon: true, planeButton: "primary" }, ], sidebar: { @@ -1149,7 +1134,7 @@ export default extendConfig( search: searchConfig, editLink: { - pattern: "https://github.com/makeplane/developer-docs/edit/main/:path", + pattern: "https://github.com/makeplane/developer-docs/edit/master/:path", }, /*footer: { diff --git a/docs/.vitepress/env.d.ts b/docs/.vitepress/env.d.ts index 9ae6cdff..4f46c59b 100644 --- a/docs/.vitepress/env.d.ts +++ b/docs/.vitepress/env.d.ts @@ -1,13 +1,4 @@ /// /// -/// - -import type {} from "vitepress"; - -declare module "vitepress" { - namespace DefaultTheme { - interface Config { - variant?: "voidzero" | "viteplus" | "vite" | "vitest" | "rolldown" | "oxc"; - } - } -} +/// +/// diff --git a/docs/.vitepress/theme/Layout.d.vue.ts b/docs/.vitepress/theme/Layout.d.vue.ts deleted file mode 100644 index 9df0577c..00000000 --- a/docs/.vitepress/theme/Layout.d.vue.ts +++ /dev/null @@ -1,4 +0,0 @@ -import type { DefineComponent } from "vue"; - -declare const component: DefineComponent; -export default component; diff --git a/docs/.vitepress/theme/components/ApiParam.d.vue.ts b/docs/.vitepress/theme/components/ApiParam.d.vue.ts deleted file mode 100644 index 9df0577c..00000000 --- a/docs/.vitepress/theme/components/ApiParam.d.vue.ts +++ /dev/null @@ -1,4 +0,0 @@ -import type { DefineComponent } from "vue"; - -declare const component: DefineComponent; -export default component; diff --git a/docs/.vitepress/theme/components/Card.d.vue.ts b/docs/.vitepress/theme/components/Card.d.vue.ts deleted file mode 100644 index 9df0577c..00000000 --- a/docs/.vitepress/theme/components/Card.d.vue.ts +++ /dev/null @@ -1,4 +0,0 @@ -import type { DefineComponent } from "vue"; - -declare const component: DefineComponent; -export default component; diff --git a/docs/.vitepress/theme/components/Card.vue b/docs/.vitepress/theme/components/Card.vue deleted file mode 100644 index 10112734..00000000 --- a/docs/.vitepress/theme/components/Card.vue +++ /dev/null @@ -1,225 +0,0 @@ - - - - - diff --git a/docs/.vitepress/theme/components/CardGroup.d.vue.ts b/docs/.vitepress/theme/components/CardGroup.d.vue.ts deleted file mode 100644 index 9df0577c..00000000 --- a/docs/.vitepress/theme/components/CardGroup.d.vue.ts +++ /dev/null @@ -1,4 +0,0 @@ -import type { DefineComponent } from "vue"; - -declare const component: DefineComponent; -export default component; diff --git a/docs/.vitepress/theme/components/CardGroup.vue b/docs/.vitepress/theme/components/CardGroup.vue deleted file mode 100644 index d59258d9..00000000 --- a/docs/.vitepress/theme/components/CardGroup.vue +++ /dev/null @@ -1,33 +0,0 @@ - - - diff --git a/docs/.vitepress/theme/components/CodePanel.d.vue.ts b/docs/.vitepress/theme/components/CodePanel.d.vue.ts deleted file mode 100644 index 9df0577c..00000000 --- a/docs/.vitepress/theme/components/CodePanel.d.vue.ts +++ /dev/null @@ -1,4 +0,0 @@ -import type { DefineComponent } from "vue"; - -declare const component: DefineComponent; -export default component; diff --git a/docs/.vitepress/theme/components/CookieConsent.d.vue.ts b/docs/.vitepress/theme/components/CookieConsent.d.vue.ts deleted file mode 100644 index 9df0577c..00000000 --- a/docs/.vitepress/theme/components/CookieConsent.d.vue.ts +++ /dev/null @@ -1,4 +0,0 @@ -import type { DefineComponent } from "vue"; - -declare const component: DefineComponent; -export default component; diff --git a/docs/.vitepress/theme/components/PlaneSearchButton.vue b/docs/.vitepress/theme/components/PlaneSearchButton.vue deleted file mode 100644 index 36ff15b0..00000000 --- a/docs/.vitepress/theme/components/PlaneSearchButton.vue +++ /dev/null @@ -1,72 +0,0 @@ - - - - - diff --git a/docs/.vitepress/theme/components/ResponsePanel.d.vue.ts b/docs/.vitepress/theme/components/ResponsePanel.d.vue.ts deleted file mode 100644 index 9df0577c..00000000 --- a/docs/.vitepress/theme/components/ResponsePanel.d.vue.ts +++ /dev/null @@ -1,4 +0,0 @@ -import type { DefineComponent } from "vue"; - -declare const component: DefineComponent; -export default component; diff --git a/docs/.vitepress/theme/components/card-brand-icons.js b/docs/.vitepress/theme/components/card-brand-icons.js deleted file mode 100644 index 65cd6844..00000000 --- a/docs/.vitepress/theme/components/card-brand-icons.js +++ /dev/null @@ -1,27 +0,0 @@ -/** Brand SVG icons for Card (deployment methods, etc.) */ -export const cardBrandIcons = { - docker: ``, - kubernetes: ``, - podman: ` -`, - portainer: ` - - - - - - - -`, - coolify: ``, - - // Add more icons as needed -}; diff --git a/docs/.vitepress/theme/index.ts b/docs/.vitepress/theme/index.ts index a592b970..58780287 100644 --- a/docs/.vitepress/theme/index.ts +++ b/docs/.vitepress/theme/index.ts @@ -1,212 +1,40 @@ -import type { Theme } from "vitepress"; -import { onMounted, onUnmounted, watch, nextTick, h } from "vue"; -import { useRoute, useData } from "vitepress"; -import { enhanceAppWithTabs } from "vitepress-plugin-tabs/client"; -import mediumZoom from "medium-zoom"; -import VoidZeroTheme from "@voidzero-dev/vitepress-theme"; -import { themeContextKey } from "@voidzero-dev/vitepress-theme"; - -import "./styles.css"; -import "./plane-overrides.css"; -import "./plane-ui.css"; -import "vitepress-plugin-tabs/client"; - +/** + * developers.plane.so theme = shared Plane docs theme (./plane, byte-identical with + * makeplane/docs) + developer-docs specifics (API reference components/layout). + */ +import { onMounted, watch, nextTick } from "vue"; +import { useRoute } from "vitepress"; +import { createPlaneTheme } from "./plane"; import ApiParam from "./components/ApiParam.vue"; import CodePanel from "./components/CodePanel.vue"; import ResponsePanel from "./components/ResponsePanel.vue"; -import Card from "./components/Card.vue"; -import CardGroup from "./components/CardGroup.vue"; -import Tags from "./components/Tags.vue"; -import CookieConsent from "./components/CookieConsent.vue"; -import PlaneLayout from "./Layout.vue"; - -const PLANE_FOOTER_BG = "https://media.docs.plane.so/logo/og-docs.webp"; -const PLANE_MONO_ICON = "/logo/favicon-32x32.png"; +import "./site.css"; -function updateLayout() { +/** Toggle `.api-page` on `.VPDoc` for API reference pages (two-column layout, no aside). */ +function updateApiPageClass() { if (typeof document === "undefined") return; - const path = window.location.pathname; const isApiPage = path.includes("/api-reference/") && !path.endsWith("/introduction") && !path.endsWith("/introduction.html"); - - const vpDoc = document.querySelector(".VPDoc"); - if (vpDoc) { - vpDoc.classList.toggle("api-page", isApiPage); - } -} - -/** Keep OSS header data-theme aligned with html.dark after hydration */ -function syncHeaderTheme() { - if (typeof document === "undefined") return; - - const isDark = document.documentElement.classList.contains("dark"); - document.querySelectorAll("header.plane-header, header.wrapper").forEach((header) => { - if (isDark) { - header.setAttribute("data-theme", "dark"); - } else { - header.removeAttribute("data-theme"); - } - }); -} - -function handleTabHash() { - if (typeof document === "undefined") return; - - const hash = window.location.hash.slice(1); - if (!hash) return; - - const tabButtons = document.querySelectorAll('[role="tab"]'); - if (tabButtons.length === 0) return; - - tabButtons.forEach((button) => { - const labelText = button.textContent?.trim().toLowerCase().replace(/\s+/g, "-"); - if (labelText === hash) { - const element = button as HTMLElement; - element.dispatchEvent( - new MouseEvent("click", { - view: window, - bubbles: true, - cancelable: true, - }) - ); - element.click(); - element.focus(); - } - }); + document.querySelector(".VPDoc")?.classList.toggle("api-page", isApiPage); } -function setupTabHashUpdates() { - if (typeof document === "undefined") return; - - const tabButtons = document.querySelectorAll('[role="tab"]'); - tabButtons.forEach((button) => { - const element = button as HTMLElement; - element.removeEventListener("click", updateHashOnTabClick); - element.addEventListener("click", updateHashOnTabClick); - }); -} - -function updateHashOnTabClick(event: Event) { - const button = event.currentTarget as HTMLElement; - const labelText = button.textContent?.trim().toLowerCase().replace(/\s+/g, "-"); - if (labelText) { - history.replaceState(null, "", `#${labelText}`); - } -} - -function runDomEnhancements() { - syncHeaderTheme(); -} - -export default { - extends: VoidZeroTheme, - Layout() { - return h(PlaneLayout, null, { - "layout-bottom": () => h(CookieConsent), - }); - }, - enhanceApp(ctx) { - VoidZeroTheme.enhanceApp?.(ctx); - const { app } = ctx; - - app.provide(themeContextKey, { - /* dark mark on light bg | light/white mark on dark bg (VoidZero naming) */ - logoDark: "/logo/dev-logo-watermark-light.png", - logoLight: "/logo/dev-logo-watermark-dark.png", - logoAlt: "Plane", - footerBg: PLANE_FOOTER_BG, - monoIcon: PLANE_MONO_ICON, - }); - - enhanceAppWithTabs(app); - - app.component("ApiParam", ApiParam); - app.component("CodePanel", CodePanel); - app.component("ResponsePanel", ResponsePanel); - app.component("Card", Card); - app.component("CardGroup", CardGroup); - app.component("Tags", Tags); +export default createPlaneTheme({ + brand: { + logoOnLight: "/logo/dev-logo-watermark-light.png", + logoOnDark: "/logo/dev-logo-watermark-dark.png", + logoAlt: "Plane", + menuTitle: "Plane Developers", + footerBg: "https://media.docs.plane.so/logo/og-docs.webp", + monoIcon: "/logo/favicon-32x32.png", }, + components: { ApiParam, CodePanel, ResponsePanel }, setup() { - if (typeof window === "undefined") return; - const route = useRoute(); - const { isDark } = useData(); - let zoom: ReturnType | null = null; - let headerObserver: MutationObserver | null = null; - let htmlClassObserver: MutationObserver | null = null; - - const initZoom = () => { - zoom?.detach(); - zoom = mediumZoom(".vp-doc :not(a) > img:not(.VPImage)", { - background: "rgba(0, 0, 0, 0.8)", - }); - }; - - const scheduleEnhancements = () => { - nextTick(() => { - runDomEnhancements(); - requestAnimationFrame(runDomEnhancements); - }); - }; - - watch(isDark, () => { - syncHeaderTheme(); - }); - - onMounted(() => { - nextTick(() => { - updateLayout(); - initZoom(); - scheduleEnhancements(); - }); - - setTimeout(() => { - handleTabHash(); - setupTabHashUpdates(); - runDomEnhancements(); - }, 100); - - window.addEventListener("hashchange", () => { - nextTick(handleTabHash); - }); - - window.addEventListener("resize", scheduleEnhancements); - - htmlClassObserver = new MutationObserver(() => { - syncHeaderTheme(); - }); - htmlClassObserver.observe(document.documentElement, { - attributes: true, - attributeFilter: ["class"], - }); - - const navRoot = document.querySelector(".VPNav"); - if (navRoot) { - headerObserver = new MutationObserver(scheduleEnhancements); - headerObserver.observe(navRoot, { childList: true, subtree: true }); - } - }); - - onUnmounted(() => { - htmlClassObserver?.disconnect(); - headerObserver?.disconnect(); - window.removeEventListener("resize", scheduleEnhancements); - zoom?.detach(); - }); - + onMounted(() => nextTick(updateApiPageClass)); watch( () => route.path, - () => { - nextTick(() => { - updateLayout(); - initZoom(); - handleTabHash(); - setupTabHashUpdates(); - scheduleEnhancements(); - }); - } + () => nextTick(updateApiPageClass) ); }, -} satisfies Theme; +}); diff --git a/docs/.vitepress/theme/plane-overrides.css b/docs/.vitepress/theme/plane-overrides.css deleted file mode 100644 index b8886653..00000000 --- a/docs/.vitepress/theme/plane-overrides.css +++ /dev/null @@ -1,526 +0,0 @@ -/* ================================================ - PLANE BRAND THEME — component overrides - ================================================ */ - -/* --- Doc links (exclude home hero CTAs) --- */ -.vp-doc - a:not(.header-anchor):not(.vp-button):not(.card-link):not(.home-doc-actions__btn):not( - :where(.home-doc-actions *) - ):not(:where(.card-links *)) { - color: var(--vp-c-brand-1); -} - -.vp-doc - a:not(.header-anchor):not(.vp-button):not(.card-link):not(.home-doc-actions__btn):not( - :where(.home-doc-actions *) - ):not(:where(.card-links *)):hover { - color: var(--plane-link-hover); -} - -/* --- Navbar / header --- */ -header.plane-header.wrapper, -.plane-header-shell { - --font-heading: var(--vp-font-family-base); - font-family: var(--vp-font-family-base); - background-color: var(--plane-header-bg) !important; - border-bottom-color: var(--plane-header-border) !important; -} - -@media (min-width: 768px) { - .docs-layout header.plane-header.wrapper, - .docs-layout .plane-header-shell .wrapper { - border-left-color: var(--docs-divider) !important; - border-right-color: var(--docs-divider) !important; - } -} - -.docs-layout .border-stroke, -html.dark .docs-layout .border-stroke, -[data-theme="dark"] .docs-layout .border-stroke { - border-color: var(--docs-divider) !important; -} - -@media (min-width: 1024px) { - .plane-header__nav .VPLink.text-base, - .plane-header__nav a.text-base { - font-size: 14px !important; - } -} - -@media (min-width: 768px) { - .plane-header .VPNavBarSearchButton, - .plane-header .search-bar { - background-color: var(--plane-header-search-bg) !important; - border: 1px solid var(--plane-header-search-border) !important; - border-radius: 12px; - color: var(--plane-header-search-text) !important; - } -} - -/* --- Home feature cards --- */ -.VPHomeFeatures .VPFeature { - background-color: #f7f8f9; - border-color: #f7f8f9; -} - -.VPHomeFeatures .VPFeature.link:hover { - background-color: #eef0f2; - border-color: #eef0f2; -} - -.VPHomeFeatures .VPFeature .icon { - background-color: transparent; - color: #0a0a0a; -} - -.VPHomeFeatures .VPFeature .title { - color: #0a0a0a; -} - -.VPHomeFeatures .VPFeature.link:hover .title { - color: #111827; -} - -.VPHomeFeatures .VPFeature .details { - color: #4a5568; -} - -.VPHomeFeatures .VPFeature .link-text-value { - color: var(--vp-c-brand-1); -} - -.VPHomeFeatures .VPFeature.link:hover .link-text-value { - color: var(--plane-brand-hover); -} - -.dark .VPHomeFeatures .VPFeature, -html.dark .VPHomeFeatures .VPFeature, -[data-theme="dark"] .VPHomeFeatures .VPFeature { - background-color: #181a1b; - border-color: #181a1b; -} - -.dark .VPHomeFeatures .VPFeature.link:hover, -html.dark .VPHomeFeatures .VPFeature.link:hover, -[data-theme="dark"] .VPHomeFeatures .VPFeature.link:hover { - background-color: #1d1f20; - border-color: #1d1f20; -} - -.dark .VPHomeFeatures .VPFeature .icon, -html.dark .VPHomeFeatures .VPFeature .icon, -[data-theme="dark"] .VPHomeFeatures .VPFeature .icon { - color: #f4f4f4; -} - -.dark .VPHomeFeatures .VPFeature .title, -html.dark .VPHomeFeatures .VPFeature .title, -[data-theme="dark"] .VPHomeFeatures .VPFeature .title { - color: #f4f4f4; -} - -.dark .VPHomeFeatures .VPFeature .details, -html.dark .VPHomeFeatures .VPFeature .details, -[data-theme="dark"] .VPHomeFeatures .VPFeature .details { - color: #a1a1aa; -} - -/* --- Hero images in markdown --- */ -.vp-doc p:has(> img[src$="#hero"]), -.vp-doc p:has(> img[src$="#hero-tl"]), -.vp-doc p:has(> img[src$="#hero-tr"]), -.vp-doc p:has(> img[src$="#hero-bl"]), -.vp-doc p:has(> img[src$="#hero-br"]), -article p:has(> img[src$="#hero"]), -article p:has(> img[src$="#hero-tl"]), -article p:has(> img[src$="#hero-tr"]), -article p:has(> img[src$="#hero-bl"]), -article p:has(> img[src$="#hero-br"]) { - background: #f1f3f3; -} - -.vp-doc img[src$="#hero"], -.vp-doc img[src$="#hero-tl"], -.vp-doc img[src$="#hero-tr"], -.vp-doc img[src$="#hero-bl"], -.vp-doc img[src$="#hero-br"], -article img[src$="#hero"], -article img[src$="#hero-tl"], -article img[src$="#hero-tr"], -article img[src$="#hero-bl"], -article img[src$="#hero-br"] { - border-color: #eaebeb; - box-shadow: - 0 1px 2px rgba(41, 47, 61, 0.04), - 0 4px 12px rgba(41, 47, 61, 0.05); -} - -/* --- HR / borders --- */ -.vp-doc hr { - border-color: #e5e7eb; -} - -.dark .vp-doc hr, -html.dark .vp-doc hr, -[data-theme="dark"] .vp-doc hr { - border-color: #2a2a2a; -} - -/* ================================================ - PLANE-SPECIFIC OVERRIDES (API docs, components) - ================================================ */ - -/* Hide aside for API reference pages */ -.api-page .aside, -.api-page .aside-container, -.api-page .VPDocAside, -.api-page aside, -.api-page .VPDocAsideOutline { - display: none !important; - width: 0 !important; - min-width: 0 !important; - opacity: 0 !important; - visibility: hidden !important; -} - -.api-page.VPDoc > .container { - display: block !important; - max-width: 100% !important; -} - -.api-page .VPDoc > .container > .content { - max-width: 100% !important; - padding-right: 24px !important; -} - -.api-page .content-container, -.api-page .main, -.api-page .vp-doc { - max-width: 100% !important; -} - -/* Two-column API layout */ -.api-two-column { - display: grid; - grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); - gap: 40px; - align-items: start; -} - -@media (max-width: 1100px) { - .api-two-column { - grid-template-columns: 1fr; - gap: 24px; - } -} - -.api-left { - min-width: 0; -} - -.api-right { - position: sticky; - top: 100px; - min-width: 0; -} - -@media (max-width: 1100px) { - .api-right { - position: static; - } -} - -/* API endpoint badge */ -.api-endpoint-badge { - display: inline-flex; - align-items: center; - gap: 12px; - margin-bottom: 20px; -} - -.api-endpoint-badge .method { - font-size: 12px; - font-weight: 700; - padding: 5px 12px; - border-radius: 6px; - font-family: var(--vp-font-family-mono); - text-transform: uppercase; -} - -.api-endpoint-badge .method.get { - background: #dcfce7; - color: #166534; -} - -.api-endpoint-badge .method.post { - background: #dbeafe; - color: #1e40af; -} - -.api-endpoint-badge .method.patch, -.api-endpoint-badge .method.put { - background: #fef3c7; - color: #92400e; -} - -.api-endpoint-badge .method.delete { - background: #fee2e2; - color: #991b1b; -} - -.dark .api-endpoint-badge .method.get { - background: rgba(34, 197, 94, 0.15); - color: #4ade80; -} - -.dark .api-endpoint-badge .method.post { - background: rgba(59, 130, 246, 0.15); - color: #60a5fa; -} - -.dark .api-endpoint-badge .method.patch, -.dark .api-endpoint-badge .method.put { - background: rgba(251, 191, 36, 0.15); - color: #fbbf24; -} - -.dark .api-endpoint-badge .method.delete { - background: rgba(239, 68, 68, 0.15); - color: #f87171; -} - -.api-endpoint-badge .path { - font-family: var(--vp-font-family-mono); - font-size: 14px; - color: var(--vp-c-text-2); -} - -/* Section headers */ -.params-section h3, -.returns-section h3 { - font-size: 13px !important; - font-weight: 600 !important; - color: var(--vp-c-text-2) !important; - text-transform: uppercase; - letter-spacing: 0.05em; - margin: 0 0 12px 0 !important; - padding: 0 !important; - border: none !important; -} - -.params-list { - border-top: 1px solid var(--vp-c-divider); -} - -/* Card components */ -.card-group { - display: grid; - gap: 16px; - margin: 24px 0; -} - -.card-group-2 { - grid-template-columns: 1fr; -} - -@media (min-width: 768px) { - .card-group-2 { - grid-template-columns: repeat(2, 1fr); - } -} - -.card-group-3 { - grid-template-columns: 1fr; -} - -@media (min-width: 768px) { - .card-group-3 { - grid-template-columns: repeat(2, 1fr); - } -} - -@media (min-width: 1024px) { - .card-group-3 { - grid-template-columns: repeat(3, 1fr); - } -} - -.card-group-4 { - grid-template-columns: 1fr; -} - -@media (min-width: 768px) { - .card-group-4 { - grid-template-columns: repeat(2, 1fr); - } -} - -@media (min-width: 1024px) { - .card-group-4 { - grid-template-columns: repeat(4, 1fr); - } -} - -.card-link { - display: block; - padding: 24px; - border-radius: 12px; - border: 1px solid #e5e7eb; - background: #ffffff; - transition: all 0.2s ease; - text-decoration: none !important; - color: inherit; -} - -.card-link:hover { - border-color: var(--vp-c-brand-1); - box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1); - text-decoration: none !important; -} - -.dark .card-link, -html.dark .card-link, -[data-theme="dark"] .card-link { - border-color: #2a2a2a; - background: rgba(20, 20, 20, 0.5); -} - -.dark .card-link:hover, -html.dark .card-link:hover, -[data-theme="dark"] .card-link:hover { - border-color: var(--vp-c-brand-1); -} - -.card-head { - margin-bottom: 0; -} - -.card-title { - font-size: 1.0625rem !important; - font-weight: 600 !important; - line-height: 1.35 !important; - letter-spacing: -0.01em; - color: #111827 !important; - border: none !important; - transition: color 0.2s ease; -} - -.card-link:hover .card-title { - color: var(--vp-c-brand-1) !important; -} - -.dark .card-title, -html.dark .card-title, -[data-theme="dark"] .card-title { - color: #f9fafb !important; -} - -.dark .card-link:hover .card-title, -html.dark .card-link:hover .card-title, -[data-theme="dark"] .card-link:hover .card-title { - color: #f9fafb !important; -} - -.card-description { - font-size: 14px; - color: #6b7280; - line-height: 1.6; - margin: 0; -} - -.dark .card-description, -html.dark .card-description, -[data-theme="dark"] .card-description { - color: #9ca3af; -} - -/* Card CTA link (optional slot / future use) */ -.card-cta { - color: var(--vp-c-brand-1); - font-weight: 500; - transition: color 0.2s ease; -} - -.card-link:hover .card-cta { - color: var(--plane-brand-hover); -} - -/* Hero image layout in markdown */ -.vp-doc p:has(> img[src$="#hero"]), -.vp-doc p:has(> img[src$="#hero-tl"]), -.vp-doc p:has(> img[src$="#hero-tr"]), -.vp-doc p:has(> img[src$="#hero-bl"]), -.vp-doc p:has(> img[src$="#hero-br"]), -article p:has(> img[src$="#hero"]), -article p:has(> img[src$="#hero-tl"]), -article p:has(> img[src$="#hero-tr"]), -article p:has(> img[src$="#hero-bl"]), -article p:has(> img[src$="#hero-br"]) { - max-width: 841px; - padding: 36px 56px 0; - border-radius: 12px; - box-sizing: border-box; - overflow: hidden; -} - -.vp-doc img[src$="#hero"], -.vp-doc img[src$="#hero-tl"], -.vp-doc img[src$="#hero-tr"], -.vp-doc img[src$="#hero-bl"], -.vp-doc img[src$="#hero-br"], -article img[src$="#hero"], -article img[src$="#hero-tl"], -article img[src$="#hero-tr"], -article img[src$="#hero-bl"], -article img[src$="#hero-br"] { - width: 100%; - height: auto; - display: block; - margin: 0; - border-radius: 8px 8px 0 0; - border: 1px solid #eaebeb; - border-bottom: none; - object-fit: cover; -} - -/* Medium zoom */ -.vp-doc img { - cursor: zoom-in; -} - -.medium-zoom-overlay { - z-index: 9999 !important; - background: rgba(0, 0, 0, 0.8) !important; -} - -.medium-zoom-image, -.medium-zoom-image--opened { - z-index: 10000 !important; -} - -.medium-zoom-image--opened { - cursor: zoom-out; -} - -.hidden { - display: none !important; -} - -.mobile-img-container { - display: flex; - flex-direction: row; - justify-content: center; - align-items: center; - flex-wrap: wrap; -} - -.mobile-img-box { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - max-width: 25rem; - min-width: 15rem; - text-align: center; - margin: 10px; -} diff --git a/docs/.vitepress/theme/plane-ui.css b/docs/.vitepress/theme/plane-ui.css deleted file mode 100644 index 0ef6b475..00000000 --- a/docs/.vitepress/theme/plane-ui.css +++ /dev/null @@ -1,892 +0,0 @@ -/* ================================================ - PLANE UI — layout, typography, shell, header - (VoidZero base + Plane overrides) - ================================================ */ - -:root { - --vp-layout-top-height: 0; - --plane-header-padding-y: 1.125rem; - --plane-header-padding-x: 1.375rem; - --plane-header-logo-height: 1.6875rem; - --vp-nav-height: 84px; - - /* Navbar tokens (fallback outside .docs-layout) */ - --plane-header-bg: #ffffff; - --plane-header-border: var(--docs-divider, #ececec); - --plane-header-text: var(--color-primary, #0a0a0a); - --plane-header-muted: var(--vp-c-text-2, #6b7280); - --plane-header-divider: var(--docs-divider, #ececec); - --plane-header-search-bg: #ffffff; - --plane-header-search-border: #e7e7e7; - --plane-header-search-text: #867e8e; - --plane-header-search-keys-border: #e5e4e7; -} - -html.dark, -html.dark .plane-header-shell, -[data-theme="dark"], -[data-theme="dark"] .plane-header-shell { - --plane-header-bg: var(--color-primary, #141415); - --plane-header-border: var(--docs-divider, #2a2a2a); - --plane-header-text: #ffffff; - --plane-header-muted: var(--vp-c-text-2, #9ca3af); - --plane-header-divider: var(--docs-divider, #2a2a2a); - --plane-header-search-bg: #111111; - --plane-header-search-border: #323232; - --plane-header-search-text: #867e8e; - --plane-header-search-keys-border: #323232; -} - -/* --- Fonts & body --- */ -body, -.docs-layout { - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - text-rendering: optimizeLegibility; - font-feature-settings: - "cv01" on, - "cv02" on, - "zero" on; -} - -html.dark .docs-layout, -html.dark body { - background-color: #141415; - color-scheme: dark; -} - -.docs-layout { - --vp-font-family-base: var(--vp-font-family-base, "Inter", system-ui, sans-serif); - --vp-font-family-mono: var(--vp-font-family-mono, "IBM Plex Mono", ui-monospace, monospace); - /* Plane header row — keep in sync with .plane-header padding + content */ - --plane-header-padding-y: 1.125rem; - --plane-header-padding-x: 1.375rem; - --plane-header-logo-height: 1.6875rem; - --vp-nav-height: 84px; - - /* Navbar — Plane theme tokens (light) */ - --plane-header-bg: #ffffff; - --plane-header-border: var(--docs-divider); - --plane-header-text: var(--color-primary, #0a0a0a); - --plane-header-muted: var(--vp-c-text-2); - --plane-header-divider: var(--docs-divider); - --plane-header-search-bg: #ffffff; - --plane-header-search-border: #e7e7e7; - --plane-header-search-text: #867e8e; - --plane-header-search-keys-border: #e5e4e7; -} - -html.dark .docs-layout, -html.dark .docs-layout .plane-header-shell, -[data-theme="dark"] .docs-layout, -[data-theme="dark"] .plane-header-shell { - --plane-header-bg: var(--color-primary, #141415); - --plane-header-border: var(--docs-divider); - --plane-header-text: #ffffff; - --plane-header-muted: var(--vp-c-text-2); - --plane-header-divider: var(--docs-divider); - --plane-header-search-bg: #111111; - --plane-header-search-border: #323232; - --plane-header-search-text: #867e8e; - --plane-header-search-keys-border: #323232; -} - -/* --- Doc prose --- */ -.vp-doc { - line-height: 1.7; - letter-spacing: -0.011em; -} - -.vp-doc h1 { - letter-spacing: -0.022em; - line-height: 1.2; -} - -.vp-doc h2 { - border-top: none !important; - margin-top: 32px; - letter-spacing: -0.017em; - line-height: 1.3; -} - -.vp-doc h3 { - border-top: none !important; - letter-spacing: -0.014em; - line-height: 1.4; -} - -/* --- Sidebars: flush under OSS navbar --- */ -@media (min-width: 1024px) { - .docs-layout .VPSidebar { - padding-top: 0; - top: calc(var(--vp-nav-height) + var(--vp-banner-height, 0px) - 4px); - } - - .docs-layout .VPDoc .aside-container { - padding-top: 0; - top: calc(var(--vp-nav-height) + var(--vp-banner-height, 0px) - 4px); - } -} - -/* --- Doc layout shell (1280px+) --- */ -@media (min-width: 1280px) { - .docs-layout .VPSidebar .nav { - padding-top: 0; - } - - .docs-layout .VPDoc .content { - padding-top: 32px; - } - - .docs-layout .VPDoc .aside { - max-width: 256px; - } - - .docs-layout .VPDoc .aside-container { - width: 256px; - padding-left: 24px; - border-left-color: var(--docs-divider); - } - - .docs-layout .VPDocOutlineItem .outline-link { - white-space: normal; - text-overflow: unset; - overflow: visible; - } -} - -/* No right outline (aside: false) — widen main column */ -.docs-layout .VPDoc:not(.has-aside) .content-container { - max-width: 960px; - padding-left: 24px; - padding-right: 24px; -} - -@media (min-width: 1280px) { - .docs-layout .VPDoc:not(.has-aside) .content-container { - max-width: 1104px; - padding-left: 48px; - padding-right: 48px; - } -} - -/* --- Copy page menu (components/CopyPageMenu.vue) — placement --- - The control is server-rendered in the `doc-before` slot (first child of - `.VPDoc .content-container`, above
) and, once mounted, teleported into a - `.copy-page-slot` host inserted right after the page H1 inside `.vp-doc`. Both - positions are styled here; visual styles are scoped in the component. */ -.docs-layout .VPDoc .content-container { - position: relative; /* anchor for the pre-teleport position */ -} - -/* < 768px: below the title, left-aligned, in flow */ -@media (max-width: 767px) { - /* Pre-teleport position would sit above the title — keep it hidden to avoid a jump. */ - .docs-layout .VPDoc .content-container > .copy-page { - display: none; - } - - .docs-layout .vp-doc > div > .copy-page-slot { - margin: 14px 0 16px; - } -} - -/* ≥ 768px: on the title row, flush with the right edge of the text column */ -@media (min-width: 768px) { - .docs-layout .VPDoc .content-container > .copy-page, - .docs-layout .vp-doc > div > .copy-page-slot > .copy-page { - position: absolute; /* relative to .content-container / .vp-doc — same top edge */ - top: 2px; /* (h1 line box ~38px − 34px button) / 2 */ - right: 0; - margin: 0; - z-index: 10; /* below --vp-z-index-local-nav (20) and the image lightbox */ - } - - /* Reserve the control's footprint on the H1 only when it actually rendered - (so `copyPage: false` pages keep the full width). */ - .docs-layout .vp-doc > div > h1:has(+ .copy-page-slot > .copy-page), - .docs-layout .VPDoc .content-container > .copy-page ~ .main .vp-doc > div > h1 { - padding-right: 156px; /* ≈ 132px control + 24px gap — re-measure if the label changes */ - } -} - -/* Unified layout borders & corner ticks */ -@media (min-width: 768px) { - .docs-layout .content-wrapper, - .docs-layout .wrapper { - border-left-color: var(--docs-divider); - border-right-color: var(--docs-divider); - } -} - -.docs-layout .tick-left::before { - border-left-color: var(--docs-divider); -} - -.docs-layout .tick-right::after { - border-right-color: var(--docs-divider); -} - -.docs-layout .VPSidebar { - border-right: 1px solid var(--docs-divider); -} - -/* Hide local nav when sidebar is present */ -@media (min-width: 1024px) { - .docs-layout .VPLocalNav { - display: none !important; - } -} - -/* --- Plane header — match content-wrapper width (centered when fixed) --- */ -@media (min-width: 768px) { - .docs-layout .plane-header-shell { - width: calc(100vw - 2rem); - max-width: calc(100vw - 2rem); - margin-left: auto; - margin-right: auto; - box-sizing: border-box; - } -} - -@media (min-width: 90rem) { - .docs-layout .plane-header-shell { - width: 90rem; - max-width: 90rem; - } -} - -@media (min-width: 1024px) { - .docs-layout .plane-header-shell--docs { - left: 50%; - right: auto; - transform: translateX(-50%); - width: calc(100vw - 2rem); - max-width: calc(100vw - 2rem); - } - - .docs-layout .plane-header-shell--docs.plane-header-shell { - overflow-x: clip; - } -} - -@media (min-width: 90rem) { - .docs-layout .plane-header-shell--docs { - width: 90rem; - max-width: 90rem; - } -} - -@media (min-width: 1024px) { - .plane-header-shell { - overflow-x: clip; - } - - .docs-layout .plane-header.wrapper { - width: 100%; - max-width: 100%; - margin-left: auto; - margin-right: auto; - box-sizing: border-box; - overflow-x: clip; - } - - .plane-header .plane-header__start, - .plane-header .plane-header__actions { - min-width: 0; - overflow: hidden; - } - - .plane-header .plane-header__search { - width: 240px; - max-width: 240px; - } - - .plane-header .VPNavBarSearch { - width: 100%; - min-width: 0; - max-width: 240px; - } - - .plane-header .VPNavBarSearchButton, - .plane-header .DocSearch.DocSearch-Button { - width: 100% !important; - max-width: 240px !important; - min-width: 0 !important; - } - - .plane-header .DocSearch.DocSearch-Button, - .plane-header .VPNavBarSearchButton, - .plane-header .search-bar { - height: 36px; - border-radius: 12px; - background: var(--plane-header-search-bg) !important; - border: 1px solid var(--plane-header-search-border) !important; - color: var(--plane-header-search-text) !important; - justify-content: flex-start !important; - text-align: left !important; - gap: 8px; - } - - .plane-header .DocSearch-Button-Container { - justify-content: flex-start !important; - flex: 1; - min-width: 0; - gap: 0.5rem; - } - - .plane-header .DocSearch-Search-Icon { - color: var(--plane-header-search-text) !important; - } - - .plane-header .DocSearch-Button-Placeholder, - .plane-header .VPNavBarSearchButton .text, - .plane-header .search-bar__text { - color: var(--plane-header-search-text) !important; - text-align: left !important; - flex: 0 1 auto; - font-size: 13px; - } - - .plane-header .DocSearch-Button-Keys, - .plane-header .VPNavBarSearchButton .keys, - .plane-header .search-bar__keys { - color: var(--plane-header-search-text) !important; - border: 1px solid var(--plane-header-search-keys-border) !important; - margin-left: auto !important; - flex-shrink: 0; - font-size: 12px; - font-weight: 500; - } - - .plane-header__nav .VPLink.link, - .plane-header__nav .VPLink.text-base, - .plane-header .VPNavBarMenuLink:not(.home-doc-actions__btn), - .plane-header .VPNavBarMenuGroup .button { - color: var(--plane-header-text) !important; - font-size: 14px !important; - font-weight: 400; - padding: 0.375rem 0.5rem; - min-width: 0; - max-width: 100%; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - transition: color 0.2s ease; - } - - .plane-header__nav .VPLink.link:hover, - .plane-header .VPNavBarMenuLink:not(.home-doc-actions__btn):hover, - .plane-header .VPNavBarMenuGroup .button:hover { - color: var(--vp-c-brand-1) !important; - opacity: 1; - } - - .plane-header a.home-doc-actions__btn--secondary.VPNavBarMenuLink, - .plane-header a.home-doc-actions__btn--secondary.VPNavBarMenuLink:hover { - color: inherit !important; - opacity: 1 !important; - } - - .plane-header .VPNavBarMenuLink.active, - .plane-header__nav .VPLink.link.active { - color: var(--vp-c-brand-1) !important; - } - - .plane-header .VPNavBarAppearance, - .plane-header .VPNavBarSocialLinks { - display: flex !important; - align-items: center; - flex-shrink: 0; - } - - .plane-header .VPNavBarSocialLinks { - gap: 0.25rem; - } - - .plane-header .VPNavBarSocialLinks .VPSocialLink { - color: var(--plane-header-text); - transition: color 0.2s ease; - } - - .plane-header .VPNavBarSocialLinks .VPSocialLink:hover { - color: var(--vp-c-brand-1); - } - - .plane-header .VPSwitchAppearance { - --vp-c-bg: var(--plane-header-search-bg); - } -} - -/* Theme switcher border: --vp-input-border-color → --vp-c-border → stroke / nickel */ -.docs-layout .VPSwitchAppearance { - --vp-c-border: var(--color-stroke); - --vp-input-border-color: var(--vp-c-border); -} - -html.dark .docs-layout .VPSwitchAppearance, -[data-theme="dark"] .docs-layout .VPSwitchAppearance { - --vp-c-border: var(--color-nickel); - --vp-input-border-color: var(--vp-c-border); -} - -@media (min-width: 1024px) and (max-width: 1279px) { - .plane-header__title { - display: none; - } -} - -@media (min-width: 1280px) { - .plane-header { - gap: 0.5rem 1.25rem; - --plane-header-padding-x: 1.5rem; - } - - .plane-header__nav .VPLink.link, - .plane-header__nav .VPLink.text-base, - .plane-header .VPNavBarMenuLink:not(.home-doc-actions__btn), - .plane-header .VPNavBarMenuGroup .button { - font-size: 14px !important; - padding: 0.375rem 0.75rem; - } -} - -/* --- Code blocks --- */ -div[class*="language-"] { - border-radius: 8px !important; - overflow: hidden; - border: 1px solid #e5e7eb; - background: #fafafa !important; -} - -.dark div[class*="language-"], -html.dark div[class*="language-"], -[data-theme="dark"] div[class*="language-"] { - background: #0f0f0f !important; - border-color: #2a2a2a !important; -} - -div[class*="language-"] pre { - background: transparent !important; -} - -.dark .shiki, -.dark .shiki span, -html.dark .shiki, -html.dark .shiki span { - background: transparent !important; -} - -div[class*="language-"] > span.lang { - color: #9ca3af; - font-size: 12px; -} - -/* --- Home page (layout: doc) --- */ -.vp-doc .home-hero { - margin-bottom: 2rem; -} - -.vp-doc .home-hero h1 { - font-size: 2.5rem; - font-weight: 600; - letter-spacing: -0.02em; - margin: 0 0 0.5rem; - line-height: 1.15; -} - -.vp-doc .home-hero .home-hero-text { - font-size: 1.75rem; - font-weight: 600; - letter-spacing: -0.02em; - margin: 0 0 1rem; - color: var(--vp-c-text-1); -} - -.vp-doc .home-hero .home-hero-tagline { - font-size: 1.125rem; - color: var(--vp-c-text-2); - max-width: 42rem; - margin: 0 0 1.5rem; - line-height: 1.6; -} - -.home-doc-actions { - display: flex; - flex-wrap: wrap; - gap: 12px; - margin-bottom: 3rem; -} - -/* --- Plane buttons: shared base --- */ -a.home-doc-actions__btn, -.home-doc-actions__btn { - display: inline-flex; - align-items: center; - justify-content: center; - min-height: 40px; - padding: 9px 18px; - border-radius: 8px; - border: 1px solid transparent; - font-size: 15px; - font-weight: 500; - line-height: 1.25; - text-decoration: none !important; - cursor: pointer; - transition: - background 0.2s ease, - color 0.2s ease, - border-color 0.2s ease, - box-shadow 0.2s ease, - transform 0.2s ease; -} - -/* Navbar — smaller primary variant */ -a.home-doc-actions__btn--nav, -.home-doc-actions__btn--nav { - min-height: auto; - padding: 4px 15px; - border-radius: 6px; - font-size: 14px; - margin: 0 !important; -} - -/* Primary */ -a.home-doc-actions__btn--primary, -a.home-doc-actions__btn--primary:is(:link, :visited, :focus, :active) { - background: #006399 !important; - border-color: #006399 !important; - color: #ffffff !important; -} - -a.home-doc-actions__btn--primary:hover { - background: #0078b8 !important; - border-color: #0078b8 !important; - color: #ffffff !important; - box-shadow: 0 2px 8px rgba(0, 99, 153, 0.25); -} - -a.home-doc-actions__btn--primary.home-doc-actions__btn--nav:hover { - transform: translateY(-1px); -} - -.dark a.home-doc-actions__btn--primary, -.dark a.home-doc-actions__btn--primary:is(:link, :visited, :focus, :active), -html.dark a.home-doc-actions__btn--primary, -html.dark a.home-doc-actions__btn--primary:is(:link, :visited, :focus, :active), -[data-theme="dark"] a.home-doc-actions__btn--primary, -[data-theme="dark"] a.home-doc-actions__btn--primary:is(:link, :visited, :focus, :active) { - background: #2893cc !important; - border-color: #2893cc !important; - color: #ffffff !important; -} - -.dark a.home-doc-actions__btn--primary:hover, -html.dark a.home-doc-actions__btn--primary:hover, -[data-theme="dark"] a.home-doc-actions__btn--primary:hover { - background: #3aa5d4 !important; - border-color: #3aa5d4 !important; - color: #ffffff !important; -} - -.dark a.home-doc-actions__btn--primary.home-doc-actions__btn--nav:hover, -html.dark a.home-doc-actions__btn--primary.home-doc-actions__btn--nav:hover, -[data-theme="dark"] a.home-doc-actions__btn--primary.home-doc-actions__btn--nav:hover { - box-shadow: 0 2px 8px rgba(40, 147, 204, 0.25); -} - -/* Secondary */ -a.home-doc-actions__btn--secondary, -a.home-doc-actions__btn--secondary:is(:link, :visited, :focus, :active) { - background: transparent !important; - border-color: #e5e7eb !important; - color: #0a0a0a !important; -} - -a.home-doc-actions__btn--secondary:hover { - background: #f7f8f9 !important; - border-color: #d1d5db !important; - color: #0a0a0a !important; -} - -.dark a.home-doc-actions__btn--secondary, -.dark a.home-doc-actions__btn--secondary:is(:link, :visited, :focus, :active), -html.dark a.home-doc-actions__btn--secondary, -html.dark a.home-doc-actions__btn--secondary:is(:link, :visited, :focus, :active), -[data-theme="dark"] a.home-doc-actions__btn--secondary, -[data-theme="dark"] a.home-doc-actions__btn--secondary:is(:link, :visited, :focus, :active) { - background: #252829 !important; - border-color: #3f4244 !important; - color: #ffffff !important; -} - -.dark a.home-doc-actions__btn--secondary:hover, -html.dark a.home-doc-actions__btn--secondary:hover, -[data-theme="dark"] a.home-doc-actions__btn--secondary:hover { - background: #2f3236 !important; - border-color: #4a4e52 !important; - color: #ffffff !important; -} - -/* Navbar buttons — hide external-link ↗ icon */ -.plane-header a.home-doc-actions__btn.vp-external-link-icon::after, -.plane-header .VPLink.home-doc-actions__btn.vp-external-link-icon::after, -.plane-header a.home-doc-actions__btn.no-icon::after, -.plane-header .VPLink.home-doc-actions__btn.no-icon::after { - display: none !important; - content: none !important; -} - -.plane-header a.home-doc-actions__btn .vp-external-link-icon, -.plane-header a.home-doc-actions__btn svg { - display: none !important; -} - -/* Navbar primary & secondary buttons */ -.plane-header a.home-doc-actions__btn--primary.VPNavBarMenuLink, -.plane-header a.home-doc-actions__btn--secondary.VPNavBarMenuLink, -.plane-header .VPLink.home-doc-actions__btn--primary, -.plane-header .VPLink.home-doc-actions__btn--secondary { - padding: 4px 15px !important; - font-size: 14px !important; - border-radius: 6px !important; - min-height: auto !important; - line-height: 1.25 !important; - font-weight: 500 !important; - white-space: nowrap; -} - -/* Don't apply nav text-link hover to Sign in button */ -.plane-header .VPNavBarMenuLink.home-doc-actions__btn:hover { - opacity: 1 !important; -} - -.home-feature-cards { - margin: 2rem 0 3rem; -} - -.home-quick-start { - margin: 1.5rem 0 3rem; -} - -.home-feature-cards .card-group, -.home-quick-start .card-group { - display: grid; - gap: 16px; - grid-template-columns: 1fr; -} - -@media (min-width: 640px) { - .home-feature-cards .card-group, - .home-quick-start .card-group { - grid-template-columns: repeat(2, 1fr); - } -} - -@media (min-width: 960px) { - .home-feature-cards .card-group, - .home-quick-start .card-group { - grid-template-columns: repeat(3, 1fr); - } -} - -.home-feature-cards .card-group, -.home-quick-start .card-group { - gap: 1rem; -} - -.home-feature-cards .card-link, -.home-quick-start .card-link { - display: flex; - flex-direction: column; - height: 100%; - min-height: 10rem; - border: 1px solid transparent; - background: #f7f8f9; - padding: 1.5rem; - border-radius: 0.875rem; - box-shadow: none; - transition: - background 0.2s ease, - border-color 0.2s ease, - transform 0.2s ease; -} - -.home-feature-cards .card-link:hover, -.home-quick-start .card-link:hover { - background: #eef0f2; - border-color: color-mix(in srgb, var(--vp-c-brand-1) 12%, transparent); - box-shadow: none; - transform: translateY(-1px); - text-decoration: none !important; -} - -.home-quick-start .card-link--static { - cursor: default; -} - -.home-quick-start .card-link--static:hover { - background: #f7f8f9; - border-color: transparent; - box-shadow: none; - transform: none; - text-decoration: none !important; -} - -.home-quick-start .card-link--static:hover .card-title { - color: #0a0a0a !important; -} - -.home-quick-start .card-link--static:hover .card-icon__surface { - background: color-mix(in srgb, var(--vp-c-brand-1) 10%, transparent); -} - -.dark .home-quick-start .card-link--static:hover, -html.dark .home-quick-start .card-link--static:hover, -[data-theme="dark"] .home-quick-start .card-link--static:hover { - background: #181a1b; - border-color: transparent; -} - -.dark .home-quick-start .card-link--static:hover .card-title, -html.dark .home-quick-start .card-link--static:hover .card-title, -[data-theme="dark"] .home-quick-start .card-link--static:hover .card-title { - color: #f4f4f4 !important; -} - -.dark .home-feature-cards .card-link, -html.dark .home-feature-cards .card-link, -[data-theme="dark"] .home-feature-cards .card-link, -.dark .home-quick-start .card-link, -html.dark .home-quick-start .card-link, -[data-theme="dark"] .home-quick-start .card-link { - background: #181a1b; - border-color: transparent; -} - -.dark .home-feature-cards .card-link:hover, -html.dark .home-feature-cards .card-link:hover, -[data-theme="dark"] .home-feature-cards .card-link:hover, -.dark .home-quick-start .card-link:hover, -html.dark .home-quick-start .card-link:hover, -[data-theme="dark"] .home-quick-start .card-link:hover { - background: #1f2224; - border-color: color-mix(in srgb, var(--vp-c-brand-1) 22%, transparent); - box-shadow: none; -} - -.home-feature-cards .card-head, -.home-quick-start .card-head { - margin-bottom: 0.625rem; -} - -.home-feature-cards .card-title, -.home-quick-start .card-title { - color: #0a0a0a !important; - font-size: 1rem !important; -} - -.dark .home-feature-cards .card-title, -html.dark .home-feature-cards .card-title, -[data-theme="dark"] .home-feature-cards .card-title, -.dark .home-quick-start .card-title, -html.dark .home-quick-start .card-title, -[data-theme="dark"] .home-quick-start .card-title { - color: #f4f4f4 !important; -} - -.home-feature-cards .card-link:hover .card-title, -.home-quick-start .card-link:hover .card-title { - color: #0a0a0a !important; -} - -.dark .home-feature-cards .card-link:hover .card-title, -html.dark .home-feature-cards .card-link:hover .card-title, -[data-theme="dark"] .home-feature-cards .card-link:hover .card-title, -.dark .home-quick-start .card-link:hover .card-title, -html.dark .home-quick-start .card-link:hover .card-title, -[data-theme="dark"] .home-quick-start .card-link:hover .card-title { - color: #f4f4f4 !important; -} - -.home-feature-cards .card-description, -.home-quick-start .card-description { - font-size: 0.875rem; - line-height: 1.55; - color: #5c6570; -} - -.dark .home-feature-cards .card-description, -html.dark .home-feature-cards .card-description, -[data-theme="dark"] .home-feature-cards .card-description, -.dark .home-quick-start .card-description, -html.dark .home-quick-start .card-description, -[data-theme="dark"] .home-quick-start .card-description { - color: #9ca3af; -} - -.home-feature-cards .card-cta, -.home-quick-start .card-links { - margin-top: auto; -} - -.home-feature-cards .card-cta { - padding-top: 1rem; -} - -.home-quick-start .card-links { - padding-top: 1rem; -} - -.home-feature-cards .card-icon__surface, -.home-quick-start .card-icon__surface { - width: 2.75rem; - height: 2.75rem; - border-radius: 0.6875rem; -} - -.home-feature-cards .card-icon__custom svg, -.home-quick-start .card-icon__custom svg { - width: 1.375rem; - height: 1.375rem; -} - -/* Hide footer prev/next on home when card grids are present */ -.Layout:has(.home-feature-cards) .VPDocFooter .prev, -.Layout:has(.home-feature-cards) .VPDocFooter .next, -.Layout:has(.home-feature-cards) .VPDocFooter .pager-link, -.Layout:has(.home-quick-start) .VPDocFooter .prev, -.Layout:has(.home-quick-start) .VPDocFooter .next, -.Layout:has(.home-quick-start) .VPDocFooter .pager-link { - display: none; -} - -/* Legacy VPFeature icons in dark mode */ -.dark .VPFeature .VPImage, -html.dark .VPFeature .VPImage { - filter: invert(1) hue-rotate(180deg); -} - -.dark .VPFeatures .VPFeature .icon img, -html.dark .VPFeatures .VPFeature .icon img { - filter: invert(1) hue-rotate(180deg); -} - -/* --- Tags --- */ -.plantag, -a.plantag { - display: inline-flex; - align-items: center; - padding: 2px 8px; - border-radius: 4px; - font-size: 12px; - font-weight: 500; - background: var(--vp-c-brand-soft); - color: var(--vp-c-brand-1); - text-decoration: none; - line-height: 1.4; -} - -a.plantag:hover { - color: var(--plane-brand-hover); -} diff --git a/docs/.vitepress/theme/plane/README.md b/docs/.vitepress/theme/plane/README.md new file mode 100644 index 00000000..e3351ae0 --- /dev/null +++ b/docs/.vitepress/theme/plane/README.md @@ -0,0 +1,16 @@ +# Plane docs theme (shared) + +This folder is **byte-identical** in [makeplane/docs](https://github.com/makeplane/docs) and +[makeplane/developer-docs](https://github.com/makeplane/developer-docs) — it is the single source of the +visual identity for docs.plane.so and developers.plane.so (tokens, fonts, header, layout, Card/CardGroup/Tags, +Copy page menu, cookie consent). + +- Each site's `docs/.vitepress/theme/index.ts` is a thin `createPlaneTheme({ brand, … })` call; anything + site-specific (logo URLs, extra components, `.api-page` toggling) is passed in as options or lives outside + this folder. +- **Editing:** change files here in one repo, copy the whole folder to the sibling repo, and run + `pnpm check:theme-sync` in both (locally: `THEME_SIBLING_PATH=../ pnpm check:theme-sync`). + CI runs the same check against the sibling's `master` (or a same-named PR branch). +- **Formatting:** files here are formatted so that oxfmt (docs) and prettier (developer-docs, via the + `.prettierrc` override for this folder) agree — keep `printWidth: 100`, `trailingComma: "all"`. +- Add new files to `manifest.json`. diff --git a/docs/.vitepress/theme/plane/components/Card.vue b/docs/.vitepress/theme/plane/components/Card.vue new file mode 100644 index 00000000..64b78b8f --- /dev/null +++ b/docs/.vitepress/theme/plane/components/Card.vue @@ -0,0 +1,81 @@ + + + diff --git a/docs/.vitepress/theme/plane/components/CardGroup.vue b/docs/.vitepress/theme/plane/components/CardGroup.vue new file mode 100644 index 00000000..16256d57 --- /dev/null +++ b/docs/.vitepress/theme/plane/components/CardGroup.vue @@ -0,0 +1,20 @@ + + + diff --git a/docs/.vitepress/theme/components/CookieConsent.vue b/docs/.vitepress/theme/plane/components/CookieConsent.vue similarity index 98% rename from docs/.vitepress/theme/components/CookieConsent.vue rename to docs/.vitepress/theme/plane/components/CookieConsent.vue index 091960f5..99f20fc2 100644 --- a/docs/.vitepress/theme/components/CookieConsent.vue +++ b/docs/.vitepress/theme/plane/components/CookieConsent.vue @@ -81,7 +81,8 @@ onMounted(() => {