From 54c23d852af5e8001d995c4165a1290778c1cad9 Mon Sep 17 00:00:00 2001 From: Marco Pasqualetti Date: Mon, 17 Aug 2026 00:35:24 +0200 Subject: [PATCH 01/14] feat: publish the documentation and the card wizard from one Astro site --- .github/CONTRIBUTING.md | 6 +- .gitignore | 2 + README.md | 78 +- apps/frontend/astro.config.ts | 69 + apps/frontend/e2e/app.spec.ts | 95 +- apps/frontend/e2e/stats-rank.spec.ts | 13 +- ...rends-auth.spec.ts => wizard-auth.spec.ts} | 15 +- apps/frontend/index.html | 62 - apps/frontend/package.json | 17 +- apps/frontend/playwright.config.ts | 6 +- apps/frontend/src/axios-override.ts | 22 +- .../src/components/Generic/ThemePicker.tsx | 131 - apps/frontend/src/components/SiteTitle.astro | 56 + apps/frontend/src/content.config.ts | 13 + apps/frontend/src/content/docs/_README.md | 14 + .../docs/docs}/advanced_documentation.md | 94 +- .../frontend/src/content/docs/docs}/deploy.md | 18 +- .../frontend/src/content/docs/docs}/fork.md | 8 +- .../content/docs/docs/frontend-screenshot.png | Bin 0 -> 120532 bytes apps/frontend/src/content/docs/docs/index.md | 99 + .../frontend/src/content/docs/docs/themes.md | 122 +- apps/frontend/src/index.css | 136 +- apps/frontend/src/index.tsx | 20 - apps/frontend/src/modules.d.ts | 7 - apps/frontend/src/pages/App/Header.tsx | 63 - apps/frontend/src/pages/index.astro | 39 + .../src/redux/selectors/themeSelectors.ts | 27 - apps/frontend/src/redux/slices/theme.ts | 69 - apps/frontend/src/redux/store.ts | 2 - apps/frontend/src/shared/LinkExternal.tsx | 37 + apps/frontend/src/styles/layer-order.css | 8 + apps/frontend/src/styles/starlight-theme.css | 20 + .../src/{pages => wizard}/Home/Home.tsx | 30 +- .../Home/buildCardUrl.test.ts | 8 +- .../{pages => wizard}/Home/buildCardUrl.ts | 12 +- .../src/{pages => wizard}/Home/cardOptions.ts | 8 +- .../Home/stages/Customize.tsx | 36 +- .../{pages => wizard}/Home/stages/Display.tsx | 12 +- .../Home/stages/Login/Login.tsx | 0 .../stages/Login/LoginAccountDeleteModal.tsx | 5 +- .../stages/Login/LoginAccountManagement.tsx | 4 +- .../Home/stages/Login/LoginBox.tsx | 0 .../Home/stages/Login/LoginBoxDemoCards.tsx | 10 +- .../Home/stages/Login/LoginOptions.tsx | 2 +- .../Home/stages/SelectCard.tsx | 12 +- .../{pages => wizard}/Home/stages/Theme.tsx | 21 +- .../Home/useCardDescriptor.ts | 4 +- .../App/AppTrends.tsx => wizard/Wizard.tsx} | 50 +- apps/frontend/src/{ => wizard}/api/user.ts | 2 +- .../src/{ => wizard}/components/Card/Card.tsx | 0 .../components/Card/CardImage.tsx | 2 +- .../components/Card/SvgInline.tsx | 6 +- .../components/Card/themeBackdrop.ts | 0 .../components/Generic/Button.tsx | 0 .../components/Generic/Checkbox.tsx | 0 .../components/Generic/Select.tsx | 0 .../components/Home/CheckboxSection.tsx | 0 .../Home/LanguagesLayoutSection.tsx | 0 .../components/Home/NumericSection.tsx | 0 .../{ => wizard}/components/Home/Progress.tsx | 3 +- .../{ => wizard}/components/Home/Section.tsx | 0 .../components/Home/StatsRankSection.tsx | 0 .../components/Home/TextSection.tsx | 0 .../components/Home/WakatimeLayoutSection.tsx | 0 apps/frontend/src/wizard/docsUrl.ts | 16 + .../{ => wizard}/hooks/useDebouncedField.ts | 0 apps/frontend/src/{ => wizard}/mock-http.ts | 0 .../mockData/additional_user_stars.json | 0 .../mockData/commented_issues.json | 0 .../{ => wizard}/mockData/commented_prs.json | 0 .../src/{ => wizard}/mockData/commits.json | 0 .../{ => wizard}/mockData/gist-graphql.json | 0 .../src/{ => wizard}/mockData/gist-rest.json | 0 .../src/{ => wizard}/mockData/repository.json | 0 .../{ => wizard}/mockData/reviewed_prs.json | 0 .../{ => wizard}/mockData/top_languages.json | 0 .../src/{ => wizard}/mockData/user_stats.json | 0 .../{ => wizard}/mockData/wakatime_proxy.json | 0 .../src/{ => wizard}/models/CardType.ts | 0 .../src/{ => wizard}/models/CardUrl.test.ts | 0 .../src/{ => wizard}/models/CardUrl.ts | 0 .../frontend/src/{ => wizard}/models/Stage.ts | 0 apps/frontend/src/wizard/useIsDarkTheme.ts | 24 + apps/frontend/tsconfig.json | 9 +- .../{vite.config.ts => vitest.config.ts} | 16 - docs/frontend-screenshot.png | Bin 107178 -> 0 bytes eslint.config.js | 2 + knip.jsonc | 3 +- .../core/scripts/generate-theme-readme.js | 7 +- pnpm-lock.yaml | 3982 ++++++++++++++++- pnpm-workspace.yaml | 5 +- vercel-preparation.sh | 2 + vitest.config.ts | 2 +- 93 files changed, 4733 insertions(+), 930 deletions(-) create mode 100644 apps/frontend/astro.config.ts rename apps/frontend/e2e/{app-trends-auth.spec.ts => wizard-auth.spec.ts} (79%) delete mode 100644 apps/frontend/index.html delete mode 100644 apps/frontend/src/components/Generic/ThemePicker.tsx create mode 100644 apps/frontend/src/components/SiteTitle.astro create mode 100644 apps/frontend/src/content.config.ts create mode 100644 apps/frontend/src/content/docs/_README.md rename {docs => apps/frontend/src/content/docs/docs}/advanced_documentation.md (89%) rename {docs => apps/frontend/src/content/docs/docs}/deploy.md (93%) rename {docs => apps/frontend/src/content/docs/docs}/fork.md (96%) create mode 100644 apps/frontend/src/content/docs/docs/frontend-screenshot.png create mode 100644 apps/frontend/src/content/docs/docs/index.md rename packages/core/src/themes/README.md => apps/frontend/src/content/docs/docs/themes.md (65%) delete mode 100644 apps/frontend/src/index.tsx delete mode 100644 apps/frontend/src/pages/App/Header.tsx create mode 100644 apps/frontend/src/pages/index.astro delete mode 100644 apps/frontend/src/redux/selectors/themeSelectors.ts delete mode 100644 apps/frontend/src/redux/slices/theme.ts create mode 100644 apps/frontend/src/shared/LinkExternal.tsx create mode 100644 apps/frontend/src/styles/layer-order.css create mode 100644 apps/frontend/src/styles/starlight-theme.css rename apps/frontend/src/{pages => wizard}/Home/Home.tsx (93%) rename apps/frontend/src/{pages => wizard}/Home/buildCardUrl.test.ts (90%) rename apps/frontend/src/{pages => wizard}/Home/buildCardUrl.ts (90%) rename apps/frontend/src/{pages => wizard}/Home/cardOptions.ts (78%) rename apps/frontend/src/{pages => wizard}/Home/stages/Customize.tsx (92%) rename apps/frontend/src/{pages => wizard}/Home/stages/Display.tsx (88%) rename apps/frontend/src/{pages => wizard}/Home/stages/Login/Login.tsx (100%) rename apps/frontend/src/{pages => wizard}/Home/stages/Login/LoginAccountDeleteModal.tsx (89%) rename apps/frontend/src/{pages => wizard}/Home/stages/Login/LoginAccountManagement.tsx (97%) rename apps/frontend/src/{pages => wizard}/Home/stages/Login/LoginBox.tsx (100%) rename apps/frontend/src/{pages => wizard}/Home/stages/Login/LoginBoxDemoCards.tsx (86%) rename apps/frontend/src/{pages => wizard}/Home/stages/Login/LoginOptions.tsx (96%) rename apps/frontend/src/{pages => wizard}/Home/stages/SelectCard.tsx (89%) rename apps/frontend/src/{pages => wizard}/Home/stages/Theme.tsx (85%) rename apps/frontend/src/{pages => wizard}/Home/useCardDescriptor.ts (95%) rename apps/frontend/src/{pages/App/AppTrends.tsx => wizard/Wizard.tsx} (70%) rename apps/frontend/src/{ => wizard}/api/user.ts (97%) rename apps/frontend/src/{ => wizard}/components/Card/Card.tsx (100%) rename apps/frontend/src/{ => wizard}/components/Card/CardImage.tsx (94%) rename apps/frontend/src/{ => wizard}/components/Card/SvgInline.tsx (97%) rename apps/frontend/src/{ => wizard}/components/Card/themeBackdrop.ts (100%) rename apps/frontend/src/{ => wizard}/components/Generic/Button.tsx (100%) rename apps/frontend/src/{ => wizard}/components/Generic/Checkbox.tsx (100%) rename apps/frontend/src/{ => wizard}/components/Generic/Select.tsx (100%) rename apps/frontend/src/{ => wizard}/components/Home/CheckboxSection.tsx (100%) rename apps/frontend/src/{ => wizard}/components/Home/LanguagesLayoutSection.tsx (100%) rename apps/frontend/src/{ => wizard}/components/Home/NumericSection.tsx (100%) rename apps/frontend/src/{ => wizard}/components/Home/Progress.tsx (92%) rename apps/frontend/src/{ => wizard}/components/Home/Section.tsx (100%) rename apps/frontend/src/{ => wizard}/components/Home/StatsRankSection.tsx (100%) rename apps/frontend/src/{ => wizard}/components/Home/TextSection.tsx (100%) rename apps/frontend/src/{ => wizard}/components/Home/WakatimeLayoutSection.tsx (100%) create mode 100644 apps/frontend/src/wizard/docsUrl.ts rename apps/frontend/src/{ => wizard}/hooks/useDebouncedField.ts (100%) rename apps/frontend/src/{ => wizard}/mock-http.ts (100%) rename apps/frontend/src/{ => wizard}/mockData/additional_user_stars.json (100%) rename apps/frontend/src/{ => wizard}/mockData/commented_issues.json (100%) rename apps/frontend/src/{ => wizard}/mockData/commented_prs.json (100%) rename apps/frontend/src/{ => wizard}/mockData/commits.json (100%) rename apps/frontend/src/{ => wizard}/mockData/gist-graphql.json (100%) rename apps/frontend/src/{ => wizard}/mockData/gist-rest.json (100%) rename apps/frontend/src/{ => wizard}/mockData/repository.json (100%) rename apps/frontend/src/{ => wizard}/mockData/reviewed_prs.json (100%) rename apps/frontend/src/{ => wizard}/mockData/top_languages.json (100%) rename apps/frontend/src/{ => wizard}/mockData/user_stats.json (100%) rename apps/frontend/src/{ => wizard}/mockData/wakatime_proxy.json (100%) rename apps/frontend/src/{ => wizard}/models/CardType.ts (100%) rename apps/frontend/src/{ => wizard}/models/CardUrl.test.ts (100%) rename apps/frontend/src/{ => wizard}/models/CardUrl.ts (100%) rename apps/frontend/src/{ => wizard}/models/Stage.ts (100%) create mode 100644 apps/frontend/src/wizard/useIsDarkTheme.ts rename apps/frontend/{vite.config.ts => vitest.config.ts} (53%) delete mode 100644 docs/frontend-screenshot.png diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 59cb333e379f5..9433e4ee2b982 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -10,7 +10,7 @@ pnpm run build:packages pnpm run dev:frontend ``` -The easiest way to run and test the project is to deploy it to Vercel as described in the [deployment guide](../docs/deploy.md). +The easiest way to run and test the project is to deploy it to Vercel as described in the [deployment guide](../apps/frontend/src/content/docs/docs/deploy.md). ## Tests @@ -46,11 +46,11 @@ Never edit the generated files by hand — change the `.graphql` file and regene ## Themes Contribution -We have stopped the addition of new themes to decrease maintenance efforts. If you are considering contributing your theme just because you are using it personally, then instead of adding it to our theme collection, you can use card [customization options](../docs/advanced_documentation.md#customization). +We have stopped the addition of new themes to decrease maintenance efforts. If you are considering contributing your theme just because you are using it personally, then instead of adding it to our theme collection, you can use card [customization options](../apps/frontend/src/content/docs/docs/advanced_documentation.md#customization). ## Translations Contribution -GitHub-Stats-Extended supports multiple languages. If we are missing your language, you can contribute it! You can check the currently supported languages [here](../docs/advanced_documentation.md#available-locales). +GitHub-Stats-Extended supports multiple languages. If we are missing your language, you can contribute it! You can check the currently supported languages [here](../apps/frontend/src/content/docs/docs/advanced_documentation.md#available-locales). To contribute your language you need to edit the [backend/src/translations.js](../backend/src/translations.js) file and add a new property to each object where the key is the language code in [ISO 639-1 standard](https://www.andiamo.co.uk/resources/iso-language-codes/) and the value is the translated string. diff --git a/.gitignore b/.gitignore index d314c97112b67..e2a871679be21 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,8 @@ apps/backend/vercel_token apps/backend-copy apps/frontend/.env +# Astro's generated types and content store +apps/frontend/.astro .turbo diff --git a/README.md b/README.md index 46f255235ced4..7b32b96924fb5 100644 --- a/README.md +++ b/README.md @@ -5,82 +5,34 @@ -This project is the [extended, actively maintained successor](docs/fork.md) of [github-readme-stats](https://github.com/anuraghazra/github-readme-stats). It generates [various stats cards](#card-types), e.g. about your GitHub contributions, your top languages, etc. You can [customize](#advanced-customization) the cards via multiple parameters. - -## Table of Contents - -- [Quick Start](#quick-start) -- [Migration from github-readme-stats](#migration-from-github-readme-stats) -- [Card Types](#card-types) -- [Advanced Customization](#advanced-customization) -- [Run It Yourself](#run-it-yourself) -- [Acknowledgements](#acknowledgements) -- [Contributing](#contributing) +This project is the [extended, actively maintained successor](https://github-stats-extended.vercel.app/frontend/docs/fork/) of [github-readme-stats](https://github.com/anuraghazra/github-readme-stats). It generates stats cards about your GitHub contributions, your top languages and more, all customizable through a large set of parameters. ## Quick Start -- Copy and paste this into your markdown: - ```markdown - [![Anurag's GitHub stats](https://github-stats-extended.vercel.app/api?username=anuraghazra)](https://github.com/stats-organization/github-stats-extended) - ``` -- Change the `?username=` value to your GitHub username. -- Done! - ---- - -As more comfortable alternative, use the [GitHub-Stats-Extended Wizard](https://github-stats-extended.vercel.app/frontend) to create your custom stats card. Copy the generated markdown code and paste it into your [GitHub profile README](https://docs.github.com/en/account-and-profile/how-tos/profile-customization/managing-your-profile-readme#adding-a-profile-readme). Done! - -## Migration from github-readme-stats +Copy and paste this into your markdown, then change the `?username=` value to your GitHub username: -To migrate from [github-readme-stats](https://github.com/anuraghazra/github-readme-stats) you only need to change the domain from `github-readme-stats.vercel.app` to `github-stats-extended.vercel.app`: - -```diff -- https://github-readme-stats.vercel.app/api?username=octocat&theme=radical -+ https://github-stats-extended.vercel.app/api?username=octocat&theme=radical +```markdown +[![Anurag's GitHub stats](https://github-stats-extended.vercel.app/api?username=anuraghazra)](https://github.com/stats-organization/github-stats-extended) ``` -GitHub-Stats-Extended aims to be fully compatible with github-readme-stats. For more details see [Compatibility Notes](docs/fork.md#compatibility-notes). - -## Card Types - -- Show your GitHub statistics: - - ![Anurag's GitHub stats](https://github-stats-extended.vercel.app/api?username=anuraghazra) - -- ...your top languages...: - - ![Top Langs](https://github-stats-extended.vercel.app/api/top-langs/?username=anuraghazra&langs_count=4) - -- ...and development time: - - [![Alan's WakaTime stats](https://github-stats-extended.vercel.app/api/wakatime?username=alan&langs_count=6)](https://wakatime.com/@alan) - -- Pin more than 6 repos in your GitHub profile: - - [![Readme Card](https://github-stats-extended.vercel.app/api/pin/?username=anuraghazra&repo=github-readme-stats)](https://github.com/anuraghazra/github-readme-stats) - -- Pin Gists in your GitHub profile: - - [![Gist Card](https://github-stats-extended.vercel.app/api/gist?id=bbfce31e0217a3689c8d961a356cb10d)](https://gist.github.com/Yizack/bbfce31e0217a3689c8d961a356cb10d) - -- Customize all the cards: - - [![Anurag's GitHub stats](https://github-stats-extended.vercel.app/api/?username=anuraghazra&show_icons=true&theme=calm&rank_icon=github&include_all_commits=true&custom_title=Anurag's+Stats&disable_animations=true&number_format=long&show=prs_merged_percentage,prs_reviewed)](https://github-stats-extended.vercel.app/api/?username=anuraghazra&show_icons=true&theme=calm&rank_icon=github&include_all_commits=true&custom_title=Anurag's+Stats&disable_animations=true&number_format=long&show=prs_merged_percentage,prs_reviewed) - -## Advanced Customization +As a more comfortable alternative, use the [card wizard](https://github-stats-extended.vercel.app/frontend) to configure your card visually and copy the generated markdown. -The [GitHub-Stats-Extended Wizard](https://github-stats-extended.vercel.app/frontend) offers some essential customization options. For more advanced customization check out the [advanced documentation](docs/advanced_documentation.md). +## Documentation -## Run It Yourself +The full documentation is served by the deployment it describes, so every sample it shows is rendered by the version that is actually running: -If you want to run GitHub-Stats-Extended on your own, there are two main deployment options: you can use [github-readme-stats-action](https://github.com/stats-organization/github-readme-stats-action) to generate cards in your own GitHub Actions workflow. Or you can self-host GitHub-Stats-Extended on Vercel. +**[github-stats-extended.vercel.app/frontend/docs](https://github-stats-extended.vercel.app/frontend/docs)** -See [Run It Yourself](docs/deploy.md) for detailed instructions. +- [Overview](https://github-stats-extended.vercel.app/frontend/docs/) — card types, quick start and migration from github-readme-stats. +- [Advanced Customization](https://github-stats-extended.vercel.app/frontend/docs/advanced_documentation/) — every parameter the cards accept. +- [Available Themes](https://github-stats-extended.vercel.app/frontend/docs/themes/) — the built-in themes. +- [Run It Yourself](https://github-stats-extended.vercel.app/frontend/docs/deploy/) — GitHub Actions or a self-hosted Vercel deployment. +- [Fork Information](https://github-stats-extended.vercel.app/frontend/docs/fork/) — what this project adds on top of github-readme-stats. ## Acknowledgements -This project is based on [github-readme-stats](https://github.com/anuraghazra/github-readme-stats). On top of that project's functionality GitHub-Stats-Extended adds several new features and improvements. See [Fork Information](docs/fork.md) for a list of changes. The frontend added to GitHub-Stats-Extended is based on [GitHub Trends](https://github.com/avgupta456/github-trends). Big thanks to [@anuraghazra](https://github.com/anuraghazra), [@avgupta456](https://github.com/avgupta456), [@rickstaa](https://github.com/rickstaa), [@qwerty541](https://github.com/qwerty541) and everyone else who worked on these projects! ❤️ +This project is based on [github-readme-stats](https://github.com/anuraghazra/github-readme-stats), and the card wizard on [GitHub Trends](https://github.com/avgupta456/github-trends). Big thanks to [@anuraghazra](https://github.com/anuraghazra), [@avgupta456](https://github.com/avgupta456), [@rickstaa](https://github.com/rickstaa), [@qwerty541](https://github.com/qwerty541) and everyone else who worked on these projects! ❤️ ## Contributing -Contributions are welcome! +Contributions are welcome! See [CONTRIBUTING.md](.github/CONTRIBUTING.md) to get started. diff --git a/apps/frontend/astro.config.ts b/apps/frontend/astro.config.ts new file mode 100644 index 0000000000000..2f670f5734b2a --- /dev/null +++ b/apps/frontend/astro.config.ts @@ -0,0 +1,69 @@ +import path from "node:path"; + +import react from "@astrojs/react"; +import starlight from "@astrojs/starlight"; +import tailwindcss from "@tailwindcss/vite"; +import { defineConfig, passthroughImageService } from "astro/config"; +import starlightLinksValidator from "starlight-links-validator"; + +/* + * One app: Starlight serves the docs under `/frontend/docs`, and the card + * wizard is a page of the same site at `/frontend`. + */ +export default defineConfig({ + site: "https://github-stats-extended.vercel.app", + base: "/frontend", + outDir: "./build", + // One screenshot; not worth a native image dependency. + image: { service: passthroughImageService() }, + integrations: [ + react(), + starlight({ + title: "GitHub Stats Extended", + description: "Dynamically generate GitHub stats for your READMEs.", + logo: { src: "./src/assets/appLogo64.png", alt: "" }, + social: [ + { + icon: "github", + label: "GitHub", + href: "https://github.com/stats-organization/github-stats-extended", + }, + ], + // The docs link to each other by site path, so a rename must fail the build. + plugins: [starlightLinksValidator()], + customCss: ["./src/styles/starlight-theme.css"], + components: { SiteTitle: "./src/components/SiteTitle.astro" }, + sidebar: [ + { label: "Overview", slug: "docs" }, + { + label: "Advanced Customization", + slug: "docs/advanced_documentation", + }, + { label: "Available Themes", slug: "docs/themes" }, + { label: "Run It Yourself", slug: "docs/deploy" }, + { label: "Fork Information", slug: "docs/fork" }, + ], + }), + ], + vite: { + plugins: [tailwindcss()], + resolve: { + conditions: ["@stats/source"], + alias: [ + { + find: "../fetchers/wakatime.js", + replacement: path.resolve( + import.meta.dirname, + "src/wakatime-override.ts", + ), + }, + ], + }, + // The backend code the wizard reuses imports `pg`, which never runs in the browser. + build: { + rollupOptions: { + external: ["pg"], + }, + }, + }, +}); diff --git a/apps/frontend/e2e/app.spec.ts b/apps/frontend/e2e/app.spec.ts index 04f41e5e8c1b8..63b2b5e674be0 100644 --- a/apps/frontend/e2e/app.spec.ts +++ b/apps/frontend/e2e/app.spec.ts @@ -1,25 +1,29 @@ import { expect, test } from "@playwright/test"; +const REPO_URL = "https://github.com/stats-organization/github-stats-extended"; + test("load initial page correctly", async ({ page }) => { await page.goto(""); // Expect a title "to contain" a substring. await expect(page).toHaveTitle(/GitHub Stats Extended/); - // Page title / header branding - await expect(page.getByText("GitHub Stats Extended")).toBeVisible(); - - // Logo exists - await expect(page.locator('img[alt="logo"]')).toBeVisible(); + // Header branding, with the logo beside the wordmark. + const siteTitle = page.getByRole("link", { name: "GitHub Stats Extended" }); + await expect(siteTitle).toBeVisible(); + await expect(siteTitle.locator("img")).toBeVisible(); - // Star on GitHub link - const starButton = page.getByRole("link", { name: /star on/i }); - await expect(starButton).toBeVisible(); + // The two halves of the site, with this one marked as current. + await expect( + page.getByRole("link", { name: "Wizard", exact: true }), + ).toHaveAttribute("aria-current", "page"); + await expect( + page.getByRole("link", { name: "Docs", exact: true }), + ).toHaveAttribute("href", "/frontend/docs/"); - const githubLink = page.locator( - 'a[href*="github.com/stats-organization/github-stats-extended"]', - ); - await expect(githubLink).toHaveAttribute("target", "_blank"); + await expect( + page.getByRole("link", { name: "GitHub", exact: true }), + ).toHaveAttribute("href", REPO_URL); // Login buttons const publicAccessBtn = page.getByRole("button", { @@ -39,72 +43,53 @@ test("load initial page correctly", async ({ page }) => { await expect(guestBtn).toBeEnabled(); }); -test("theme picker switches and persists the theme", async ({ page }) => { +test("theme selection applies, persists and reaches the wizard", async ({ + page, +}) => { await page.goto(""); const html = page.locator("html"); - const trigger = page.getByRole("button", { name: /choose theme/i }); - const options = page.getByRole("menuitemradio"); - - // The menu is closed initially. - await expect(trigger).toBeVisible(); - await expect(trigger).toHaveAttribute("aria-expanded", "false"); - await expect(options).toHaveCount(0); - - // Opening it reveals the options (portaled to body, above the stepper). - await trigger.click(); - await expect(trigger).toHaveAttribute("aria-expanded", "true"); - - // Read the themes off the options instead of hardcoding their names. - const firstTheme = await options.first().getAttribute("data-theme-value"); - const lastTheme = await options.last().getAttribute("data-theme-value"); - - // Selecting an option applies the theme, persists it, and closes the menu. - await options.last().click(); - await expect(html).toHaveAttribute("data-theme", lastTheme ?? ""); - await expect(options).toHaveCount(0); + const themeSelect = page.getByRole("combobox", { name: "Select theme" }); + + // The site's own control owns `data-theme`, which is also what daisyUI reads. + await themeSelect.selectOption("dark"); + await expect(html).toHaveAttribute("data-theme", "dark"); await expect( - page.evaluate(() => localStorage.getItem("theme")), - ).resolves.toBe(lastTheme); + page.evaluate(() => localStorage.getItem("starlight-theme")), + ).resolves.toBe("dark"); - // The choice survives a reload and is marked as selected on reopen. + // The choice survives a reload. await page.reload(); - await expect(html).toHaveAttribute("data-theme", lastTheme ?? ""); - await trigger.click(); - await expect(options.last()).toHaveAttribute("aria-checked", "true"); + await expect(html).toHaveAttribute("data-theme", "dark"); + await expect(themeSelect).toHaveValue("dark"); - // Switching to another option works too. - await options.first().click(); - await expect(html).toHaveAttribute("data-theme", firstTheme ?? ""); + // Switching back works too. + await themeSelect.selectOption("light"); + await expect(html).toHaveAttribute("data-theme", "light"); }); test("navigates between steps", async ({ page }) => { await page.goto(""); + // Scoped to the page content: Astro's dev toolbar has headings of its own. + const heading = page.locator("main").getByRole("heading", { level: 1 }); + // We are at stage 1 - await expect(page.getByRole("heading", { level: 1 })).toContainText("Login"); + await expect(heading).toContainText("Login"); // Go to stage 2 await page.getByRole("button", { name: "Select card" }).click(); - await expect(page.getByRole("heading", { level: 1 })).toContainText( - "Select a Card", - ); + await expect(heading).toContainText("Select a Card"); // Go to stage 3 await page.getByRole("button", { name: "Modify parameters" }).click(); - await expect(page.getByRole("heading", { level: 1 })).toContainText( - "Modify Card Parameters", - ); + await expect(heading).toContainText("Modify Card Parameters"); // Go to stage 4 await page.getByRole("button", { name: "Select theme" }).click(); - await expect(page.getByRole("heading", { level: 1 })).toContainText( - "Choose a Theme", - ); + await expect(heading).toContainText("Choose a Theme"); // Go to stage 5 await page.getByRole("button", { name: "Display card" }).click(); - await expect(page.getByRole("heading", { level: 1 })).toContainText( - "Display your Card", - ); + await expect(heading).toContainText("Display your Card"); }); diff --git a/apps/frontend/e2e/stats-rank.spec.ts b/apps/frontend/e2e/stats-rank.spec.ts index 4bbfe8b4375fe..b1180e2f74040 100644 --- a/apps/frontend/e2e/stats-rank.spec.ts +++ b/apps/frontend/e2e/stats-rank.spec.ts @@ -7,9 +7,10 @@ test("selecting 'None' progress style hides the rank circle", async ({ // Stage 0 -> 2: go to customization await page.getByRole("button", { name: "Modify Parameters" }).click(); - await expect(page.getByRole("heading", { level: 1 })).toContainText( - "Modify Card Parameters", - ); + // Scoped to the page content: Astro's dev toolbar has headings of its own. + await expect( + page.locator("main").getByRole("heading", { level: 1 }), + ).toContainText("Modify Card Parameters"); const preview = page.locator("#svgWrapper"); const rankCircle = preview.locator('[data-testid="rank-circle"]'); @@ -18,7 +19,11 @@ test("selecting 'None' progress style hides the rank circle", async ({ await expect(preview).toBeAttached(); await expect(rankCircle).toHaveCount(1); - await page.getByRole("combobox").selectOption({ label: "None" }); + // Scoped: the site header has a theme combobox of its own. + await page + .locator("main") + .getByRole("combobox") + .selectOption({ label: "None" }); await expect(rankCircle).toHaveCount(0); }); diff --git a/apps/frontend/e2e/app-trends-auth.spec.ts b/apps/frontend/e2e/wizard-auth.spec.ts similarity index 79% rename from apps/frontend/e2e/app-trends-auth.spec.ts rename to apps/frontend/e2e/wizard-auth.spec.ts index 6976e1eb0fc33..5e7e054a72f18 100644 --- a/apps/frontend/e2e/app-trends-auth.spec.ts +++ b/apps/frontend/e2e/wizard-auth.spec.ts @@ -5,7 +5,7 @@ import type { Page } from "@playwright/test"; * Puts the SPA into an authenticated state without contacting GitHub by * stubbing the OAuth code exchange and the follow-up user-access lookup: * `authenticate` returns a userId (which flips `isAuthenticated` to true), and - * `user-access` returns metadata so AppTrends does not immediately log back out. + * `user-access` returns metadata so the wizard does not immediately log back out. * @param page - The Playwright page to install the route handlers on. */ async function mockAuthEndpoints(page: Page): Promise { @@ -25,7 +25,7 @@ async function mockAuthEndpoints(page: Page): Promise { ); } -test.describe("AppTrends auth-driven stage transition", () => { +test.describe("the wizard auth-driven stage transition", () => { test("auto-advances from Login to Select a Card once authenticated", async ({ page, }) => { @@ -34,12 +34,13 @@ test.describe("AppTrends auth-driven stage transition", () => { // Land on the app as GitHub does after the OAuth redirect (URL carries `code`). await page.goto("?code=test-oauth-code"); - // AppTrends starts unauthenticated on stage 0 ("Login"). When the code + // the wizard starts unauthenticated on stage 0 ("Login"). When the code // exchange flips `isAuthenticated` false -> true, the render-phase // transition must advance the stepper to stage 1 ("Select a Card"). - await expect(page.getByRole("heading", { level: 1 })).toContainText( - "Select a Card", - ); + // Scoped to the page content: Astro's dev toolbar has headings of its own. + await expect( + page.locator("main").getByRole("heading", { level: 1 }), + ).toContainText("Select a Card"); }); test("keeps a manually selected earlier step instead of re-forcing it", async ({ @@ -48,7 +49,7 @@ test.describe("AppTrends auth-driven stage transition", () => { await mockAuthEndpoints(page); await page.goto("?code=test-oauth-code"); - const heading = page.getByRole("heading", { level: 1 }); + const heading = page.locator("main").getByRole("heading", { level: 1 }); await expect(heading).toContainText("Select a Card"); // Going back to "Login" while still authenticated must stick: the transition diff --git a/apps/frontend/index.html b/apps/frontend/index.html deleted file mode 100644 index ff41b6247fa2d..0000000000000 --- a/apps/frontend/index.html +++ /dev/null @@ -1,62 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - GitHub Stats Extended - - - - -
- - - diff --git a/apps/frontend/package.json b/apps/frontend/package.json index 13bce35494c22..8192723d27dbd 100644 --- a/apps/frontend/package.json +++ b/apps/frontend/package.json @@ -4,19 +4,22 @@ "license": "MIT", "private": true, "scripts": { - "dev": "vite", - "build": "vite build", - "preview": "vite preview", + "dev": "astro dev", + "build": "astro build", + "preview": "astro preview", "test": "vitest", "test:e2e": "playwright test", - "lint": "eslint", - "typecheck": "tsc -p tsconfig.typecheck.json" + "lint": "astro sync && eslint", + "typecheck": "astro sync && tsc -p tsconfig.typecheck.json" }, "dependencies": { + "@astrojs/react": "^6.0.2", + "@astrojs/starlight": "0.41.7", "@reduxjs/toolkit": "^2.12.0", "@stats-organization/github-readme-stats-backend": "workspace:^", "@stats-organization/github-readme-stats-core": "workspace:^", "@tailwindcss/vite": "^4.3.3", + "astro": "7.2.0", "axios": "catalog:default", "axios-cache-interceptor": "^1.12.0", "daisyui": "^5.6.18", @@ -34,10 +37,10 @@ "devDependencies": { "@types/react": "19.2.17", "@types/react-dom": "19.2.3", - "@vitejs/plugin-react": "6.0.3", "clsx": "2.1.1", + "jsdom": "catalog:default", + "starlight-links-validator": "0.25.2", "tailwindcss": "4.3.3", - "vite": "catalog:default", "vitest": "catalog:default" }, "browserslist": { diff --git a/apps/frontend/playwright.config.ts b/apps/frontend/playwright.config.ts index 0e38be08fcd6a..81684b84182e9 100644 --- a/apps/frontend/playwright.config.ts +++ b/apps/frontend/playwright.config.ts @@ -8,7 +8,7 @@ import { defineConfig, devices } from "@playwright/test"; // import path from 'path'; // dotenv.config({ path: path.resolve(__dirname, '.env') }); -const baseURL = "http://localhost:5173/frontend/"; +const baseURL = "http://localhost:4321/frontend/"; /** * See https://playwright.dev/docs/test-configuration. @@ -25,6 +25,8 @@ export default defineConfig({ workers: 1, /* Reporter to use. See https://playwright.dev/docs/test-reporters */ reporter: "html", + // The wizard is a client-only island; on a cold dev server its bundle takes a while. + expect: { timeout: 15_000 }, /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ use: { /* Base URL to use in actions like `await page.goto('')`. */ @@ -77,5 +79,7 @@ export default defineConfig({ command: "pnpm run dev", url: baseURL, reuseExistingServer: !process.env["CI"], + // `astro dev` detaches itself in an AI-agent shell, which reads as exiting early. + env: { ASTRO_DEV_BACKGROUND: "1" }, }, }); diff --git a/apps/frontend/src/axios-override.ts b/apps/frontend/src/axios-override.ts index c518926f3fb79..a007ed1a58a53 100644 --- a/apps/frontend/src/axios-override.ts +++ b/apps/frontend/src/axios-override.ts @@ -8,17 +8,17 @@ import { DEMO_WAKATIME_USER, HOST, } from "./constants"; -import additionalUserStars from "./mockData/additional_user_stars.json" with { type: "json" }; -import commentedIssues from "./mockData/commented_issues.json" with { type: "json" }; -import commentedPrs from "./mockData/commented_prs.json" with { type: "json" }; -import commits from "./mockData/commits.json" with { type: "json" }; -import gist_graphql from "./mockData/gist-graphql.json" with { type: "json" }; -import gist_rest from "./mockData/gist-rest.json" with { type: "json" }; -import repository from "./mockData/repository.json" with { type: "json" }; -import reviewedPrs from "./mockData/reviewed_prs.json" with { type: "json" }; -import topLanguages from "./mockData/top_languages.json" with { type: "json" }; -import userStats from "./mockData/user_stats.json" with { type: "json" }; -import wakatimeProxy from "./mockData/wakatime_proxy.json" with { type: "json" }; +import additionalUserStars from "./wizard/mockData/additional_user_stars.json" with { type: "json" }; +import commentedIssues from "./wizard/mockData/commented_issues.json" with { type: "json" }; +import commentedPrs from "./wizard/mockData/commented_prs.json" with { type: "json" }; +import commits from "./wizard/mockData/commits.json" with { type: "json" }; +import gist_graphql from "./wizard/mockData/gist-graphql.json" with { type: "json" }; +import gist_rest from "./wizard/mockData/gist-rest.json" with { type: "json" }; +import repository from "./wizard/mockData/repository.json" with { type: "json" }; +import reviewedPrs from "./wizard/mockData/reviewed_prs.json" with { type: "json" }; +import topLanguages from "./wizard/mockData/top_languages.json" with { type: "json" }; +import userStats from "./wizard/mockData/user_stats.json" with { type: "json" }; +import wakatimeProxy from "./wizard/mockData/wakatime_proxy.json" with { type: "json" }; const cachedAxios = setupCache(axios, { // Cache for 30 minutes diff --git a/apps/frontend/src/components/Generic/ThemePicker.tsx b/apps/frontend/src/components/Generic/ThemePicker.tsx deleted file mode 100644 index a4397497f62d0..0000000000000 --- a/apps/frontend/src/components/Generic/ThemePicker.tsx +++ /dev/null @@ -1,131 +0,0 @@ -import { useCallback, useEffect, useRef, useState } from "react"; -import type { JSX } from "react"; -import { createPortal } from "react-dom"; -import { - FaCheck as CheckIcon, - FaMoon as MoonIcon, - FaSun as SunIcon, -} from "react-icons/fa"; - -import { useTheme } from "../../redux/selectors/themeSelectors"; -import { THEMES } from "../../redux/slices/theme"; - -interface ThemeIconProps { - isDark: boolean; - className: string; -} - -/** Sun for light themes, moon for dark ones. */ -function ThemeIcon({ isDark, className }: ThemeIconProps): JSX.Element { - return isDark ? ( - - ) : ( - - ); -} - -export function ThemePicker(): JSX.Element { - const { theme, isDark, setTheme } = useTheme(); - const [open, setOpen] = useState(false); - const [position, setPosition] = useState({ top: 0, right: 0 }); - const buttonRef = useRef(null); - const menuRef = useRef(null); - - const handleToggle = useCallback(() => { - if (!open && buttonRef.current) { - const rect = buttonRef.current.getBoundingClientRect(); - setPosition({ - top: rect.bottom + 8, - right: window.innerWidth - rect.right, - }); - } - setOpen((value) => !value); - }, [open]); - - useEffect(() => { - if (!open) { - return undefined; - } - - const close = () => { - setOpen(false); - }; - const handlePointerDown = (event: MouseEvent) => { - const target = event.target as Node; - if ( - !menuRef.current?.contains(target) && - !buttonRef.current?.contains(target) - ) { - setOpen(false); - } - }; - const handleKeyDown = (event: KeyboardEvent) => { - if (event.key === "Escape") { - setOpen(false); - } - }; - - document.addEventListener("mousedown", handlePointerDown); - document.addEventListener("keydown", handleKeyDown); - window.addEventListener("resize", close); - // Close on scroll so the fixed menu can't detach from its button. - window.addEventListener("scroll", close, true); - - return () => { - document.removeEventListener("mousedown", handlePointerDown); - document.removeEventListener("keydown", handleKeyDown); - window.removeEventListener("resize", close); - window.removeEventListener("scroll", close, true); - }; - }, [open]); - - return ( - <> - - - {open && - createPortal( -
    - {THEMES.map((option) => ( -
  • - -
  • - ))} -
, - document.body, - )} - - ); -} diff --git a/apps/frontend/src/components/SiteTitle.astro b/apps/frontend/src/components/SiteTitle.astro new file mode 100644 index 0000000000000..2607fd72b98eb --- /dev/null +++ b/apps/frontend/src/components/SiteTitle.astro @@ -0,0 +1,56 @@ +--- +// Starlight has no nav slot, so the two top-level destinations sit beside the title. +import Default from "@astrojs/starlight/components/SiteTitle.astro"; + +const base = import.meta.env.BASE_URL.replace(/\/$/, ""); +const path = Astro.url.pathname; +const links = [ + { href: `${base}/`, label: "Wizard", current: !path.startsWith(`${base}/docs`) }, + { href: `${base}/docs/`, label: "Docs", current: path.startsWith(`${base}/docs`) }, +]; +--- + + + + + + diff --git a/apps/frontend/src/content.config.ts b/apps/frontend/src/content.config.ts new file mode 100644 index 0000000000000..6eb205a7fcc2f --- /dev/null +++ b/apps/frontend/src/content.config.ts @@ -0,0 +1,13 @@ +import { defineCollection } from "astro:content"; + +import { docsLoader } from "@astrojs/starlight/loaders"; +import { docsSchema } from "@astrojs/starlight/schema"; + +/* + * Starlight fixes this collection at `src/content/docs/` and maps it to the site root, + * which the wizard owns — hence the pages sitting one level down, in `docs/`. + * https://starlight.astro.build/guides/pages/#pages-from-markdown + */ +export const collections = { + docs: defineCollection({ loader: docsLoader(), schema: docsSchema() }), +}; diff --git a/apps/frontend/src/content/docs/_README.md b/apps/frontend/src/content/docs/_README.md new file mode 100644 index 0000000000000..1f56e1cc9ae52 --- /dev/null +++ b/apps/frontend/src/content/docs/_README.md @@ -0,0 +1,14 @@ +# Why `docs/docs` + +Starlight fixes its content collection at `src/content/docs/`, and maps that folder to the **site root** (`/frontend`), which the card wizard occupies (`src/pages/index.astro`). + +The pages therefore sit one level down, in `docs/`, so they publish at `/frontend/docs/`. +Nothing else can be renamed: the outer name is Starlight's, the inner one is the URL segment. + +Flattening this needs a custom `generateId` on the loader, which is the one thing `starlight-links-validator` cannot follow. +It derives valid routes from file paths, so every internal link would be reported broken. +The nesting buys build-time dead-link checking. + +Starlight ignores files starting with `_`, which is why this note isn't published. + +See . diff --git a/docs/advanced_documentation.md b/apps/frontend/src/content/docs/docs/advanced_documentation.md similarity index 89% rename from docs/advanced_documentation.md rename to apps/frontend/src/content/docs/docs/advanced_documentation.md index 01d2acb36f522..b7ea8b2ee7993 100644 --- a/docs/advanced_documentation.md +++ b/apps/frontend/src/content/docs/docs/advanced_documentation.md @@ -1,22 +1,16 @@ -# Advanced Customization - -## Table of Contents - -- [GitHub Stats Card](#github-stats-card) -- [GitHub Extra Pins](#github-extra-pins) -- [GitHub Gist Pins](#github-gist-pins) -- [Top Languages Card](#top-languages-card) -- [WakaTime Stats Card](#wakatime-stats-card) -- [All Demos](#all-demos) -- [Quick Tip (Align The Cards)](#quick-tip-align-the-cards) +--- +title: Advanced Customization +--- ## GitHub Stats Card -> [!WARNING] -> By default, the stats card only shows statistics like stars, commits, and pull requests from public repositories. To show private statistics on the stats card, [allow GitHub-Stats-Extended to access your private contributions](fork.md#private-contributions-support) or [deploy your own instance](deploy.md). +:::caution[Warning] +By default, the stats card only shows statistics like stars, commits, and pull requests from public repositories. To show private statistics on the stats card, [allow GitHub-Stats-Extended to access your private contributions](/frontend/docs/fork/#private-contributions-support) or [deploy your own instance](/frontend/docs/deploy/). +::: -> [!NOTE] -> Available ranks are S (top 1%), A+ (12.5%), A (25%), A- (37.5%), B+ (50%), B (62.5%), B- (75%), C+ (87.5%) and C (everyone). This ranking scheme is based on the [Japanese academic grading](https://wikipedia.org/wiki/Academic_grading_in_Japan) system. The global percentile is calculated as a weighted sum of percentiles for each statistic (number of commits, pull requests, reviews, issues, stars, and followers), based on the cumulative distribution function of the [exponential](https://wikipedia.org/wiki/exponential_distribution) and the [log-normal](https://wikipedia.org/wiki/Log-normal_distribution) distributions. The implementation can be investigated at [calculateRank.ts](https://github.com/stats-organization/github-stats-extended/blob/master/packages/core/src/calculateRank.ts). The circle around the rank shows 100 minus the global percentile. +:::note +Available ranks are S (top 1%), A+ (12.5%), A (25%), A- (37.5%), B+ (50%), B (62.5%), B- (75%), C+ (87.5%) and C (everyone). This ranking scheme is based on the [Japanese academic grading](https://wikipedia.org/wiki/Academic_grading_in_Japan) system. The global percentile is calculated as a weighted sum of percentiles for each statistic (number of commits, pull requests, reviews, issues, stars, and followers), based on the cumulative distribution function of the [exponential](https://wikipedia.org/wiki/exponential_distribution) and the [log-normal](https://wikipedia.org/wiki/Log-normal_distribution) distributions. The implementation can be investigated at [calculateRank.ts](https://github.com/stats-organization/github-stats-extended/blob/master/packages/core/src/calculateRank.ts). The circle around the rank shows 100 minus the global percentile. +::: ### Hiding individual stats @@ -78,7 +72,7 @@ GitHub Stats Extended comes with several built-in themes (e.g. `dark`, `radical` GitHub Stats Extended Themes -You can look at a preview for [all available themes](../packages/core/src/themes/README.md) or checkout the [theme config file](../packages/core/src/themes/index.ts). Please note that we paused the addition of new themes to decrease maintenance efforts; all pull requests related to new themes will be closed. +You can look at a preview for [all available themes](/frontend/docs/themes/) or checkout the [theme config file](https://github.com/stats-organization/github-stats-extended/blob/master/packages/core/src/themes/index.ts). Please note that we paused the addition of new themes to decrease maintenance efforts; all pull requests related to new themes will be closed. #### Responsive Card Theme @@ -104,7 +98,7 @@ You can use [GitHub's new media feature](https://github.blog/changelog/2022-05-1 ```
-:eyes: Show example +👀 Show example -:eyes: Show example +👀 Show example [![Anurag's GitHub stats-Dark](https://github-stats-extended.vercel.app/api?username=anuraghazra&show_icons=true&theme=dark#gh-dark-mode-only)](https://github.com/stats-organization/github-stats-extended#gh-dark-mode-only) [![Anurag's GitHub stats-Light](https://github-stats-extended.vercel.app/api?username=anuraghazra&show_icons=true&theme=default#gh-light-mode-only)](https://github.com/stats-organization/github-stats-extended#gh-light-mode-only) @@ -143,7 +137,7 @@ We have included a `transparent` theme that has a transparent background. This t ```
-:eyes: Show example +👀 Show example ![Anurag's GitHub stats](https://github-stats-extended.vercel.app/api?username=anuraghazra&show_icons=true&theme=transparent) @@ -151,14 +145,14 @@ We have included a `transparent` theme that has a transparent background. This t ##### Add transparent alpha channel to a themes bg\_color -You can use the `bg_color` parameter to make any of [the available themes](../packages/core/src/themes/README.md) transparent. This is done by setting the `bg_color` to a color with a transparent alpha channel (i.e. `bg_color=00000000`): +You can use the `bg_color` parameter to make any of [the available themes](/frontend/docs/themes/) transparent. This is done by setting the `bg_color` to a color with a transparent alpha channel (i.e. `bg_color=00000000`): ```md ![Anurag's GitHub stats](https://github-stats-extended.vercel.app/api?username=anuraghazra&show_icons=true&bg_color=00000000) ```
-:eyes: Show example +👀 Show example ![Anurag's GitHub stats](https://github-stats-extended.vercel.app/api?username=anuraghazra&show_icons=true&bg_color=00000000) @@ -178,13 +172,14 @@ You can customize the appearance of all your cards however you wish with URL par | `border_color` | Card's border color. Does not apply when `hide_border` is enabled. | string (hex color) | `e4e2e2` | | `bg_color` | Card's background color. | string (hex color or a gradient in the form of _angle,start,end_) | `fffefe` | | `hide_border` | Hides the card's border. | boolean | `false` | -| `theme` | Name of the theme, choose from [all available themes](../packages/core/src/themes/README.md). | enum | `default` | +| `theme` | Name of the theme, choose from [all available themes](/frontend/docs/themes/). | enum | `default` | | `cache_seconds` | Sets the cache header manually (min: 21600, max: 86400). | integer | `21600` | | `locale` | Sets the language in the card, you can check full list of available locales [here](#available-locales). | enum | `en` | | `border_radius` | Corner rounding on the card. | number | `4.5` | -> [!WARNING] -> We use caching to decrease the load on our servers (see ). Cards generated by [https://github-stats-extended.vercel.app/](https://github-stats-extended.vercel.app/frontend) are cached for a few hours or days, depending on server load. If you want the data on your cards to be updated more often you can [deploy your own instance](deploy.md) and set [environment variable](deploy.md#available-environment-variables) `CACHE_SECONDS` to a value of your choosing. Or you can use the [GitHub Action workflow](https://github.com/stats-organization/github-readme-stats-action). +:::caution[Warning] +We use caching to decrease the load on our servers (see ). Cards generated by [https://github-stats-extended.vercel.app/](https://github-stats-extended.vercel.app/frontend) are cached for a few hours or days, depending on server load. If you want the data on your cards to be updated more often you can [deploy your own instance](/frontend/docs/deploy/) and set [environment variable](/frontend/docs/deploy/#available-environment-variables) `CACHE_SECONDS` to a value of your choosing. Or you can use the [GitHub Action workflow](https://github.com/stats-organization/github-readme-stats-action). +::: ##### Gradient in bg\_color @@ -262,7 +257,7 @@ Here is a list of all available locales: -If we don't support your language, please consider contributing! You can find more information about how to do it in our [contributing guidelines](../.github/CONTRIBUTING.md#translations-contribution). +If we don't support your language, please consider contributing! You can find more information about how to do it in our [contributing guidelines](https://github.com/stats-organization/github-stats-extended/blob/master/.github/CONTRIBUTING.md#translations-contribution). #### Stats Card Exclusive Options @@ -289,11 +284,13 @@ If we don't support your language, please consider contributing! You can find mo | `show` | Shows [additional items](#showing-additional-individual-stats) on stats card (i.e. `reviews`, `discussions_started`, `discussions_answered`, `prs_merged` or `prs_merged_percentage`. And the following, which support the `repo` and `owner` filters: `prs_authored`, `prs_commented`, `prs_reviewed`, `issues_authored` or `issues_commented`). | string (comma-separated values) | `null` | | `commits_year` | Filters and counts only commits made in the specified year. | integer _(YYYY)_ | ` (one year to date)` | -> [!WARNING] -> Custom title should be URI-escaped, as specified in [Percent Encoding](https://en.wikipedia.org/wiki/Percent-encoding) (i.e: `Anurag's GitHub Stats` should become `Anurag%27s%20GitHub%20Stats`). You can use [urlencoder.org](https://www.urlencoder.org/) to help you do this automatically. +:::caution[Warning] +Custom title should be URI-escaped, as specified in [Percent Encoding](https://en.wikipedia.org/wiki/Percent-encoding) (i.e: `Anurag's GitHub Stats` should become `Anurag%27s%20GitHub%20Stats`). You can use [urlencoder.org](https://www.urlencoder.org/) to help you do this automatically. +::: -> [!NOTE] -> When hide\_rank=`true`, the minimum card width is 270 px + the title length and padding. +:::note +When hide\_rank=`true`, the minimum card width is 270 px + the title length and padding. +::: --- @@ -380,14 +377,17 @@ Use [show\_owner](#options-1) query option to include the gist's owner username The top languages card shows your most frequently used languages. -> [!WARNING] -> By default, the language card shows language results only from public repositories. To include languages used in private repositories, [allow GitHub-Stats-Extended to access your private contributions](fork.md#private-contributions-support) or [deploy your own instance](deploy.md). +:::caution[Warning] +By default, the language card shows language results only from public repositories. To include languages used in private repositories, [allow GitHub-Stats-Extended to access your private contributions](/frontend/docs/fork/#private-contributions-support) or [deploy your own instance](/frontend/docs/deploy/). +::: -> [!WARNING] -> This card shows language usage only inside your own non-forked repositories, not depending on who the author of the commits is. It does not include your contributions into another users/organizations repositories. Currently there are no way to get this data from GitHub API. If you want this behavior to be improved you can support [this feature request](https://github.com/orgs/community/discussions/18230) created by [@rickstaa](https://github.com/rickstaa) inside GitHub Community. +:::caution[Warning] +This card shows language usage only inside your own non-forked repositories, not depending on who the author of the commits is. It does not include your contributions into another users/organizations repositories. Currently there are no way to get this data from GitHub API. If you want this behavior to be improved you can support [this feature request](https://github.com/orgs/community/discussions/18230) created by [@rickstaa](https://github.com/rickstaa) inside GitHub Community. +::: -> [!WARNING] -> Currently this card shows data only about first 1000 repositories. This is because GitHub API limitations which cause downtimes of public instances (see [#1471](https://github.com/anuraghazra/github-readme-stats/issues/1471)). In future this behavior will be improved by releasing GitHub action or providing environment variables for user's own instances. +:::caution[Warning] +Currently this card shows data only about first 1000 repositories. This is because GitHub API limitations which cause downtimes of public instances (see [#1471](https://github.com/anuraghazra/github-readme-stats/issues/1471)). In future this behavior will be improved by releasing GitHub action or providing environment variables for user's own instances. +::: ### Usage @@ -421,8 +421,9 @@ You can customize the appearance and behavior of the top languages card using th | `count_weight` | Configures language stats algorithm (see [Language stats algorithm](#language-stats-algorithm)). | integer | `0` | | `stats_format` | Switches between two available formats for language's stats `percentages` and `bytes`. | enum | `percentages` | -> [!WARNING] -> Language names and custom title should be URI-escaped, as specified in [Percent Encoding](https://en.wikipedia.org/wiki/Percent-encoding) (i.e: `c++` should become `c%2B%2B`, `jupyter notebook` should become `jupyter%20notebook`, `Most Used Languages` should become `Most%20Used%20Languages`, etc.) You can use [urlencoder.org](https://www.urlencoder.org/) to help you do this automatically. +:::caution[Warning] +Language names and custom title should be URI-escaped, as specified in [Percent Encoding](https://en.wikipedia.org/wiki/Percent-encoding) (i.e: `c++` should become `c%2B%2B`, `jupyter notebook` should become `jupyter%20notebook`, `Most Used Languages` should become `Most%20Used%20Languages`, etc.) You can use [urlencoder.org](https://www.urlencoder.org/) to help you do this automatically. +::: ### Language stats algorithm @@ -551,11 +552,13 @@ You can use the `&stats_format=bytes` option to display the stats in bytes inste ## WakaTime Stats Card -> [!WARNING] -> Please be aware that we currently only show data from WakaTime profiles that are public. You therefore have to make sure that **BOTH** `Display code time publicly` and `Display languages, editors, os, categories publicly` are enabled. +:::caution[Warning] +Please be aware that we currently only show data from WakaTime profiles that are public. You therefore have to make sure that **BOTH** `Display code time publicly` and `Display languages, editors, os, categories publicly` are enabled. +::: -> [!WARNING] -> In case you just created a new WakaTime account, then it might take up to 24 hours until your stats will become visible on the WakaTime stats card. +:::caution[Warning] +In case you just created a new WakaTime account, then it might take up to 24 hours until your stats will become visible on the WakaTime stats card. +::: Change the `?username=` value to your [WakaTime](https://wakatime.com) username. @@ -581,8 +584,9 @@ You can customize the appearance and behavior of the WakaTime stats card using t | `display_format` | Sets the WakaTime stats display format. Choose `time` to display time-based stats or `percent` to show percentages. | enum | `time` | | `disable_animations` | Disables all animations in the card. | boolean | `false` | -> [!WARNING] -> Custom title should be URI-escaped, as specified in [Percent Encoding](https://en.wikipedia.org/wiki/Percent-encoding) (i.e: `WakaTime Stats` should become `WakaTime%20Stats`). You can use [urlencoder.org](https://www.urlencoder.org/) to help you do this automatically. +:::caution[Warning] +Custom title should be URI-escaped, as specified in [Percent Encoding](https://en.wikipedia.org/wiki/Percent-encoding) (i.e: `WakaTime Stats` should become `WakaTime%20Stats`). You can use [urlencoder.org](https://www.urlencoder.org/) to help you do this automatically. +::: ### Demo @@ -703,7 +707,7 @@ By default, GitHub does not lay out the cards side by side. To do that, you can ```
-:eyes: Show example +👀 Show example -:eyes: Show example +👀 Show example diff --git a/docs/deploy.md b/apps/frontend/src/content/docs/docs/deploy.md similarity index 93% rename from docs/deploy.md rename to apps/frontend/src/content/docs/docs/deploy.md index b3de00cbfa62c..e20de4a0f6aa2 100644 --- a/docs/deploy.md +++ b/apps/frontend/src/content/docs/docs/deploy.md @@ -1,4 +1,6 @@ -# Run It Yourself +--- +title: Run It Yourself +--- We cache generated cards for a few hours or days to avoid potential rate-limiting in the GitHub API or on Vercel. If you want to set your own cache duration or you want to include private contributions in your stats without granting our hosted version of GitHub-Stats-Extended access to your private contributions, you can run GitHub-Stats-Extended on your own. @@ -74,8 +76,9 @@ Selecting the right scopes for your token is important in case you want to displ #### Fine-grained token -> [!WARNING]\ -> This limits the scope of commits to public repositories only. +:::caution[Warning] +This limits the scope of commits to public repositories only. +::: - Go to [Account → Settings → Developer Settings → Personal access tokens → Fine-grained tokens](https://github.com/settings/personal-access-tokens). - Click on `Generate new token → Generate new token`. @@ -161,12 +164,12 @@ GitHub Stats Extended provides several environment variables that can be used to UPDATE_AFTER_HOURS - Sets the duration in hours after which the server proactively regenerates a previously requested card. Defaults to 11 hours. + Sets the duration in hours after which the server proactively regenerates a previously requested card. Defaults to 11 hours. Any int or float DELETE_AFTER_HOURS - Sets the duration in hours after which the server stops proactively regenerating a previously requested card if it hasn't been requested again in the meantime. Defaults to 8 days, i.e. 192 hours. + Sets the duration in hours after which the server stops proactively regenerating a previously requested card if it hasn't been requested again in the meantime. Defaults to 8 days, i.e. 192 hours. Any int or float @@ -194,8 +197,9 @@ GitHub Stats Extended provides several environment variables that can be used to See [the Vercel documentation](https://vercel.com/docs/concepts/projects/environment-variables) on adding these environment variables to your Vercel instance. -> [!WARNING] -> Please remember to redeploy your instance after making any changes to the environment variables so that the updates take effect. The changes will not be applied to the previous deployments. +:::caution[Warning] +Please remember to redeploy your instance after making any changes to the environment variables so that the updates take effect. The changes will not be applied to the previous deployments. +::: ### Keep your fork up to date diff --git a/docs/fork.md b/apps/frontend/src/content/docs/docs/fork.md similarity index 96% rename from docs/fork.md rename to apps/frontend/src/content/docs/docs/fork.md index 145a3f2e929c3..4e69b00890d0b 100644 --- a/docs/fork.md +++ b/apps/frontend/src/content/docs/docs/fork.md @@ -1,4 +1,6 @@ -# Fork Information +--- +title: Fork Information +--- This project is an actively maintained fork and extension of [github-readme-stats](https://github.com/anuraghazra/github-readme-stats). @@ -18,7 +20,7 @@ The frontend is based on [GitHub Trends](https://github.com/avgupta456/github-tr To include stars from repos which are not owned by you, but where you are a collaborator or organization member, add `&role=OWNER,ORGANIZATION_MEMBER,COLLABORATOR` to your stats card url. To include such repos in your language stats, you can also add the same parameter to your top languages card url. -See [here](advanced_documentation.md#filtering-by-repository-and-owner) for full feature documentation. +See [here](/frontend/docs/advanced_documentation/#filtering-by-repository-and-owner) for full feature documentation. The resolution of this most requested feature in github-readme-stats was [originally implemented](https://github.com/anuraghazra/github-readme-stats/issues/1#issuecomment-855681098) by [@developStorm](https://github.com/developStorm). @@ -46,7 +48,7 @@ GitHub-Stats-Extended adds the ability to show contribution stats for specific r Especially for regular contributors in open source projects it might make sense to display an overview of their own contributions to these projects on their GitHub profile. -See [here](advanced_documentation.md#filtering-by-repository-and-owner) for full feature documentation. +See [here](/frontend/docs/advanced_documentation/#filtering-by-repository-and-owner) for full feature documentation. --- diff --git a/apps/frontend/src/content/docs/docs/frontend-screenshot.png b/apps/frontend/src/content/docs/docs/frontend-screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..4a0b2dbae6efe00c39c1296813d6c0e4e4eae503 GIT binary patch literal 120532 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

*W>$ko<7KF;!pM>^R#PN`@lcVyz|4{+JL; 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 literal 0 HcmV?d00001 diff --git a/apps/frontend/src/content/docs/docs/index.md b/apps/frontend/src/content/docs/docs/index.md new file mode 100644 index 0000000000000..eb07f15064151 --- /dev/null +++ b/apps/frontend/src/content/docs/docs/index.md @@ -0,0 +1,99 @@ +--- +title: GitHub Stats Extended +--- + +Dynamically generate GitHub stats for your READMEs. + +GitHub-Stats-Extended is the [extended, actively maintained successor](/frontend/docs/fork/) of [github-readme-stats](https://github.com/anuraghazra/github-readme-stats). It generates stats cards about your GitHub contributions, your top languages and more, which you can [customize](/frontend/docs/advanced_documentation/) through a large set of parameters. + + + + Anurag's GitHub stats + + +## Quick Start + +Copy and paste this into your markdown, then change the `?username=` value to your GitHub username: + +```md +[![Anurag's GitHub stats](https://github-stats-extended.vercel.app/api?username=anuraghazra)](https://github.com/stats-organization/github-stats-extended) +``` + +As a more comfortable alternative, use the [card wizard](https://github-stats-extended.vercel.app/frontend) to configure your card visually, then copy the generated markdown into your [GitHub profile README](https://docs.github.com/en/account-and-profile/how-tos/profile-customization/managing-your-profile-readme#adding-a-profile-readme). + +## Card Types + +### Stats Card + + + + Anurag's GitHub stats + + +### Top Languages Card + + + + Top Langs + + +### WakaTime Card + + + + Alan's WakaTime stats + + +### Repo Card + + + + Readme Card + + +### Gist Card + + + + Gist Card + + +## Migration from github-readme-stats + +To migrate from [github-readme-stats](https://github.com/anuraghazra/github-readme-stats) you only need to change the domain from `github-readme-stats.vercel.app` to `github-stats-extended.vercel.app`: + +```diff +- https://github-readme-stats.vercel.app/api?username=octocat&theme=radical ++ https://github-stats-extended.vercel.app/api?username=octocat&theme=radical +``` + +GitHub-Stats-Extended aims to be fully compatible with github-readme-stats. For details see [Compatibility Notes](/frontend/docs/fork/#compatibility-notes). + +## Where to next + +- [Advanced Customization](/frontend/docs/advanced_documentation/) — every parameter the cards accept. +- [Available Themes](/frontend/docs/themes/) — the built-in themes, rendered as live samples. +- [Run It Yourself](/frontend/docs/deploy/) — GitHub Actions or a self-hosted Vercel deployment. +- [Fork Information](/frontend/docs/fork/) — what this project adds on top of github-readme-stats. + +## Acknowledgements + +This project is based on [github-readme-stats](https://github.com/anuraghazra/github-readme-stats). The card wizard is based on [GitHub Trends](https://github.com/avgupta456/github-trends). Big thanks to [@anuraghazra](https://github.com/anuraghazra), [@avgupta456](https://github.com/avgupta456), [@rickstaa](https://github.com/rickstaa), [@qwerty541](https://github.com/qwerty541) and everyone else who worked on these projects! ❤️ diff --git a/packages/core/src/themes/README.md b/apps/frontend/src/content/docs/docs/themes.md similarity index 65% rename from packages/core/src/themes/README.md rename to apps/frontend/src/content/docs/docs/themes.md index b58b7071e5baa..d589add623971 100644 --- a/packages/core/src/themes/README.md +++ b/apps/frontend/src/content/docs/docs/themes.md @@ -1,4 +1,6 @@ -## Available Themes +--- +title: Available Themes +--- @@ -14,68 +16,74 @@ Use `?theme=THEME_NAME` parameter like so: > These themes work with all five of our cards: Stats Card, Repo Card, Gist Card, Top Languages Card, and WakaTime Card. -| | | | -| :------------------------------------------------------------: | :------------------------------------------------------------: | :------------------------------------------------------------------: | -| `default` ![default][default] | `transparent` ![transparent][transparent] | `shadow_red` ![shadow_red][shadow_red] | -| `shadow_green` ![shadow_green][shadow_green] | `shadow_blue` ![shadow_blue][shadow_blue] | `dark` ![dark][dark] | -| `radical` ![radical][radical] | `merko` ![merko][merko] | `gruvbox` ![gruvbox][gruvbox] | -| `gruvbox_light` ![gruvbox_light][gruvbox_light] | `tokyonight` ![tokyonight][tokyonight] | `onedark` ![onedark][onedark] | -| `cobalt` ![cobalt][cobalt] | `synthwave` ![synthwave][synthwave] | `highcontrast` ![highcontrast][highcontrast] | -| `dracula` ![dracula][dracula] | `prussian` ![prussian][prussian] | `monokai` ![monokai][monokai] | -| `vue` ![vue][vue] | `vue-dark` ![vue-dark][vue-dark] | `shades-of-purple` ![shades-of-purple][shades-of-purple] | -| `nightowl` ![nightowl][nightowl] | `buefy` ![buefy][buefy] | `blue-green` ![blue-green][blue-green] | -| `algolia` ![algolia][algolia] | `great-gatsby` ![great-gatsby][great-gatsby] | `darcula` ![darcula][darcula] | -| `bear` ![bear][bear] | `solarized-dark` ![solarized-dark][solarized-dark] | `solarized-light` ![solarized-light][solarized-light] | -| `chartreuse-dark` ![chartreuse-dark][chartreuse-dark] | `nord` ![nord][nord] | `gotham` ![gotham][gotham] | -| `material-palenight` ![material-palenight][material-palenight] | `graywhite` ![graywhite][graywhite] | `vision-friendly-dark` ![vision-friendly-dark][vision-friendly-dark] | -| `ayu-mirage` ![ayu-mirage][ayu-mirage] | `midnight-purple` ![midnight-purple][midnight-purple] | `calm` ![calm][calm] | -| `flag-india` ![flag-india][flag-india] | `omni` ![omni][omni] | `react` ![react][react] | -| `jolly` ![jolly][jolly] | `maroongold` ![maroongold][maroongold] | `yeblu` ![yeblu][yeblu] | -| `blueberry` ![blueberry][blueberry] | `slateorange` ![slateorange][slateorange] | `kacho_ga` ![kacho_ga][kacho_ga] | -| `outrun` ![outrun][outrun] | `ocean_dark` ![ocean_dark][ocean_dark] | `city_lights` ![city_lights][city_lights] | -| `github_dark` ![github_dark][github_dark] | `github_dark_dimmed` ![github_dark_dimmed][github_dark_dimmed] | `discord_old_blurple` ![discord_old_blurple][discord_old_blurple] | -| `aura_dark` ![aura_dark][aura_dark] | `panda` ![panda][panda] | `noctis_minimus` ![noctis_minimus][noctis_minimus] | -| `cobalt2` ![cobalt2][cobalt2] | `swift` ![swift][swift] | `aura` ![aura][aura] | -| `apprentice` ![apprentice][apprentice] | `moltack` ![moltack][moltack] | `codeSTACKr` ![codeSTACKr][codeSTACKr] | -| `rose_pine` ![rose_pine][rose_pine] | `catppuccin_latte` ![catppuccin_latte][catppuccin_latte] | `catppuccin_mocha` ![catppuccin_mocha][catppuccin_mocha] | -| `date_night` ![date_night][date_night] | `one_dark_pro` ![one_dark_pro][one_dark_pro] | `rose` ![rose][rose] | -| `holi` ![holi][holi] | `neon` ![neon][neon] | `blue_navy` ![blue_navy][blue_navy] | -| `calm_pink` ![calm_pink][calm_pink] | `ambient_gradient` ![ambient_gradient][ambient_gradient] | | +| | | | +| :------------------------------------------------------------: | :------------------------------------------------------------------: | :------------------------------------------------------------: | +| `default` ![default][default] | `light_github` ![light_github][light_github] | `dark_github` ![dark_github][dark_github] | +| `transparent` ![transparent][transparent] | `shadow_red` ![shadow_red][shadow_red] | `shadow_green` ![shadow_green][shadow_green] | +| `shadow_blue` ![shadow_blue][shadow_blue] | `dark` ![dark][dark] | `radical` ![radical][radical] | +| `merko` ![merko][merko] | `gruvbox` ![gruvbox][gruvbox] | `gruvbox_light` ![gruvbox_light][gruvbox_light] | +| `tokyonight` ![tokyonight][tokyonight] | `onedark` ![onedark][onedark] | `cobalt` ![cobalt][cobalt] | +| `synthwave` ![synthwave][synthwave] | `highcontrast` ![highcontrast][highcontrast] | `dracula` ![dracula][dracula] | +| `prussian` ![prussian][prussian] | `monokai` ![monokai][monokai] | `vue` ![vue][vue] | +| `vue-dark` ![vue-dark][vue-dark] | `shades-of-purple` ![shades-of-purple][shades-of-purple] | `nightowl` ![nightowl][nightowl] | +| `buefy` ![buefy][buefy] | `blue-green` ![blue-green][blue-green] | `algolia` ![algolia][algolia] | +| `great-gatsby` ![great-gatsby][great-gatsby] | `darcula` ![darcula][darcula] | `bear` ![bear][bear] | +| `solarized-dark` ![solarized-dark][solarized-dark] | `solarized-light` ![solarized-light][solarized-light] | `chartreuse-dark` ![chartreuse-dark][chartreuse-dark] | +| `nord` ![nord][nord] | `gotham` ![gotham][gotham] | `material-palenight` ![material-palenight][material-palenight] | +| `graywhite` ![graywhite][graywhite] | `vision-friendly-dark` ![vision-friendly-dark][vision-friendly-dark] | `ayu-mirage` ![ayu-mirage][ayu-mirage] | +| `midnight-purple` ![midnight-purple][midnight-purple] | `calm` ![calm][calm] | `flag-india` ![flag-india][flag-india] | +| `omni` ![omni][omni] | `react` ![react][react] | `jolly` ![jolly][jolly] | +| `maroongold` ![maroongold][maroongold] | `yeblu` ![yeblu][yeblu] | `blueberry` ![blueberry][blueberry] | +| `slateorange` ![slateorange][slateorange] | `kacho_ga` ![kacho_ga][kacho_ga] | `outrun` ![outrun][outrun] | +| `ocean_dark` ![ocean_dark][ocean_dark] | `city_lights` ![city_lights][city_lights] | `github_dark` ![github_dark][github_dark] | +| `github_dark_dimmed` ![github_dark_dimmed][github_dark_dimmed] | `discord_old_blurple` ![discord_old_blurple][discord_old_blurple] | `aura_dark` ![aura_dark][aura_dark] | +| `panda` ![panda][panda] | `noctis_minimus` ![noctis_minimus][noctis_minimus] | `cobalt2` ![cobalt2][cobalt2] | +| `swift` ![swift][swift] | `aura` ![aura][aura] | `apprentice` ![apprentice][apprentice] | +| `moltack` ![moltack][moltack] | `codeSTACKr` ![codeSTACKr][codeSTACKr] | `rose_pine` ![rose_pine][rose_pine] | +| `catppuccin_latte` ![catppuccin_latte][catppuccin_latte] | `catppuccin_mocha` ![catppuccin_mocha][catppuccin_mocha] | `date_night` ![date_night][date_night] | +| `one_dark_pro` ![one_dark_pro][one_dark_pro] | `rose` ![rose][rose] | `holi` ![holi][holi] | +| `neon` ![neon][neon] | `blue_navy` ![blue_navy][blue_navy] | `calm_pink` ![calm_pink][calm_pink] | +| `ambient_gradient` ![ambient_gradient][ambient_gradient] | | | ## Repo Card > These themes work with all five of our cards: Stats Card, Repo Card, Gist Card, Top Languages Card, and WakaTime Card. -| | | | -| :-----------------------------------------------------------------: | :-----------------------------------------------------------------: | :-----------------------------------------------------------------------: | -| `default_repocard` ![default_repocard][default_repocard_repo] | `transparent` ![transparent][transparent_repo] | `shadow_red` ![shadow_red][shadow_red_repo] | -| `shadow_green` ![shadow_green][shadow_green_repo] | `shadow_blue` ![shadow_blue][shadow_blue_repo] | `dark` ![dark][dark_repo] | -| `radical` ![radical][radical_repo] | `merko` ![merko][merko_repo] | `gruvbox` ![gruvbox][gruvbox_repo] | -| `gruvbox_light` ![gruvbox_light][gruvbox_light_repo] | `tokyonight` ![tokyonight][tokyonight_repo] | `onedark` ![onedark][onedark_repo] | -| `cobalt` ![cobalt][cobalt_repo] | `synthwave` ![synthwave][synthwave_repo] | `highcontrast` ![highcontrast][highcontrast_repo] | -| `dracula` ![dracula][dracula_repo] | `prussian` ![prussian][prussian_repo] | `monokai` ![monokai][monokai_repo] | -| `vue` ![vue][vue_repo] | `vue-dark` ![vue-dark][vue-dark_repo] | `shades-of-purple` ![shades-of-purple][shades-of-purple_repo] | -| `nightowl` ![nightowl][nightowl_repo] | `buefy` ![buefy][buefy_repo] | `blue-green` ![blue-green][blue-green_repo] | -| `algolia` ![algolia][algolia_repo] | `great-gatsby` ![great-gatsby][great-gatsby_repo] | `darcula` ![darcula][darcula_repo] | -| `bear` ![bear][bear_repo] | `solarized-dark` ![solarized-dark][solarized-dark_repo] | `solarized-light` ![solarized-light][solarized-light_repo] | -| `chartreuse-dark` ![chartreuse-dark][chartreuse-dark_repo] | `nord` ![nord][nord_repo] | `gotham` ![gotham][gotham_repo] | -| `material-palenight` ![material-palenight][material-palenight_repo] | `graywhite` ![graywhite][graywhite_repo] | `vision-friendly-dark` ![vision-friendly-dark][vision-friendly-dark_repo] | -| `ayu-mirage` ![ayu-mirage][ayu-mirage_repo] | `midnight-purple` ![midnight-purple][midnight-purple_repo] | `calm` ![calm][calm_repo] | -| `flag-india` ![flag-india][flag-india_repo] | `omni` ![omni][omni_repo] | `react` ![react][react_repo] | -| `jolly` ![jolly][jolly_repo] | `maroongold` ![maroongold][maroongold_repo] | `yeblu` ![yeblu][yeblu_repo] | -| `blueberry` ![blueberry][blueberry_repo] | `slateorange` ![slateorange][slateorange_repo] | `kacho_ga` ![kacho_ga][kacho_ga_repo] | -| `outrun` ![outrun][outrun_repo] | `ocean_dark` ![ocean_dark][ocean_dark_repo] | `city_lights` ![city_lights][city_lights_repo] | -| `github_dark` ![github_dark][github_dark_repo] | `github_dark_dimmed` ![github_dark_dimmed][github_dark_dimmed_repo] | `discord_old_blurple` ![discord_old_blurple][discord_old_blurple_repo] | -| `aura_dark` ![aura_dark][aura_dark_repo] | `panda` ![panda][panda_repo] | `noctis_minimus` ![noctis_minimus][noctis_minimus_repo] | -| `cobalt2` ![cobalt2][cobalt2_repo] | `swift` ![swift][swift_repo] | `aura` ![aura][aura_repo] | -| `apprentice` ![apprentice][apprentice_repo] | `moltack` ![moltack][moltack_repo] | `codeSTACKr` ![codeSTACKr][codeSTACKr_repo] | -| `rose_pine` ![rose_pine][rose_pine_repo] | `catppuccin_latte` ![catppuccin_latte][catppuccin_latte_repo] | `catppuccin_mocha` ![catppuccin_mocha][catppuccin_mocha_repo] | -| `date_night` ![date_night][date_night_repo] | `one_dark_pro` ![one_dark_pro][one_dark_pro_repo] | `rose` ![rose][rose_repo] | -| `holi` ![holi][holi_repo] | `neon` ![neon][neon_repo] | `blue_navy` ![blue_navy][blue_navy_repo] | -| `calm_pink` ![calm_pink][calm_pink_repo] | `ambient_gradient` ![ambient_gradient][ambient_gradient_repo] | | +| | | | +| :-----------------------------------------------------------------: | :--------------------------------------------------------------------------: | :-----------------------------------------------------------------------: | +| `default_repocard` ![default_repocard][default_repocard_repo] | `light_github_repocard` ![light_github_repocard][light_github_repocard_repo] | `dark_github_repocard` ![dark_github_repocard][dark_github_repocard_repo] | +| `transparent` ![transparent][transparent_repo] | `shadow_red` ![shadow_red][shadow_red_repo] | `shadow_green` ![shadow_green][shadow_green_repo] | +| `shadow_blue` ![shadow_blue][shadow_blue_repo] | `dark` ![dark][dark_repo] | `radical` ![radical][radical_repo] | +| `merko` ![merko][merko_repo] | `gruvbox` ![gruvbox][gruvbox_repo] | `gruvbox_light` ![gruvbox_light][gruvbox_light_repo] | +| `tokyonight` ![tokyonight][tokyonight_repo] | `onedark` ![onedark][onedark_repo] | `cobalt` ![cobalt][cobalt_repo] | +| `synthwave` ![synthwave][synthwave_repo] | `highcontrast` ![highcontrast][highcontrast_repo] | `dracula` ![dracula][dracula_repo] | +| `prussian` ![prussian][prussian_repo] | `monokai` ![monokai][monokai_repo] | `vue` ![vue][vue_repo] | +| `vue-dark` ![vue-dark][vue-dark_repo] | `shades-of-purple` ![shades-of-purple][shades-of-purple_repo] | `nightowl` ![nightowl][nightowl_repo] | +| `buefy` ![buefy][buefy_repo] | `blue-green` ![blue-green][blue-green_repo] | `algolia` ![algolia][algolia_repo] | +| `great-gatsby` ![great-gatsby][great-gatsby_repo] | `darcula` ![darcula][darcula_repo] | `bear` ![bear][bear_repo] | +| `solarized-dark` ![solarized-dark][solarized-dark_repo] | `solarized-light` ![solarized-light][solarized-light_repo] | `chartreuse-dark` ![chartreuse-dark][chartreuse-dark_repo] | +| `nord` ![nord][nord_repo] | `gotham` ![gotham][gotham_repo] | `material-palenight` ![material-palenight][material-palenight_repo] | +| `graywhite` ![graywhite][graywhite_repo] | `vision-friendly-dark` ![vision-friendly-dark][vision-friendly-dark_repo] | `ayu-mirage` ![ayu-mirage][ayu-mirage_repo] | +| `midnight-purple` ![midnight-purple][midnight-purple_repo] | `calm` ![calm][calm_repo] | `flag-india` ![flag-india][flag-india_repo] | +| `omni` ![omni][omni_repo] | `react` ![react][react_repo] | `jolly` ![jolly][jolly_repo] | +| `maroongold` ![maroongold][maroongold_repo] | `yeblu` ![yeblu][yeblu_repo] | `blueberry` ![blueberry][blueberry_repo] | +| `slateorange` ![slateorange][slateorange_repo] | `kacho_ga` ![kacho_ga][kacho_ga_repo] | `outrun` ![outrun][outrun_repo] | +| `ocean_dark` ![ocean_dark][ocean_dark_repo] | `city_lights` ![city_lights][city_lights_repo] | `github_dark` ![github_dark][github_dark_repo] | +| `github_dark_dimmed` ![github_dark_dimmed][github_dark_dimmed_repo] | `discord_old_blurple` ![discord_old_blurple][discord_old_blurple_repo] | `aura_dark` ![aura_dark][aura_dark_repo] | +| `panda` ![panda][panda_repo] | `noctis_minimus` ![noctis_minimus][noctis_minimus_repo] | `cobalt2` ![cobalt2][cobalt2_repo] | +| `swift` ![swift][swift_repo] | `aura` ![aura][aura_repo] | `apprentice` ![apprentice][apprentice_repo] | +| `moltack` ![moltack][moltack_repo] | `codeSTACKr` ![codeSTACKr][codeSTACKr_repo] | `rose_pine` ![rose_pine][rose_pine_repo] | +| `catppuccin_latte` ![catppuccin_latte][catppuccin_latte_repo] | `catppuccin_mocha` ![catppuccin_mocha][catppuccin_mocha_repo] | `date_night` ![date_night][date_night_repo] | +| `one_dark_pro` ![one_dark_pro][one_dark_pro_repo] | `rose` ![rose][rose_repo] | `holi` ![holi][holi_repo] | +| `neon` ![neon][neon_repo] | `blue_navy` ![blue_navy][blue_navy_repo] | `calm_pink` ![calm_pink][calm_pink_repo] | +| `ambient_gradient` ![ambient_gradient][ambient_gradient_repo] | | | [default]: https://github-stats-extended.vercel.app/api?username=anuraghazra&show_icons=true&hide=contribs,prs&cache_seconds=86400&theme=default [default_repocard]: https://github-stats-extended.vercel.app/api?username=anuraghazra&show_icons=true&hide=contribs,prs&cache_seconds=86400&theme=default_repocard +[light_github]: https://github-stats-extended.vercel.app/api?username=anuraghazra&show_icons=true&hide=contribs,prs&cache_seconds=86400&theme=light_github +[dark_github]: https://github-stats-extended.vercel.app/api?username=anuraghazra&show_icons=true&hide=contribs,prs&cache_seconds=86400&theme=dark_github +[light_github_repocard]: https://github-stats-extended.vercel.app/api?username=anuraghazra&show_icons=true&hide=contribs,prs&cache_seconds=86400&theme=light_github_repocard +[dark_github_repocard]: https://github-stats-extended.vercel.app/api?username=anuraghazra&show_icons=true&hide=contribs,prs&cache_seconds=86400&theme=dark_github_repocard [transparent]: https://github-stats-extended.vercel.app/api?username=anuraghazra&show_icons=true&hide=contribs,prs&cache_seconds=86400&theme=transparent [shadow_red]: https://github-stats-extended.vercel.app/api?username=anuraghazra&show_icons=true&hide=contribs,prs&cache_seconds=86400&theme=shadow_red [shadow_green]: https://github-stats-extended.vercel.app/api?username=anuraghazra&show_icons=true&hide=contribs,prs&cache_seconds=86400&theme=shadow_green @@ -151,6 +159,10 @@ Use `?theme=THEME_NAME` parameter like so: [ambient_gradient]: https://github-stats-extended.vercel.app/api?username=anuraghazra&show_icons=true&hide=contribs,prs&cache_seconds=86400&theme=ambient_gradient [default_repo]: https://github-stats-extended.vercel.app/api/pin/?username=anuraghazra&repo=github-readme-stats&cache_seconds=86400&theme=default [default_repocard_repo]: https://github-stats-extended.vercel.app/api/pin/?username=anuraghazra&repo=github-readme-stats&cache_seconds=86400&theme=default_repocard +[light_github_repo]: https://github-stats-extended.vercel.app/api/pin/?username=anuraghazra&repo=github-readme-stats&cache_seconds=86400&theme=light_github +[dark_github_repo]: https://github-stats-extended.vercel.app/api/pin/?username=anuraghazra&repo=github-readme-stats&cache_seconds=86400&theme=dark_github +[light_github_repocard_repo]: https://github-stats-extended.vercel.app/api/pin/?username=anuraghazra&repo=github-readme-stats&cache_seconds=86400&theme=light_github_repocard +[dark_github_repocard_repo]: https://github-stats-extended.vercel.app/api/pin/?username=anuraghazra&repo=github-readme-stats&cache_seconds=86400&theme=dark_github_repocard [transparent_repo]: https://github-stats-extended.vercel.app/api/pin/?username=anuraghazra&repo=github-readme-stats&cache_seconds=86400&theme=transparent [shadow_red_repo]: https://github-stats-extended.vercel.app/api/pin/?username=anuraghazra&repo=github-readme-stats&cache_seconds=86400&theme=shadow_red [shadow_green_repo]: https://github-stats-extended.vercel.app/api/pin/?username=anuraghazra&repo=github-readme-stats&cache_seconds=86400&theme=shadow_green diff --git a/apps/frontend/src/index.css b/apps/frontend/src/index.css index 54b223dd31bd0..725c44119a73b 100644 --- a/apps/frontend/src/index.css +++ b/apps/frontend/src/index.css @@ -1,3 +1,4 @@ +@import "./styles/layer-order.css"; @import "tailwindcss"; @plugin "daisyui" { themes: @@ -28,82 +29,85 @@ Override background color in dark mode with GitHub's dark mode background color. --transition-surface: 300ms; } -body { - margin: 0; - font-family: "Segoe UI", Ubuntu, Sans-Serif; +/* + * Everything here is nested so it reaches only the wizard's own nodes: + * the page background, font and reset belong to the site, which shares this page. + */ +.wizard { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; background-color: var(--color-base-100); color: var(--color-base-content); -} -button { - cursor: pointer; -} + button { + cursor: pointer; + } -/* Smoothly animate color changes when switching themes / on hover. */ -*, -*::before, -*::after { - transition-property: background-color, border-color, color, fill, stroke; - /* - * An ease-out curve: animations start at full speed so quick interactions - * like button hovers feel responsive instead of laggy. - */ - transition-timing-function: cubic-bezier(0, 0, 0.2, 1); - transition-duration: var(--transition-fast); -} + /* Smoothly animate color changes when switching themes / on hover. */ + *, + *::before, + *::after { + transition-property: background-color, border-color, color, fill, stroke; + /* + * An ease-out curve: animations start at full speed so quick interactions + * like button hovers feel responsive instead of laggy. + */ + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); + transition-duration: var(--transition-fast); + } -/* - * Large surface backgrounds fade more slowly so a theme switch reads as a - * deliberate fade rather than an instant flip. Durations reference the custom - * properties above so the reduced-motion override below zeroes them out - * regardless of selector specificity. - */ -body, -.bg-base-100, -.bg-base-200, -.bg-base-300 { - transition-duration: var(--transition-surface); -} + /* + * Large surface backgrounds fade more slowly so a theme switch reads as a + * deliberate fade rather than an instant flip. Durations reference the custom + * properties above so the reduced-motion override below zeroes them out + * regardless of selector specificity. + */ + &, + & .bg-base-100, + & .bg-base-200, + & .bg-base-300 { + transition-duration: var(--transition-surface); + } -/* - * DaisyUI's `.btn` ships its own 200ms transition which felt slow. The global - * rule above is unlayered, so it would otherwise clobber the button's property - * list and timing function. Restore both here (so fill, text and border animate - * together on outline buttons) and only speed up the duration. - */ -.btn { - transition-property: color, background-color, border-color, box-shadow; - transition-timing-function: cubic-bezier(0, 0, 0.2, 1); - transition-duration: var(--transition-fast); -} + /* + * DaisyUI's `.btn` ships its own 200ms transition which felt slow. The rule + * above is unlayered, so it would otherwise clobber the button's property list + * and timing function. Restore both here (so fill, text and border animate + * together on outline buttons) and only speed up the duration. + */ + .btn { + transition-property: color, background-color, border-color, box-shadow; + transition-timing-function: cubic-bezier(0, 0, 0.2, 1); + transition-duration: var(--transition-fast); + } -/* - * DaisyUI's default hover is very subtle; make it noticeable. Primary always darkens, - * while soft lightens in dark mode and darkens in light mode, so the change shows. - */ -.btn-primary:hover { - background-color: color-mix(in oklab, var(--color-primary), #000 30%); -} -.btn-soft { - background-color: color-mix(in oklab, var(--color-base-100), #888 35%); -} -.btn-soft:hover { - background-color: color-mix(in oklab, var(--color-base-100), #888 60%); -} -.btn-error { - color: var(--color-neutral-content); -} -.btn-error:hover { - color: var(--color-neutral-content); - background-color: color-mix(in oklab, var(--color-error), #f00 40%); -} + /* + * DaisyUI's default hover is very subtle; make it noticeable. Primary always darkens, + * while soft lightens in dark mode and darkens in light mode, so the change shows. + */ + .btn-primary:hover { + background-color: color-mix(in oklab, var(--color-primary), #000 30%); + } + .btn-soft { + background-color: color-mix(in oklab, var(--color-base-100), #888 35%); + } + .btn-soft:hover { + background-color: color-mix(in oklab, var(--color-base-100), #888 60%); + } + .btn-error { + color: var(--color-neutral-content); + } + .btn-error:hover { + color: var(--color-neutral-content); + background-color: color-mix(in oklab, var(--color-error), #f00 40%); + } -input:focus, -select:focus { - outline: 0; - border: var(--color-primary) solid 1px; + /* Unscoped, this strips the focus ring from the site's search box. */ + input:focus, + select:focus { + outline: 0; + border: var(--color-primary) solid 1px; + } } /* Respect users who prefer reduced motion. */ diff --git a/apps/frontend/src/index.tsx b/apps/frontend/src/index.tsx deleted file mode 100644 index f2a0f657e5cd4..0000000000000 --- a/apps/frontend/src/index.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import "./axios-override"; - -import { StrictMode } from "react"; -import { createRoot } from "react-dom/client"; -import { Provider } from "react-redux"; - -import { AppTrends } from "./pages/App/AppTrends"; -import { store } from "./redux/store"; - -import "./index.css"; - -const root = createRoot(document.getElementById("root") as HTMLElement); - -root.render( - - - - - , -); diff --git a/apps/frontend/src/modules.d.ts b/apps/frontend/src/modules.d.ts index 8e5a1b4b5d2fe..bb96604b64938 100644 --- a/apps/frontend/src/modules.d.ts +++ b/apps/frontend/src/modules.d.ts @@ -1,10 +1,3 @@ -declare module "*.css"; - -declare module "*.png" { - const img: string; - export default img; -} - // This package doesn't have a @types counter part declare module "save-svg-as-png" { const saveSvgAsPng: ( diff --git a/apps/frontend/src/pages/App/Header.tsx b/apps/frontend/src/pages/App/Header.tsx deleted file mode 100644 index 7701ef1c1109d..0000000000000 --- a/apps/frontend/src/pages/App/Header.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import type { JSX } from "react"; -import { FaGithub as GithubIcon } from "react-icons/fa"; - -import appIcon from "../../assets/appLogo64.png"; -import { ThemePicker } from "../../components/Generic/ThemePicker"; -import { ProgressBar } from "../../components/Home/Progress"; -import { STAGE_LABELS } from "../../models/Stage"; -import type { StageIndex } from "../../models/Stage"; - -interface HeaderProps { - currStageIndex: StageIndex; - onStageIndexChange: (stageIndex: StageIndex) => void; -} - -const items = STAGE_LABELS.map((it) => it.shortTitle); - -export function Header({ - currStageIndex, - onStageIndexChange, -}: HeaderProps): JSX.Element { - return ( - <> -
-
- {/* Logo */} - - logo - GitHub Stats Extended - - {/* Star on GitHub + theme toggle */} - -
-
- { - onStageIndexChange(itemIndex as StageIndex); - }} - /> - - ); -} diff --git a/apps/frontend/src/pages/index.astro b/apps/frontend/src/pages/index.astro new file mode 100644 index 0000000000000..042d8c4ba0d2e --- /dev/null +++ b/apps/frontend/src/pages/index.astro @@ -0,0 +1,39 @@ +--- +import StarlightPage from "@astrojs/starlight/components/StarlightPage.astro"; + +import { Wizard } from "../wizard/Wizard"; + +// Tailwind and daisyUI, loaded only here: the docs pages are styled by Starlight. +import "../index.css"; +--- + +{/* `splash` drops the sidebar and table of contents, leaving the wizard the full width. */} + + {/* `not-content` opts out of the prose spacing Starlight applies to markdown. */} +
+
+ +{/* Only this page loads these rules, so the docs keep Starlight's reading column. */} + diff --git a/apps/frontend/src/redux/selectors/themeSelectors.ts b/apps/frontend/src/redux/selectors/themeSelectors.ts deleted file mode 100644 index ec18dd59e36e1..0000000000000 --- a/apps/frontend/src/redux/selectors/themeSelectors.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { useCallback } from "react"; -import { useDispatch, useSelector } from "react-redux"; - -import { isDarkTheme, setTheme as setThemeAction } from "../slices/theme"; -import type { StoreState } from "../store"; - -/** - * Reads the active DaisyUI theme from the Redux store and exposes a setter - * that persists the choice and updates the `data-theme` attribute. - */ -export function useTheme(): { - theme: string; - isDark: boolean; - setTheme: (theme: string) => void; -} { - const dispatch = useDispatch(); - const theme = useSelector((state: StoreState) => state.theme.theme); - - const setTheme = useCallback( - (next: string) => { - dispatch(setThemeAction(next)); - }, - [dispatch], - ); - - return { theme, isDark: isDarkTheme(theme), setTheme }; -} diff --git a/apps/frontend/src/redux/slices/theme.ts b/apps/frontend/src/redux/slices/theme.ts deleted file mode 100644 index 4f78d7a93ee1e..0000000000000 --- a/apps/frontend/src/redux/slices/theme.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { createSlice } from "@reduxjs/toolkit"; -import type { PayloadAction } from "@reduxjs/toolkit"; - -interface ThemeOption { - /** DaisyUI theme name, used as the `data-theme` value. */ - name: string; - label: string; - /** Whether the theme has a dark background (drives toast styling, etc.). */ - isDark: boolean; -} - -/** - * Themes exposed in the theme picker. - * Keep in sync with the `themes:` list in `index.css`. - */ -export const THEMES: ReadonlyArray = [ - { name: "light", label: "Light", isDark: false }, - { name: "dark", label: "Dark", isDark: true }, -]; - -const DEFAULT_LIGHT_THEME = "light"; -const DEFAULT_DARK_THEME = "dark"; - -function isValidTheme(name: string | null): name is string { - return !!name && THEMES.some((theme) => theme.name === name); -} - -export function isDarkTheme(name: string): boolean { - return THEMES.find((theme) => theme.name === name)?.isDark ?? false; -} - -/** - * @public - * Exported so the inferred store type can name it (see the note in `user.ts`). - */ -export interface ThemeState { - theme: string; -} - -function getInitialTheme(): string { - const stored = localStorage.getItem("theme"); - if (isValidTheme(stored)) { - return stored; - } - return window.matchMedia("(prefers-color-scheme: dark)").matches - ? DEFAULT_DARK_THEME - : DEFAULT_LIGHT_THEME; -} - -const initialState: ThemeState = { - theme: getInitialTheme(), -}; - -const themeSlice = createSlice({ - name: "theme", - initialState, - reducers: { - setTheme: (state, action: PayloadAction) => { - const theme = action.payload; - localStorage.setItem("theme", theme); - document.documentElement.setAttribute("data-theme", theme); - state.theme = theme; - }, - }, -}); - -export const { setTheme } = themeSlice.actions; - -export default themeSlice.reducer; diff --git a/apps/frontend/src/redux/store.ts b/apps/frontend/src/redux/store.ts index 97fc03a06cd7b..73870b8e2c9d3 100644 --- a/apps/frontend/src/redux/store.ts +++ b/apps/frontend/src/redux/store.ts @@ -3,13 +3,11 @@ import { configureStore } from "@reduxjs/toolkit"; import { USE_LOGGER } from "../constants"; import { loggerMiddleware } from "./logger"; -import theme from "./slices/theme"; import user from "./slices/user"; const store = configureStore({ reducer: { user, - theme, }, middleware: (getDefaultMiddleware) => { const middleware = getDefaultMiddleware(); diff --git a/apps/frontend/src/shared/LinkExternal.tsx b/apps/frontend/src/shared/LinkExternal.tsx new file mode 100644 index 0000000000000..75d250091a41c --- /dev/null +++ b/apps/frontend/src/shared/LinkExternal.tsx @@ -0,0 +1,37 @@ +import { clsx } from "clsx"; +import type { JSX, ReactNode } from "react"; +import { FaExternalLinkAlt as ExternalIcon } from "react-icons/fa"; + +interface LinkExternalProps { + href: string; + children: ReactNode; + className?: string; + /** + * Set to `false` for links that already carry their own mark, such as the + * branded GitHub button, where a second icon would only add noise. + */ + showIcon?: boolean; +} + +/** + * A link that opens in a new tab, with the `rel` that `target="_blank"` + * requires, and a trailing icon so the new tab is announced before the click. + */ +export function LinkExternal({ + href, + children, + className, + showIcon = true, +}: LinkExternalProps): JSX.Element { + return ( + + {children} + {showIcon && } + + ); +} diff --git a/apps/frontend/src/styles/layer-order.css b/apps/frontend/src/styles/layer-order.css new file mode 100644 index 0000000000000..8b1fbc9d67e91 --- /dev/null +++ b/apps/frontend/src/styles/layer-order.css @@ -0,0 +1,8 @@ +/* + * Tailwind's utilities must outrank Starlight's reset, whose `margin: 0` otherwise beats every margin utility in the wizard. + * Preflight stays ahead so the chrome keeps its styling. + * Imported by both entry sheets: + * only the first `@layer` wins, and dev loads them in the opposite order to a build. + */ +@layer theme, base, components, starlight.base, starlight.reset, starlight.core, + starlight.content, starlight.components, starlight.utils, utilities; diff --git a/apps/frontend/src/styles/starlight-theme.css b/apps/frontend/src/styles/starlight-theme.css new file mode 100644 index 0000000000000..7e3c2ed7d7f62 --- /dev/null +++ b/apps/frontend/src/styles/starlight-theme.css @@ -0,0 +1,20 @@ +@import "./layer-order.css"; + +/* + * Gives the docs the wizard's accent and GitHub's dark background, so the two + * halves read as one site. + */ +:root { + --app-primary: #2f80ed; + + --sl-font: "Segoe UI", Ubuntu, Sans-Serif; + --sl-color-accent: var(--app-primary); + --sl-color-accent-low: color-mix(in oklab, var(--app-primary), #000 45%); + --sl-color-accent-high: color-mix(in oklab, var(--app-primary), #fff 45%); + --sl-color-text-accent: var(--app-primary); +} + +:root[data-theme="dark"] { + --sl-color-bg: #0d1117; + --sl-color-bg-sidebar: #0d1117; +} diff --git a/apps/frontend/src/pages/Home/Home.tsx b/apps/frontend/src/wizard/Home/Home.tsx similarity index 93% rename from apps/frontend/src/pages/Home/Home.tsx rename to apps/frontend/src/wizard/Home/Home.tsx index 52788559be50a..14a84c7d009bd 100644 --- a/apps/frontend/src/pages/Home/Home.tsx +++ b/apps/frontend/src/wizard/Home/Home.tsx @@ -5,24 +5,25 @@ import { useDispatch } from "react-redux"; import { BounceLoader } from "react-spinners"; import { v4 as uuidv4 } from "uuid"; -import { authenticate } from "../../api/user"; -import { DEFAULT_OPTION as LANGUAGES_DEFAULT_LAYOUT } from "../../components/Home/LanguagesLayoutSection"; -import { DEFAULT_OPTION as WAKATIME_DEFAULT_LAYOUT } from "../../components/Home/WakatimeLayoutSection"; import { DEMO_USER } from "../../constants"; -import { - CATEGORY_BY_CARD_TYPE, - CardCategory, - CardType, -} from "../../models/CardType"; -import { STAGE_LABELS } from "../../models/Stage"; -import type { StageIndex } from "../../models/Stage"; -import { useTheme } from "../../redux/selectors/themeSelectors"; import { useIsAuthenticated, usePrivateAccess, useUserId, } from "../../redux/selectors/userSelectors"; import { login } from "../../redux/slices/user"; +import { LinkExternal } from "../../shared/LinkExternal"; +import { authenticate } from "../api/user"; +import { DEFAULT_OPTION as LANGUAGES_DEFAULT_LAYOUT } from "../components/Home/LanguagesLayoutSection"; +import { DEFAULT_OPTION as WAKATIME_DEFAULT_LAYOUT } from "../components/Home/WakatimeLayoutSection"; +import { + CATEGORY_BY_CARD_TYPE, + CardCategory, + CardType, +} from "../models/CardType"; +import { STAGE_LABELS } from "../models/Stage"; +import type { StageIndex } from "../models/Stage"; +import { useIsDarkTheme } from "../useIsDarkTheme"; import { buildCardUrl } from "./buildCardUrl"; import { getDefaultCardOptions } from "./cardOptions"; @@ -77,7 +78,7 @@ export function HomeScreen({ stage, setStage }: HomeScreenProps): JSX.Element { setCardOptions((prev) => ({ ...prev, selectedUserId: userId })); } - const { isDark } = useTheme(); + const isDark = useIsDarkTheme(); const cardCategory = CATEGORY_BY_CARD_TYPE[selectedCard]; const [theme, setTheme] = useState(() => getDefaultTheme(isDark, cardCategory), @@ -204,13 +205,12 @@ export function HomeScreen({ stage, setStage }: HomeScreenProps): JSX.Element {

You are logged in as{" "} - {userId} - + .

diff --git a/apps/frontend/src/pages/Home/buildCardUrl.test.ts b/apps/frontend/src/wizard/Home/buildCardUrl.test.ts similarity index 90% rename from apps/frontend/src/pages/Home/buildCardUrl.test.ts rename to apps/frontend/src/wizard/Home/buildCardUrl.test.ts index 9e3b3820868b6..bb3b1c4b5d82c 100644 --- a/apps/frontend/src/pages/Home/buildCardUrl.test.ts +++ b/apps/frontend/src/wizard/Home/buildCardUrl.test.ts @@ -1,9 +1,9 @@ import { describe, expect, it } from "vitest"; -import { DEFAULT_OPTION as LANGUAGES_DEFAULT_LAYOUT } from "../../components/Home/LanguagesLayoutSection"; -import { DEFAULT_OPTION as STATS_DEFAULT_RANK } from "../../components/Home/StatsRankSection"; -import { DEFAULT_OPTION as WAKATIME_DEFAULT_LAYOUT } from "../../components/Home/WakatimeLayoutSection"; -import { CardType } from "../../models/CardType"; +import { DEFAULT_OPTION as LANGUAGES_DEFAULT_LAYOUT } from "../components/Home/LanguagesLayoutSection"; +import { DEFAULT_OPTION as STATS_DEFAULT_RANK } from "../components/Home/StatsRankSection"; +import { DEFAULT_OPTION as WAKATIME_DEFAULT_LAYOUT } from "../components/Home/WakatimeLayoutSection"; +import { CardType } from "../models/CardType"; import { buildCardUrl } from "./buildCardUrl"; import type { CardOptions } from "./cardOptions"; diff --git a/apps/frontend/src/pages/Home/buildCardUrl.ts b/apps/frontend/src/wizard/Home/buildCardUrl.ts similarity index 90% rename from apps/frontend/src/pages/Home/buildCardUrl.ts rename to apps/frontend/src/wizard/Home/buildCardUrl.ts index 28081a0c2564a..ec4c8f5e3220f 100644 --- a/apps/frontend/src/pages/Home/buildCardUrl.ts +++ b/apps/frontend/src/wizard/Home/buildCardUrl.ts @@ -1,9 +1,9 @@ -import { DEFAULT_OPTION as LANGUAGES_DEFAULT_LAYOUT } from "../../components/Home/LanguagesLayoutSection"; -import { DEFAULT_OPTION as STATS_DEFAULT_RANK } from "../../components/Home/StatsRankSection"; -import { DEFAULT_OPTION as WAKATIME_DEFAULT_LAYOUT } from "../../components/Home/WakatimeLayoutSection"; -import { CardType } from "../../models/CardType"; -import { cardUrl } from "../../models/CardUrl"; -import type { CardUrlBuilder } from "../../models/CardUrl"; +import { DEFAULT_OPTION as LANGUAGES_DEFAULT_LAYOUT } from "../components/Home/LanguagesLayoutSection"; +import { DEFAULT_OPTION as STATS_DEFAULT_RANK } from "../components/Home/StatsRankSection"; +import { DEFAULT_OPTION as WAKATIME_DEFAULT_LAYOUT } from "../components/Home/WakatimeLayoutSection"; +import { CardType } from "../models/CardType"; +import { cardUrl } from "../models/CardUrl"; +import type { CardUrlBuilder } from "../models/CardUrl"; import type { CardOptions } from "./cardOptions"; diff --git a/apps/frontend/src/pages/Home/cardOptions.ts b/apps/frontend/src/wizard/Home/cardOptions.ts similarity index 78% rename from apps/frontend/src/pages/Home/cardOptions.ts rename to apps/frontend/src/wizard/Home/cardOptions.ts index 7e9a3650a60e1..461e997502169 100644 --- a/apps/frontend/src/pages/Home/cardOptions.ts +++ b/apps/frontend/src/wizard/Home/cardOptions.ts @@ -1,8 +1,8 @@ -import type { SelectOption } from "../../components/Generic/Select"; -import { DEFAULT_OPTION as LANGUAGES_DEFAULT_LAYOUT } from "../../components/Home/LanguagesLayoutSection"; -import { DEFAULT_OPTION as STATS_DEFAULT_RANK } from "../../components/Home/StatsRankSection"; -import { DEFAULT_OPTION as WAKATIME_DEFAULT_LAYOUT } from "../../components/Home/WakatimeLayoutSection"; import { DEMO_GIST, DEMO_REPO, DEMO_WAKATIME_USER } from "../../constants"; +import type { SelectOption } from "../components/Generic/Select"; +import { DEFAULT_OPTION as LANGUAGES_DEFAULT_LAYOUT } from "../components/Home/LanguagesLayoutSection"; +import { DEFAULT_OPTION as STATS_DEFAULT_RANK } from "../components/Home/StatsRankSection"; +import { DEFAULT_OPTION as WAKATIME_DEFAULT_LAYOUT } from "../components/Home/WakatimeLayoutSection"; /** * All user-tunable card parameters collected during the customize stage. diff --git a/apps/frontend/src/pages/Home/stages/Customize.tsx b/apps/frontend/src/wizard/Home/stages/Customize.tsx similarity index 92% rename from apps/frontend/src/pages/Home/stages/Customize.tsx rename to apps/frontend/src/wizard/Home/stages/Customize.tsx index 3336aaeaf7ef3..4bca80b9f8ac3 100644 --- a/apps/frontend/src/pages/Home/stages/Customize.tsx +++ b/apps/frontend/src/wizard/Home/stages/Customize.tsx @@ -1,26 +1,28 @@ import type { JSX } from "react"; -import { CardImage } from "../../../components/Card/CardImage"; -import { CheckboxSection } from "../../../components/Home/CheckboxSection"; -import { LanguagesLayoutSection } from "../../../components/Home/LanguagesLayoutSection"; -import { NumericSection } from "../../../components/Home/NumericSection"; -import { StatsRankSection } from "../../../components/Home/StatsRankSection"; -import { TextSection } from "../../../components/Home/TextSection"; -import { WakatimeLayoutSection } from "../../../components/Home/WakatimeLayoutSection"; import { DEMO_GIST, DEMO_REPO, DEMO_USER, DEMO_WAKATIME_USER, } from "../../../constants"; +import { useIsAuthenticated } from "../../../redux/selectors/userSelectors"; +import { LinkExternal } from "../../../shared/LinkExternal"; +import { CardImage } from "../../components/Card/CardImage"; +import { CheckboxSection } from "../../components/Home/CheckboxSection"; +import { LanguagesLayoutSection } from "../../components/Home/LanguagesLayoutSection"; +import { NumericSection } from "../../components/Home/NumericSection"; +import { StatsRankSection } from "../../components/Home/StatsRankSection"; +import { TextSection } from "../../components/Home/TextSection"; +import { WakatimeLayoutSection } from "../../components/Home/WakatimeLayoutSection"; +import { docsUrl } from "../../docsUrl"; import { CATEGORY_BY_CARD_TYPE, CardCategory, CardType, -} from "../../../models/CardType"; -import type { CardUrlBuilder } from "../../../models/CardUrl"; -import type { StageIndex } from "../../../models/Stage"; -import { useIsAuthenticated } from "../../../redux/selectors/userSelectors"; +} from "../../models/CardType"; +import type { CardUrlBuilder } from "../../models/CardUrl"; +import type { StageIndex } from "../../models/Stage"; import type { CardOptions } from "../cardOptions"; /** @@ -213,13 +215,12 @@ export function CustomizeStage({ description={ <> Set your{" "} - WakaTime - {" "} + {" "} username to fetch your stats. } @@ -403,13 +404,12 @@ export function CustomizeStage({ )}

For more customization options check the{" "} - customization documentation - {" "} + {" "} after you copied your card URL in step 5.
diff --git a/apps/frontend/src/pages/Home/stages/Display.tsx b/apps/frontend/src/wizard/Home/stages/Display.tsx similarity index 88% rename from apps/frontend/src/pages/Home/stages/Display.tsx rename to apps/frontend/src/wizard/Home/stages/Display.tsx index a04e1f7ad8ad7..89ea760175991 100644 --- a/apps/frontend/src/pages/Home/stages/Display.tsx +++ b/apps/frontend/src/wizard/Home/stages/Display.tsx @@ -2,12 +2,12 @@ import type { JSX } from "react"; import { toast } from "react-toastify"; import { saveSvgAsPng } from "save-svg-as-png"; -import { CardImage } from "../../../components/Card/CardImage"; -import { getCardThemeBackdrop } from "../../../components/Card/themeBackdrop"; -import { Button } from "../../../components/Generic/Button"; import { HOST } from "../../../constants"; -import type { CardUrlBuilder } from "../../../models/CardUrl"; -import { useTheme } from "../../../redux/selectors/themeSelectors"; +import { CardImage } from "../../components/Card/CardImage"; +import { getCardThemeBackdrop } from "../../components/Card/themeBackdrop"; +import { Button } from "../../components/Generic/Button"; +import type { CardUrlBuilder } from "../../models/CardUrl"; +import { useIsDarkTheme } from "../../useIsDarkTheme"; interface DisplayStageProps { filename: string; @@ -24,7 +24,7 @@ export function DisplayStage({ card, guestHint, }: DisplayStageProps): JSX.Element { - const { isDark } = useTheme(); + const isDark = useIsDarkTheme(); const downloadPNG = () => { saveSvgAsPng( diff --git a/apps/frontend/src/pages/Home/stages/Login/Login.tsx b/apps/frontend/src/wizard/Home/stages/Login/Login.tsx similarity index 100% rename from apps/frontend/src/pages/Home/stages/Login/Login.tsx rename to apps/frontend/src/wizard/Home/stages/Login/Login.tsx diff --git a/apps/frontend/src/pages/Home/stages/Login/LoginAccountDeleteModal.tsx b/apps/frontend/src/wizard/Home/stages/Login/LoginAccountDeleteModal.tsx similarity index 89% rename from apps/frontend/src/pages/Home/stages/Login/LoginAccountDeleteModal.tsx rename to apps/frontend/src/wizard/Home/stages/Login/LoginAccountDeleteModal.tsx index 905205d210ca3..2f8dac17f7071 100644 --- a/apps/frontend/src/pages/Home/stages/Login/LoginAccountDeleteModal.tsx +++ b/apps/frontend/src/wizard/Home/stages/Login/LoginAccountDeleteModal.tsx @@ -2,7 +2,7 @@ import { useEffect, useRef } from "react"; import type { JSX, RefObject } from "react"; import { createPortal } from "react-dom"; -import { Button } from "../../../../components/Generic/Button"; +import { Button } from "../../../components/Generic/Button"; function useOutsideAlerter( ref: RefObject, @@ -42,7 +42,8 @@ export function LoginAccountDeleteModal( useOutsideAlerter(wrapperRef, onClose); return createPortal( -
+ // `wizard` because this mounts on `body`, outside the element the app's styles are scoped to. +
diff --git a/apps/frontend/src/pages/Home/stages/Login/LoginOptions.tsx b/apps/frontend/src/wizard/Home/stages/Login/LoginOptions.tsx similarity index 96% rename from apps/frontend/src/pages/Home/stages/Login/LoginOptions.tsx rename to apps/frontend/src/wizard/Home/stages/Login/LoginOptions.tsx index 6ed1a7a639492..813edc7fcabb7 100644 --- a/apps/frontend/src/pages/Home/stages/Login/LoginOptions.tsx +++ b/apps/frontend/src/wizard/Home/stages/Login/LoginOptions.tsx @@ -1,11 +1,11 @@ import type { JSX } from "react"; import { FaGithub as GithubIcon } from "react-icons/fa"; -import { Button } from "../../../../components/Generic/Button"; import { GITHUB_PRIVATE_AUTH_URL, GITHUB_PUBLIC_AUTH_URL, } from "../../../../constants"; +import { Button } from "../../../components/Generic/Button"; import { LoginBox } from "./LoginBox"; diff --git a/apps/frontend/src/pages/Home/stages/SelectCard.tsx b/apps/frontend/src/wizard/Home/stages/SelectCard.tsx similarity index 89% rename from apps/frontend/src/pages/Home/stages/SelectCard.tsx rename to apps/frontend/src/wizard/Home/stages/SelectCard.tsx index b961253916e05..9c4f129cd3fa1 100644 --- a/apps/frontend/src/pages/Home/stages/SelectCard.tsx +++ b/apps/frontend/src/wizard/Home/stages/SelectCard.tsx @@ -1,18 +1,18 @@ import { useMemo } from "react"; import type { JSX } from "react"; -import { Card } from "../../../components/Card/Card"; import { DEMO_GIST, DEMO_REPO, DEMO_USER, DEMO_WAKATIME_USER, } from "../../../constants"; -import { CardType } from "../../../models/CardType"; -import { cardUrl } from "../../../models/CardUrl"; -import type { CardUrlBuilder } from "../../../models/CardUrl"; -import { useTheme } from "../../../redux/selectors/themeSelectors"; import { useUserId } from "../../../redux/selectors/userSelectors"; +import { Card } from "../../components/Card/Card"; +import { CardType } from "../../models/CardType"; +import { cardUrl } from "../../models/CardUrl"; +import type { CardUrlBuilder } from "../../models/CardUrl"; +import { useIsDarkTheme } from "../../useIsDarkTheme"; interface SelectCardStageProps { selectedCardType: CardType; @@ -24,7 +24,7 @@ export function SelectCardStage({ onCardTypeChange, }: SelectCardStageProps): JSX.Element { const userId = useUserId(DEMO_USER); - const { isDark } = useTheme(); + const isDark = useIsDarkTheme(); const options = useMemo< Array<{ diff --git a/apps/frontend/src/pages/Home/stages/Theme.tsx b/apps/frontend/src/wizard/Home/stages/Theme.tsx similarity index 85% rename from apps/frontend/src/pages/Home/stages/Theme.tsx rename to apps/frontend/src/wizard/Home/stages/Theme.tsx index 54d7688268423..9e4da5d95b659 100644 --- a/apps/frontend/src/pages/Home/stages/Theme.tsx +++ b/apps/frontend/src/wizard/Home/stages/Theme.tsx @@ -2,14 +2,16 @@ import type { ThemeName } from "@stats-organization/github-readme-stats-core"; import { themes } from "@stats-organization/github-readme-stats-core"; import type { JSX } from "react"; -import { Card } from "../../../components/Card/Card"; +import { LinkExternal } from "../../../shared/LinkExternal"; +import { Card } from "../../components/Card/Card"; import { getCardThemeBackdrop, getThemeSortRank, -} from "../../../components/Card/themeBackdrop"; -import { CardCategory } from "../../../models/CardType"; -import type { CardUrlBuilder } from "../../../models/CardUrl"; -import { useTheme } from "../../../redux/selectors/themeSelectors"; +} from "../../components/Card/themeBackdrop"; +import { docsUrl } from "../../docsUrl"; +import { CardCategory } from "../../models/CardType"; +import type { CardUrlBuilder } from "../../models/CardUrl"; +import { useIsDarkTheme } from "../../useIsDarkTheme"; const excludedThemes: Array = [ "default", @@ -60,7 +62,7 @@ export function ThemeStage({ category, onThemeChange, }: ThemeStageProps): JSX.Element { - const { isDark } = useTheme(); + const isDark = useIsDarkTheme(); const themeList = category === CardCategory.REPO ? repoCardThemes : nonRepoCardThemes; @@ -97,13 +99,12 @@ export function ThemeStage({
{"For more theme options check the "} - customization documentation - + {" after you copied your card URL in step 5."}
diff --git a/apps/frontend/src/pages/Home/useCardDescriptor.ts b/apps/frontend/src/wizard/Home/useCardDescriptor.ts similarity index 95% rename from apps/frontend/src/pages/Home/useCardDescriptor.ts rename to apps/frontend/src/wizard/Home/useCardDescriptor.ts index 56d5fb67763d5..1723ffba4986a 100644 --- a/apps/frontend/src/pages/Home/useCardDescriptor.ts +++ b/apps/frontend/src/wizard/Home/useCardDescriptor.ts @@ -8,8 +8,8 @@ import { DEMO_WAKATIME_USER, HOST, } from "../../constants"; -import { CardType } from "../../models/CardType"; -import type { CardUrlBuilder } from "../../models/CardUrl"; +import { CardType } from "../models/CardType"; +import type { CardUrlBuilder } from "../models/CardUrl"; /** Per-card metadata for the final display stage. */ export interface CardDescriptor { diff --git a/apps/frontend/src/pages/App/AppTrends.tsx b/apps/frontend/src/wizard/Wizard.tsx similarity index 70% rename from apps/frontend/src/pages/App/AppTrends.tsx rename to apps/frontend/src/wizard/Wizard.tsx index 6b557e2a48e8c..10cb1eec4a4e0 100644 --- a/apps/frontend/src/pages/App/AppTrends.tsx +++ b/apps/frontend/src/wizard/Wizard.tsx @@ -1,23 +1,26 @@ -import { useEffect, useState } from "react"; -import { useDispatch } from "react-redux"; +import { StrictMode, useEffect, useState } from "react"; +import type { JSX } from "react"; +import { Provider, useDispatch } from "react-redux"; import { ToastContainer, toast } from "react-toastify"; -import { getUserMetadata } from "../../api/user"; -import { clearAxiosCache } from "../../axios-override"; -import type { StageIndex } from "../../models/Stage"; -import { useTheme } from "../../redux/selectors/themeSelectors"; +import { clearAxiosCache } from "../axios-override"; import { useIsAuthenticated, useUserKey, useUserToken, -} from "../../redux/selectors/userSelectors"; +} from "../redux/selectors/userSelectors"; import { logout as _logout, setUserAccess as _setUserAccess, -} from "../../redux/slices/user"; -import { HomeScreen } from "../Home/Home"; +} from "../redux/slices/user"; +import { store } from "../redux/store"; -import { Header } from "./Header"; +import { HomeScreen } from "./Home/Home"; +import { getUserMetadata } from "./api/user"; +import { ProgressBar } from "./components/Home/Progress"; +import { STAGE_LABELS } from "./models/Stage"; +import type { StageIndex } from "./models/Stage"; +import { useIsDarkTheme } from "./useIsDarkTheme"; const toMessage = ( input: string | ErrorEvent | PromiseRejectionEvent, @@ -53,11 +56,13 @@ const showError = (event: ErrorEvent | PromiseRejectionEvent): void => { }); }; -export function AppTrends() { +const STAGE_TITLES = STAGE_LABELS.map((it) => it.shortTitle); + +function WizardApp(): JSX.Element { const userKey = useUserKey(); const userToken = useUserToken(); const isAuthenticated = useIsAuthenticated(); - const { isDark } = useTheme(); + const isDark = useIsDarkTheme(); const [stage, setStage] = useState(isAuthenticated ? 1 : 0); @@ -112,8 +117,14 @@ export function AppTrends() { }, [dispatch, userKey]); return ( -
-
+
+ { + setStage(itemIndex as StageIndex); + }} + />
@@ -121,3 +132,14 @@ export function AppTrends() {
); } + +/** Island root: the wizard owns the store, since it is the only React on the site. */ +export function Wizard(): JSX.Element { + return ( + + + + + + ); +} diff --git a/apps/frontend/src/api/user.ts b/apps/frontend/src/wizard/api/user.ts similarity index 97% rename from apps/frontend/src/api/user.ts rename to apps/frontend/src/wizard/api/user.ts index 4fe73bb2f63fa..445f013660e0e 100644 --- a/apps/frontend/src/api/user.ts +++ b/apps/frontend/src/wizard/api/user.ts @@ -1,6 +1,6 @@ import axios from "axios"; -import { HOST } from "../constants"; +import { HOST } from "../../constants"; const authenticate = async ( code: string, diff --git a/apps/frontend/src/components/Card/Card.tsx b/apps/frontend/src/wizard/components/Card/Card.tsx similarity index 100% rename from apps/frontend/src/components/Card/Card.tsx rename to apps/frontend/src/wizard/components/Card/Card.tsx diff --git a/apps/frontend/src/components/Card/CardImage.tsx b/apps/frontend/src/wizard/components/Card/CardImage.tsx similarity index 94% rename from apps/frontend/src/components/Card/CardImage.tsx rename to apps/frontend/src/wizard/components/Card/CardImage.tsx index 9d0e73a620f49..09519100c6324 100644 --- a/apps/frontend/src/components/Card/CardImage.tsx +++ b/apps/frontend/src/wizard/components/Card/CardImage.tsx @@ -1,6 +1,6 @@ import { clsx } from "clsx"; -import { HOST } from "../../constants"; +import { HOST } from "../../../constants"; import type { CardUrlBuilder } from "../../models/CardUrl"; import { SvgInline } from "./SvgInline"; diff --git a/apps/frontend/src/components/Card/SvgInline.tsx b/apps/frontend/src/wizard/components/Card/SvgInline.tsx similarity index 97% rename from apps/frontend/src/components/Card/SvgInline.tsx rename to apps/frontend/src/wizard/components/Card/SvgInline.tsx index 68d0bd758347c..c5cd9b47307a0 100644 --- a/apps/frontend/src/components/Card/SvgInline.tsx +++ b/apps/frontend/src/wizard/components/Card/SvgInline.tsx @@ -7,12 +7,12 @@ import type { JSX } from "react"; import Skeleton from "react-loading-skeleton"; import "react-loading-skeleton/dist/skeleton.css"; -import { setShouldMock } from "../../axios-override.js"; -import { createMockRequest, createMockResponse } from "../../mock-http.js"; +import { setShouldMock } from "../../../axios-override.js"; import { useIsAuthenticated, useUserToken, -} from "../../redux/selectors/userSelectors.js"; +} from "../../../redux/selectors/userSelectors.js"; +import { createMockRequest, createMockResponse } from "../../mock-http.js"; interface SvgInlineProps { url: string; diff --git a/apps/frontend/src/components/Card/themeBackdrop.ts b/apps/frontend/src/wizard/components/Card/themeBackdrop.ts similarity index 100% rename from apps/frontend/src/components/Card/themeBackdrop.ts rename to apps/frontend/src/wizard/components/Card/themeBackdrop.ts diff --git a/apps/frontend/src/components/Generic/Button.tsx b/apps/frontend/src/wizard/components/Generic/Button.tsx similarity index 100% rename from apps/frontend/src/components/Generic/Button.tsx rename to apps/frontend/src/wizard/components/Generic/Button.tsx diff --git a/apps/frontend/src/components/Generic/Checkbox.tsx b/apps/frontend/src/wizard/components/Generic/Checkbox.tsx similarity index 100% rename from apps/frontend/src/components/Generic/Checkbox.tsx rename to apps/frontend/src/wizard/components/Generic/Checkbox.tsx diff --git a/apps/frontend/src/components/Generic/Select.tsx b/apps/frontend/src/wizard/components/Generic/Select.tsx similarity index 100% rename from apps/frontend/src/components/Generic/Select.tsx rename to apps/frontend/src/wizard/components/Generic/Select.tsx diff --git a/apps/frontend/src/components/Home/CheckboxSection.tsx b/apps/frontend/src/wizard/components/Home/CheckboxSection.tsx similarity index 100% rename from apps/frontend/src/components/Home/CheckboxSection.tsx rename to apps/frontend/src/wizard/components/Home/CheckboxSection.tsx diff --git a/apps/frontend/src/components/Home/LanguagesLayoutSection.tsx b/apps/frontend/src/wizard/components/Home/LanguagesLayoutSection.tsx similarity index 100% rename from apps/frontend/src/components/Home/LanguagesLayoutSection.tsx rename to apps/frontend/src/wizard/components/Home/LanguagesLayoutSection.tsx diff --git a/apps/frontend/src/components/Home/NumericSection.tsx b/apps/frontend/src/wizard/components/Home/NumericSection.tsx similarity index 100% rename from apps/frontend/src/components/Home/NumericSection.tsx rename to apps/frontend/src/wizard/components/Home/NumericSection.tsx diff --git a/apps/frontend/src/components/Home/Progress.tsx b/apps/frontend/src/wizard/components/Home/Progress.tsx similarity index 92% rename from apps/frontend/src/components/Home/Progress.tsx rename to apps/frontend/src/wizard/components/Home/Progress.tsx index 161df49ba3d9d..6e9b9def16cbe 100644 --- a/apps/frontend/src/components/Home/Progress.tsx +++ b/apps/frontend/src/wizard/components/Home/Progress.tsx @@ -61,7 +61,8 @@ export function ProgressBar({ const rightDisabled = currItemIndex === items.length - 1; return ( -
+ // The site header is fixed, so the bar parks directly under it rather than at the viewport top. +
-This project is the [extended, actively maintained successor](https://github-stats-extended.vercel.app/frontend/docs/fork/) of [github-readme-stats](https://github.com/anuraghazra/github-readme-stats). It generates stats cards about your GitHub contributions, your top languages and more, all customizable through a large set of parameters. +This project is the [extended, actively maintained successor](https://github-stats-extended.vercel.app/frontend/docs/fork/) of [github-readme-stats](https://github.com/anuraghazra/github-readme-stats). It generates stats cards about your GitHub contributions, your top languages and more, which you can [customize](https://github-stats-extended.vercel.app/frontend/docs/customization/common-options/) through a large set of parameters. ## Quick Start @@ -15,7 +15,7 @@ Copy and paste this into your markdown, then change the `?username=` value to yo [![Anurag's GitHub stats](https://github-stats-extended.vercel.app/api?username=anuraghazra)](https://github.com/stats-organization/github-stats-extended) ``` -As a more comfortable alternative, use the [card wizard](https://github-stats-extended.vercel.app/frontend) to configure your card visually and copy the generated markdown. +As a more comfortable alternative, use the [card wizard](https://github-stats-extended.vercel.app/frontend) to configure your card visually, then copy the generated markdown into your [GitHub profile README](https://docs.github.com/en/account-and-profile/how-tos/profile-customization/managing-your-profile-readme#adding-a-profile-readme). ## Documentation @@ -23,15 +23,16 @@ The full documentation is served by the deployment it describes, so every sample **[github-stats-extended.vercel.app/frontend/docs](https://github-stats-extended.vercel.app/frontend/docs)** -- [Overview](https://github-stats-extended.vercel.app/frontend/docs/) — card types, quick start and migration from github-readme-stats. -- [Advanced Customization](https://github-stats-extended.vercel.app/frontend/docs/advanced_documentation/) — every parameter the cards accept. -- [Available Themes](https://github-stats-extended.vercel.app/frontend/docs/themes/) — the built-in themes. +- [Overview](https://github-stats-extended.vercel.app/frontend/docs/) — quick start and migration from github-readme-stats. +- [Cards](https://github-stats-extended.vercel.app/frontend/docs/#card-types) — a preview of every card, each linking through to its own options. +- [Customization](https://github-stats-extended.vercel.app/frontend/docs/customization/common-options/) — options every card shares, plus theming and locales. +- [Available Themes](https://github-stats-extended.vercel.app/frontend/docs/customization/themes/) — the built-in themes, rendered as live samples. - [Run It Yourself](https://github-stats-extended.vercel.app/frontend/docs/deploy/) — GitHub Actions or a self-hosted Vercel deployment. - [Fork Information](https://github-stats-extended.vercel.app/frontend/docs/fork/) — what this project adds on top of github-readme-stats. ## Acknowledgements -This project is based on [github-readme-stats](https://github.com/anuraghazra/github-readme-stats), and the card wizard on [GitHub Trends](https://github.com/avgupta456/github-trends). Big thanks to [@anuraghazra](https://github.com/anuraghazra), [@avgupta456](https://github.com/avgupta456), [@rickstaa](https://github.com/rickstaa), [@qwerty541](https://github.com/qwerty541) and everyone else who worked on these projects! ❤️ +This project is based on [github-readme-stats](https://github.com/anuraghazra/github-readme-stats). The card wizard is based on [GitHub Trends](https://github.com/avgupta456/github-trends). Big thanks to [@anuraghazra](https://github.com/anuraghazra), [@avgupta456](https://github.com/avgupta456), [@rickstaa](https://github.com/rickstaa), [@qwerty541](https://github.com/qwerty541) and everyone else who worked on these projects! ❤️ ## Contributing diff --git a/apps/frontend/astro.config.ts b/apps/frontend/astro.config.ts index 2f670f5734b2a..e7e909bb8d997 100644 --- a/apps/frontend/astro.config.ts +++ b/apps/frontend/astro.config.ts @@ -6,16 +6,26 @@ import tailwindcss from "@tailwindcss/vite"; import { defineConfig, passthroughImageService } from "astro/config"; import starlightLinksValidator from "starlight-links-validator"; +const base = "/frontend"; + /* * One app: Starlight serves the docs under `/frontend/docs`, and the card * wizard is a page of the same site at `/frontend`. */ export default defineConfig({ site: "https://github-stats-extended.vercel.app", - base: "/frontend", + base, outDir: "./build", // One screenshot; not worth a native image dependency. image: { service: passthroughImageService() }, + // The published Advanced Customization page became the Cards and Customization groups. + // Astro prefixes `base` onto the source but not the destination, hence it spelled out here. + redirects: { + "/docs/advanced_documentation": `${base}/docs/customization/common-options/`, + // Sidebar group labels are not routes, so send them to the group's first page. + "/docs/cards": `${base}/docs/cards/stats/`, + "/docs/customization": `${base}/docs/customization/common-options/`, + }, integrations: [ react(), starlight({ @@ -36,10 +46,33 @@ export default defineConfig({ sidebar: [ { label: "Overview", slug: "docs" }, { - label: "Advanced Customization", - slug: "docs/advanced_documentation", + label: "Cards", + items: [ + { label: "Stats Card", slug: "docs/cards/stats" }, + { label: "Top Languages Card", slug: "docs/cards/top-languages" }, + { label: "Repo Pin Card", slug: "docs/cards/repo-pin" }, + { label: "Gist Pin Card", slug: "docs/cards/gist-pin" }, + { label: "WakaTime Card", slug: "docs/cards/wakatime" }, + ], + }, + { + label: "Customization", + items: [ + { + label: "Common Options", + slug: "docs/customization/common-options", + }, + { label: "Theming", slug: "docs/customization/theming" }, + // Generated by packages/core scripts/generate-theme-readme.js. + { label: "Available Themes", slug: "docs/customization/themes" }, + { label: "Locales", slug: "docs/customization/locales" }, + { + label: "Aligning Cards", + slug: "docs/customization/aligning-cards", + }, + ], }, - { label: "Available Themes", slug: "docs/themes" }, + { label: "Demo Gallery", slug: "docs/demos" }, { label: "Run It Yourself", slug: "docs/deploy" }, { label: "Fork Information", slug: "docs/fork" }, ], diff --git a/apps/frontend/e2e/docs-redirects.spec.ts b/apps/frontend/e2e/docs-redirects.spec.ts new file mode 100644 index 0000000000000..4a75b28b15c35 --- /dev/null +++ b/apps/frontend/e2e/docs-redirects.spec.ts @@ -0,0 +1,34 @@ +import { expect, test } from "@playwright/test"; + +// Nothing links to a redirect, so the build's link validator cannot catch a broken one. +const redirects = [ + { + from: "docs/advanced_documentation", + to: "/frontend/docs/customization/common-options/", + heading: "Common Options", + }, + { + from: "docs/cards", + to: "/frontend/docs/cards/stats/", + heading: "Stats Card", + }, + { + from: "docs/customization", + to: "/frontend/docs/customization/common-options/", + heading: "Common Options", + }, +]; + +for (const { from, to, heading } of redirects) { + test(`/${from} redirects to ${to}`, async ({ page }) => { + for (const url of [from, `${from}/`]) { + await page.goto(url); + + await expect(page).toHaveURL(to); + // Scoped to the page content: Astro's dev toolbar has headings of its own. + await expect( + page.locator("main").getByRole("heading", { level: 1 }), + ).toHaveText(heading); + } + }); +} diff --git a/apps/frontend/src/content/docs/docs/advanced_documentation.md b/apps/frontend/src/content/docs/docs/advanced_documentation.md deleted file mode 100644 index 2c62886a14c71..0000000000000 --- a/apps/frontend/src/content/docs/docs/advanced_documentation.md +++ /dev/null @@ -1,762 +0,0 @@ ---- -title: Advanced Customization ---- - -## GitHub Stats Card - -:::caution[Warning] -By default, the stats card only shows statistics like stars, commits, and pull requests from public repositories. To show private statistics on the stats card, [allow GitHub-Stats-Extended to access your private contributions](/frontend/docs/fork/#private-contributions-support) or [deploy your own instance](/frontend/docs/deploy/). -::: - -:::note -Available ranks are S (top 1%), A+ (12.5%), A (25%), A- (37.5%), B+ (50%), B (62.5%), B- (75%), C+ (87.5%) and C (everyone). This ranking scheme is based on the [Japanese academic grading](https://wikipedia.org/wiki/Academic_grading_in_Japan) system. The global percentile is calculated as a weighted sum of percentiles for each statistic (number of commits, pull requests, reviews, issues, stars, and followers), based on the cumulative distribution function of the [exponential](https://wikipedia.org/wiki/exponential_distribution) and the [log-normal](https://wikipedia.org/wiki/Log-normal_distribution) distributions. The implementation can be investigated at [calculateRank.ts](https://github.com/stats-organization/github-stats-extended/blob/master/packages/core/src/calculateRank.ts). The circle around the rank shows 100 minus the global percentile. -::: - -### Hiding individual stats - -You can pass a query parameter `&hide=` to hide any specific stats with comma-separated values. - -> Options: `&hide=stars,commits,prs,issues,contribs` - -```md -![Anurag's GitHub stats](https://github-stats-extended.vercel.app/api?username=anuraghazra&hide=contribs,prs) -``` - -### Showing additional individual stats - -You can pass a query parameter `&show=` to show any specific additional stats with comma-separated values. - -> Options: `&show=reviews,discussions_started,discussions_answered,prs_merged,prs_merged_percentage,prs_authored,prs_commented,prs_reviewed,issues_authored,issues_commented` - -```md -![Anurag's GitHub stats](https://github-stats-extended.vercel.app/api?username=anuraghazra&show=reviews,discussions_started,discussions_answered,prs_merged,prs_merged_percentage,prs_authored,prs_commented,prs_reviewed,issues_authored,issues_commented) -``` - -### Showing icons - -To enable icons, you can pass `&show_icons=true` in the query param, like so: - -```md -![Anurag's GitHub stats](https://github-stats-extended.vercel.app/api?username=anuraghazra&show_icons=true) -``` - -### Filtering by repository and owner - -To compute your stats for only a specific repository, you can pass a query parameter `&repo=/`. You can also specify a comma-separated list of multiple repositories, e.g. `&repo=userA/repositoryA,organizationB/repositoryB`. And you can select all repositories owned by specific organizations or users by providing a comma-separated list of owners via the `owner` query parameter, e.g. `&owner=userA,organizationB,organizationC`. The `repo` and `owner` filters are supported by the following items: `commits` (when used with `&include_all_commits=true`), `prs_authored`, `prs_commented`, `prs_reviewed`, `issues_authored` and `issues_commented`. Note that most of these items are not displayed by default, but [you can enable them individually](#showing-additional-individual-stats). - -(Some of these mentioned items are similar to other items which are included by default, e.g. `issues_authored` is similar to `issues`. The difference is how these values are fetched - [via GraphQL or via REST API](https://github.com/anuraghazra/github-readme-stats/discussions/1770#number-of-commits-is-incorrect). The default items use GraphQL, but filtering by repository works better via REST API.) - -Alternatively, you can use the `role` parameter to specify a comma-separated list of [roles](https://docs.github.com/en/graphql/reference/enums#repositoryaffiliation). The stats will include all repositories in which the user has the specified role. By default, only repositories where the user is OWNER will be included, but you could e.g. set `&role=OWNER,ORGANIZATION_MEMBER,COLLABORATOR`. The `role` parameter is supported by all items except the following: `commits` (when used with `&include_all_commits=true`), `prs_authored`, `prs_commented`, `prs_reviewed`, `issues_authored` and `issues_commented`. - -### Showing commits count for specified year - -You can specify a year and fetch only the commits that were made in that year by passing `&commits_year=YYYY` to the parameter. - -```md -![Anurag's GitHub stats](https://github-stats-extended.vercel.app/api?username=anuraghazra&commits_year=2020) -``` - -### Themes - -With inbuilt themes, you can customize the look of the card without doing any [manual customization](#customization). - -Use `&theme=THEME_NAME` parameter like so : - -```md -![Anurag's GitHub stats](https://github-stats-extended.vercel.app/api?username=anuraghazra&show_icons=true&theme=radical) -``` - -#### All inbuilt themes - -GitHub Stats Extended comes with several built-in themes (e.g. `dark`, `radical`, `merko`, `gruvbox`, `tokyonight`, `onedark`, `cobalt`, `synthwave`, `highcontrast`, `dracula`). - -GitHub Stats Extended Themes - -You can look at a preview for [all available themes](/frontend/docs/themes/) or checkout the [theme config file](https://github.com/stats-organization/github-stats-extended/blob/master/packages/core/src/themes/index.ts). Please note that we paused the addition of new themes to decrease maintenance efforts; all pull requests related to new themes will be closed. - -#### Responsive Card Theme - -[![Anurag's GitHub stats-Dark](https://github-stats-extended.vercel.app/api?username=anuraghazra&show_icons=true&theme=dark#gh-dark-mode-only)](https://github.com/stats-organization/github-stats-extended#responsive-card-theme#gh-dark-mode-only) -[![Anurag's GitHub stats-Light](https://github-stats-extended.vercel.app/api?username=anuraghazra&show_icons=true&theme=default#gh-light-mode-only)](https://github.com/stats-organization/github-stats-extended#responsive-card-theme#gh-light-mode-only) - -Since GitHub will re-upload the cards and serve them from their [CDN](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/about-anonymized-urls), we can not infer the browser/GitHub theme on the server side. There are, however, four methods you can use to create dynamics themes on the client side. - -##### Use GitHub's new media feature (recommended) - -You can use [GitHub's new media feature](https://github.blog/changelog/2022-05-19-specify-theme-context-for-images-in-markdown-beta/) in HTML to specify whether to display images for light or dark themes. This is done using the HTML `` element in combination with the `prefers-color-scheme` media feature. - - -```html - - - - - -``` - -
-👀 Show example - - - - - - - -
- -##### Use GitHub's theme context tag - -You can use [GitHub's theme context](https://github.blog/changelog/2021-11-24-specify-theme-context-for-images-in-markdown/) tags to switch the theme based on the user GitHub theme automatically. This is done by appending `#gh-dark-mode-only` or `#gh-light-mode-only` to the end of an image URL. This tag will define whether the image specified in the markdown is only shown to viewers using a light or a dark GitHub theme: - -```md -[![Anurag's GitHub stats-Dark](https://github-stats-extended.vercel.app/api?username=anuraghazra&show_icons=true&theme=dark#gh-dark-mode-only)](https://github.com/stats-organization/github-stats-extended#gh-dark-mode-only) -[![Anurag's GitHub stats-Light](https://github-stats-extended.vercel.app/api?username=anuraghazra&show_icons=true&theme=default#gh-light-mode-only)](https://github.com/stats-organization/github-stats-extended#gh-light-mode-only) -``` - -
-👀 Show example - -[![Anurag's GitHub stats-Dark](https://github-stats-extended.vercel.app/api?username=anuraghazra&show_icons=true&theme=dark#gh-dark-mode-only)](https://github.com/stats-organization/github-stats-extended#gh-dark-mode-only) -[![Anurag's GitHub stats-Light](https://github-stats-extended.vercel.app/api?username=anuraghazra&show_icons=true&theme=default#gh-light-mode-only)](https://github.com/stats-organization/github-stats-extended#gh-light-mode-only) - -
- -##### Use the transparent theme - -We have included a `transparent` theme that has a transparent background. This theme is optimized to look good on GitHub's dark and light default themes. You can enable this theme using the `&theme=transparent` parameter like so: - -```md -![Anurag's GitHub stats](https://github-stats-extended.vercel.app/api?username=anuraghazra&show_icons=true&theme=transparent) -``` - -
-👀 Show example - -![Anurag's GitHub stats](https://github-stats-extended.vercel.app/api?username=anuraghazra&show_icons=true&theme=transparent) - -
- -##### Add transparent alpha channel to a themes bg\_color - -You can use the `bg_color` parameter to make any of [the available themes](/frontend/docs/themes/) transparent. This is done by setting the `bg_color` to a color with a transparent alpha channel (i.e. `bg_color=00000000`): - -```md -![Anurag's GitHub stats](https://github-stats-extended.vercel.app/api?username=anuraghazra&show_icons=true&bg_color=00000000) -``` - -
-👀 Show example - -![Anurag's GitHub stats](https://github-stats-extended.vercel.app/api?username=anuraghazra&show_icons=true&bg_color=00000000) - -
- -### Customization - -You can customize the appearance of all your cards however you wish with URL parameters. - -#### Common Options - -| Name | Description | Type | Default value | -| --------------- | ------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- | ------------- | -| `title_color` | Card's title color. | string (hex color) | `2f80ed` | -| `text_color` | Body text color. | string (hex color) | `434d58` | -| `icon_color` | Icons color if available. | string (hex color) | `4c71f2` | -| `border_color` | Card's border color. Does not apply when `hide_border` is enabled. | string (hex color) | `e4e2e2` | -| `bg_color` | Card's background color. | string (hex color or a gradient in the form of _angle,start,end_) | `fffefe` | -| `hide_border` | Hides the card's border. | boolean | `false` | -| `theme` | Name of the theme, choose from [all available themes](/frontend/docs/themes/). | enum | `default` | -| `cache_seconds` | Sets the cache header manually (min: 21600, max: 86400). | integer | `21600` | -| `locale` | Sets the language in the card, you can check full list of available locales [here](#available-locales). | enum | `en` | -| `border_radius` | Corner rounding on the card. | number | `4.5` | - -:::caution[Warning] -We use caching to reduce the load on our servers (see [this discussion](https://github.com/anuraghazra/github-readme-stats/issues/1471#issuecomment-1271551425)). - -Cards generated by [GitHub Stats Extended](https://github-stats-extended.vercel.app/frontend) are cached for a few hours to several days, depending on the current server load. - -If you need your card data to update more frequently, you can [deploy your own instance](/frontend/docs/deploy/) and set the [`CACHE_SECONDS`](/frontend/docs/deploy/#available-environment-variables) environment variable to your preferred value. - -Alternatively, you can use the [GitHub Actions workflow](/frontend/docs/deploy/#github-action) to update your cards on a schedule. -::: - -##### Gradient in bg\_color - -You can provide multiple comma-separated values in the bg\_color option to render a gradient with the following format: - - &bg_color=DEG,COLOR1,COLOR2,COLOR3...COLOR10 - -##### Available locales - -Here is a list of all available locales: - - - -
- -| Code | Locale | -| ------- | ---------------- | -| `ar` | Arabic | -| `az` | Azerbaijani | -| `be` | Belarusian | -| `bn` | Bengali | -| `bg` | Bulgarian | -| `my` | Burmese | -| `ca` | Catalan | -| `cn` | Chinese | -| `zh-tw` | Chinese (Taiwan) | -| `cs` | Czech | -| `nl` | Dutch | -| `en` | English | -| `fil` | Filipino | -| `fi` | Finnish | -| `fr` | French | -| `de` | German | - - - -| Code | Locale | -| ------- | --------------------- | -| `el` | Greek | -| `he` | Hebrew | -| `hi` | Hindi | -| `hu` | Hungarian | -| `id` | Indonesian | -| `it` | Italian | -| `ja` | Japanese | -| `kr` | Korean | -| `ml` | Malayalam | -| `np` | Nepali | -| `no` | Norwegian | -| `fa` | Persian (Farsi) | -| `pl` | Polish | -| `pt-br` | Portuguese (Brazil) | -| `pt-pt` | Portuguese (Portugal) | -| `ro` | Romanian | - - - -| Code | Locale | -| --------- | ------------------ | -| `ru` | Russian | -| `sa` | Sanskrit | -| `sr` | Serbian (Cyrillic) | -| `sr-latn` | Serbian (Latin) | -| `sk` | Slovak | -| `es` | Spanish | -| `sw` | Swahili | -| `se` | Swedish | -| `ta` | Tamil | -| `th` | Thai | -| `tr` | Turkish | -| `uk-ua` | Ukrainian | -| `ur` | Urdu | -| `uz` | Uzbek | -| `vi` | Vietnamese | - -
- -If we don't support your language, please consider contributing! You can find more information about how to do it in our [contributing guidelines](https://github.com/stats-organization/github-stats-extended/blob/master/.github/CONTRIBUTING.md#translations-contribution). - -#### Stats Card Exclusive Options - -| Name | Description | Type | Default value | -| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------- | ----------------------------------- | -| `hide` | Hides the [specified items](#hiding-individual-stats) from stats. | string (comma-separated values) | `null` | -| `hide_title` | Hides the title of your stats card. | boolean | `false` | -| `card_width` | Sets the card's width manually. | number | `500px (approx.)` | -| `hide_rank` | Hides the rank and automatically resizes the card width. | boolean | `false` | -| `rank_icon` | Shows alternative rank icon (i.e. `github`, `percentile` or `default`). | enum | `default` | -| `show_icons` | Shows icons near all stats. | boolean | `false` | -| `include_all_commits` | Count total commits instead of just the current year commits. | boolean | `false` | -| `line_height` | Sets the line height between text. | integer | `25` | -| `exclude_repo` | Excludes specified repositories. Affects only the count for "Total Stars Earned". | string (comma-separated values) | `null` | -| `repo` | Count only stats from the specified repositories. Affects only [certain items](#filtering-by-repository-and-owner). | string (comma-separated values) | `null` | -| `owner` | Count only stats from the specified organizations or users. Affects only [certain items](#filtering-by-repository-and-owner). | string (comma-separated values) | `null` | -| `role` | Include repositories where the user has one of the specified [roles](https://docs.github.com/en/graphql/reference/enums#repositoryaffiliation) (OWNER, ORGANIZATION_MEMBER, COLLABORATOR). | string (comma-separated values) | `OWNER` | -| `custom_title` | Sets a custom title for the card. | string | ` GitHub Stats` | -| `text_bold` | Uses bold text. | boolean | `true` | -| `disable_animations` | Disables all animations in the card. | boolean | `false` | -| `ring_color` | Color of the rank circle. | string (hex color) | `2f80ed` | -| `number_format` | Switches between two available formats for displaying the card values: `short` (i.e. `6.6k`) and `long` (i.e. `6626`). | enum | `short` | -| `number_precision` | Enforce the number of digits after the decimal point for `short` number format. Must be an integer between 0 and 2. Will be ignored for `long` number format. | integer (0, 1 or 2) | `null` | -| `show` | Shows [additional items](#showing-additional-individual-stats) on stats card (i.e. `reviews`, `discussions_started`, `discussions_answered`, `prs_merged` or `prs_merged_percentage`. And the following, which support the `repo` and `owner` filters: `prs_authored`, `prs_commented`, `prs_reviewed`, `issues_authored` or `issues_commented`). | string (comma-separated values) | `null` | -| `commits_year` | Filters and counts only commits made in the specified year. | integer _(YYYY)_ | ` (one year to date)` | - -:::caution[Warning] -Custom title should be URI-escaped, as specified in [Percent Encoding](https://en.wikipedia.org/wiki/Percent-encoding) (i.e: `Anurag's GitHub Stats` should become `Anurag%27s%20GitHub%20Stats`). You can use [urlencoder.org](https://www.urlencoder.org/) to help you do this automatically. -::: - -:::note -When hide\_rank=`true`, the minimum card width is 270 px + the title length and padding. -::: - ---- - -## GitHub Extra Pins - -GitHub extra pins allow you to pin more than 6 repositories in your profile using a GitHub profile readme. - -Yay! You are no longer limited to 6 pinned repositories. - -### Usage - -Copy-paste this code into your readme and change the links. - -Endpoint: `api/pin?username=anuraghazra&repo=type-trident` - -```md -[![Readme Card](https://github-stats-extended.vercel.app/api/pin/?username=anuraghazra&repo=type-trident)](https://github.com/anuraghazra/type-trident) -``` - -### Options - -You can customize the appearance and behavior of the pinned repository card using the [common options](#common-options) and exclusive options listed in the table below. - -| Name | Description | Type | Default value | -| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------- | ------------------ | -| `show_owner` | Shows the repo's owner name. | boolean | `false` | -| `browser_rendering` | Compute text wrapping of repository description natively in the browser, instead of computing it server-side. | boolean | `false` | -| `description_lines_count` | Manually set the number of lines for the description. Specified value will be clamped between 1 and 3. If this parameter is not specified, the number of lines will be automatically adjusted according to the actual length of the description. | number | `null` | -| `card_width` | Sets the card's width manually. | number | `400px (approx.)` | -| `show_icons` | Shows icons near all stats enabled via `show`. | boolean | `true` | -| `line_height` | Sets the line height between stats enabled via `show`. | integer | `22` | -| `text_bold` | Uses bold text for all stats enabled via `show`. | boolean | `false` | -| `number_format` | Switches between two available formats for displaying the numbers for all stats enabled via `show`: `short` (i.e. `6.6k`) and `long` (i.e. `6626`). | enum | `short` | -| `show` | Shows [additional items](#showing-additional-individual-stats) on stats card (i.e. `prs_authored`, `prs_commented`, `prs_reviewed`, `issues_authored` or `issues_commented`). | string (comma-separated values) | `null` | - -### Demo - -![Readme Card](https://github-stats-extended.vercel.app/api/pin/?username=anuraghazra&repo=github-readme-stats) - -Use [show\_owner](#options) query option to include the repo's owner username: - -![Readme Card](https://github-stats-extended.vercel.app/api/pin/?username=anuraghazra&repo=github-readme-stats&show_owner=true) - -Use [show](#options) query option to display the user's contributions to the repository: - -![Readme Card](https://github-stats-extended.vercel.app/api/pin/?username=anuraghazra&repo=github-readme-stats&show=prs_authored,prs_commented,prs_reviewed,issues_authored,issues_commented) - -You can also specify the `repo` parameter in the form `/` to pin a repository from any user or organization, not just your own. This allows you to showcase repositories you contributed to, regardless of ownership. - -![Readme Card](https://github-stats-extended.vercel.app/api/pin/?username=anuraghazra&repo=statykjs/statyk&show_owner=true&show=prs_authored,prs_commented,prs_reviewed,issues_authored,issues_commented) - -## GitHub Gist Pins - -GitHub gist pins allow you to pin gists in your GitHub profile using a GitHub profile readme. - -### Usage - -Copy-paste this code into your readme and change the links. - -Endpoint: `api/gist?id=bbfce31e0217a3689c8d961a356cb10d` - -```md -[![Gist Card](https://github-stats-extended.vercel.app/api/gist?id=bbfce31e0217a3689c8d961a356cb10d)](https://gist.github.com/Yizack/bbfce31e0217a3689c8d961a356cb10d/) -``` - -### Options - -You can customize the appearance and behavior of the gist card using the [common options](#common-options) and exclusive options listed in the table below. - -| Name | Description | Type | Default value | -| ------------------- | ------------------------------------------------------------------------------------------------------- | ------- | ------------- | -| `show_owner` | Shows the gist's owner name. | boolean | `false` | -| `browser_rendering` | Compute text wrapping of gist description natively in the browser, instead of computing it server-side. | boolean | `false` | - -### Demo - -![Gist Card](https://github-stats-extended.vercel.app/api/gist?id=bbfce31e0217a3689c8d961a356cb10d) - -Use [show\_owner](#options-1) query option to include the gist's owner username - -![Gist Card](https://github-stats-extended.vercel.app/api/gist?id=bbfce31e0217a3689c8d961a356cb10d&show_owner=true) - -## Top Languages Card - -The top languages card shows your most frequently used languages. - -:::caution[Warning] -By default, the language card shows language results only from public repositories. To include languages used in private repositories, [allow GitHub-Stats-Extended to access your private contributions](/frontend/docs/fork/#private-contributions-support) or [deploy your own instance](/frontend/docs/deploy/). -::: - -:::caution[Warning] -This card shows language usage only inside your own non-forked repositories, not depending on who the author of the commits is. It does not include your contributions into another users/organizations repositories. Currently there are no way to get this data from GitHub API. If you want this behavior to be improved you can support [this feature request](https://github.com/orgs/community/discussions/18230) created by [@rickstaa](https://github.com/rickstaa) inside GitHub Community. -::: - -:::caution[Warning] -Currently this card shows data only about first 1000 repositories. This is because GitHub API limitations which cause downtimes of public instances (see [#1471](https://github.com/anuraghazra/github-readme-stats/issues/1471)). In future this behavior will be improved by releasing GitHub action or providing environment variables for user's own instances. -::: - -### Usage - -Copy-paste this code into your readme and change the links. - -Endpoint: `api/top-langs?username=anuraghazra` - -```md -[![Top Langs](https://github-stats-extended.vercel.app/api/top-langs/?username=anuraghazra)](https://github.com/stats-organization/github-stats-extended) -``` - -### Options - -You can customize the appearance and behavior of the top languages card using the [common options](#common-options) and exclusive options listed in the table below. - -| Name | Description | Type | Default value | -| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------- | --------------------------------------------------- | -| `hide` | Hides the [specified languages](#hide-individual-languages) from card. | string (comma-separated values) | `null` | -| `hide_title` | Hides the title of your card. | boolean | `false` | -| `layout` | Switches between five available layouts `normal` & `compact` & `donut` & `donut-vertical` & `pie`. | enum | `normal` | -| `card_width` | Sets the card's width manually. | number | `300` | -| `langs_count` | Shows more languages on the card, between 1-20. | integer | `5` for `normal` and `donut`, `6` for other layouts | -| `exclude_repo` | Excludes specified repositories. | string (comma-separated values) | `null` | -| `role` | Include repositories where the user has one of the specified [roles](https://docs.github.com/en/graphql/reference/enums#repositoryaffiliation) (OWNER, ORGANIZATION_MEMBER, COLLABORATOR). | string (comma-separated values) | `OWNER` | -| `custom_title` | Sets a custom title for the card. | string | `Most Used Languages` | -| `disable_animations` | Disables all animations in the card. | boolean | `false` | -| `prog_bar_bg_color` | Background color of the bars. (Applies only to `normal` layout.) | string (hex color) | `#ddd` | -| `hide_progress` | Uses the compact layout option, hides percentages, and removes the bars. | boolean | `false` | -| `hide_values` | Hides language percentages or bytes while keeping the progress bars or chart. | boolean | `false` | -| `size_weight` | Configures language stats algorithm (see [Language stats algorithm](#language-stats-algorithm)). | integer | `1` | -| `count_weight` | Configures language stats algorithm (see [Language stats algorithm](#language-stats-algorithm)). | integer | `0` | -| `stats_format` | Switches between two available formats for language's stats `percentages` and `bytes`. | enum | `percentages` | - -:::caution[Warning] -Language names and custom title should be URI-escaped, as specified in [Percent Encoding](https://en.wikipedia.org/wiki/Percent-encoding) (i.e: `c++` should become `c%2B%2B`, `jupyter notebook` should become `jupyter%20notebook`, `Most Used Languages` should become `Most%20Used%20Languages`, etc.) You can use [urlencoder.org](https://www.urlencoder.org/) to help you do this automatically. -::: - -### Language stats algorithm - -We use the following algorithm to calculate the languages percentages on the language card: - -```js -ranking_index = (byte_count ^ size_weight) * (repo_count ^ count_weight); -``` - -By default, only the byte count is used for determining the languages percentages shown on the language card (i.e. `size_weight=1` and `count_weight=0`). You can, however, use the `&size_weight=` and `&count_weight=` options to weight the language usage calculation. The values must be positive real numbers. [More details about the algorithm can be found here](https://github.com/anuraghazra/github-readme-stats/issues/1600#issuecomment-1046056305). - -- `&size_weight=1&count_weight=0` - _(default)_ Orders by byte count. -- `&size_weight=0.5&count_weight=0.5` - _(recommended)_ Uses both byte and repo count for ranking - - `&size_weight=0&count_weight=1` - Orders by repo count - -```md -![Top Langs](https://github-stats-extended.vercel.app/api/top-langs/?username=anuraghazra&size_weight=0.5&count_weight=0.5) -``` - -If the percentages still look wrong to you, these two comments cover most of the reasons why, and are worth reading before opening an issue: - -- -- - -### Exclude individual repositories - -You can use the `&exclude_repo=repo1,repo2` parameter to exclude individual repositories. - -```md -![Top Langs](https://github-stats-extended.vercel.app/api/top-langs/?username=anuraghazra&exclude_repo=github-readme-stats,anuraghazra.github.io) -``` - -### Hide individual languages - -You can use `&hide=language1,language2` parameter to hide individual languages. - -```md -![Top Langs](https://github-stats-extended.vercel.app/api/top-langs/?username=anuraghazra&hide=javascript,html) -``` - -Language names with spaces or symbols need to be [percent-encoded](#options-2), so Jupyter Notebook becomes `&hide=jupyter%20notebook` and C++ becomes `&hide=c%2B%2B`. - -### Show more languages - -You can use the `&langs_count=` option to increase or decrease the number of languages shown on the card. Valid values are integers between 1 and 20 (inclusive). By default it was set to `5` for `normal` & `donut` and `6` for other layouts. - -```md -![Top Langs](https://github-stats-extended.vercel.app/api/top-langs/?username=anuraghazra&langs_count=8) -``` - -### Compact Language Card Layout - -You can use the `&layout=compact` option to change the card design. - -```md -![Top Langs](https://github-stats-extended.vercel.app/api/top-langs/?username=anuraghazra&layout=compact) -``` - -### Donut Chart Language Card Layout - -You can use the `&layout=donut` option to change the card design. - -```md -[![Top Langs](https://github-stats-extended.vercel.app/api/top-langs/?username=anuraghazra&layout=donut)](https://github.com/stats-organization/github-stats-extended) -``` - -### Donut Vertical Chart Language Card Layout - -You can use the `&layout=donut-vertical` option to change the card design. - -```md -[![Top Langs](https://github-stats-extended.vercel.app/api/top-langs/?username=anuraghazra&layout=donut-vertical)](https://github.com/stats-organization/github-stats-extended) -``` - -### Pie Chart Language Card Layout - -You can use the `&layout=pie` option to change the card design. - -```md -[![Top Langs](https://github-stats-extended.vercel.app/api/top-langs/?username=anuraghazra&layout=pie)](https://github.com/stats-organization/github-stats-extended) -``` - -### Hide Progress Bars - -You can use the `&hide_progress=true` option to hide the percentages and the progress bars (layout will be automatically set to `compact`). - -```md -![Top Langs](https://github-stats-extended.vercel.app/api/top-langs/?username=anuraghazra&hide_progress=true) -``` - -### Change format of language's stats - -You can use the `&stats_format=bytes` option to display the stats in bytes instead of percentage. - -```md -![Top Langs](https://github-stats-extended.vercel.app/api/top-langs/?username=anuraghazra&stats_format=bytes) -``` - -### Demo - -![Top Langs](https://github-stats-extended.vercel.app/api/top-langs/?username=anuraghazra) - -#### Compact layout - -![Top Langs](https://github-stats-extended.vercel.app/api/top-langs/?username=anuraghazra&layout=compact) - -#### Donut Chart layout - -[![Top Langs](https://github-stats-extended.vercel.app/api/top-langs/?username=anuraghazra&layout=donut)](https://github-stats-extended.vercel.app/api/top-langs/?username=anuraghazra&layout=donut) - -#### Donut Vertical Chart layout - -[![Top Langs](https://github-stats-extended.vercel.app/api/top-langs/?username=anuraghazra&layout=donut-vertical)](https://github-stats-extended.vercel.app/api/top-langs/?username=anuraghazra&layout=donut-vertical) - -#### Pie Chart layout - -[![Top Langs](https://github-stats-extended.vercel.app/api/top-langs/?username=anuraghazra&layout=pie)](https://github-stats-extended.vercel.app/api/top-langs/?username=anuraghazra&layout=pie) - -#### Hidden progress bars - -[![Top Langs](https://github-stats-extended.vercel.app/api/top-langs/?username=anuraghazra&hide_progress=true)](https://github-stats-extended.vercel.app/api/top-langs/?username=anuraghazra&hide_progress=true) - -#### Display bytes instead of percentage - -[![Top Langs](https://github-stats-extended.vercel.app/api/top-langs/?username=anuraghazra&stats_format=bytes)](https://github-stats-extended.vercel.app/api/top-langs/?username=anuraghazra&stats_format=bytes) - -## WakaTime Stats Card - -:::caution[Warning] -Please be aware that we currently only show data from WakaTime profiles that are public. You therefore have to make sure that **BOTH** `Display code time publicly` and `Display languages, editors, os, categories publicly` are enabled. -::: - -:::caution[Warning] -In case you just created a new WakaTime account, then it might take up to 24 hours until your stats will become visible on the WakaTime stats card. -::: - -Change the `?username=` value to your [WakaTime](https://wakatime.com) username. - -```md -[![Alan's WakaTime stats](https://github-stats-extended.vercel.app/api/wakatime?username=alan)](https://wakatime.com/@alan) -``` - -### Options - -You can customize the appearance and behavior of the WakaTime stats card using the [common options](#common-options) and exclusive options listed in the table below. - -| Name | Description | Type | Default value | -| -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------- | ---------------- | -| `hide` | Hides the languages specified from the card. | string (comma-separated values) | `null` | -| `hide_title` | Hides the title of your card. | boolean | `false` | -| `card_width` | Sets the card's width manually. | number | `495` | -| `line_height` | Sets the line height between text. | integer | `25` | -| `hide_progress` | Hides the progress bar and percentage. | boolean | `false` | -| `custom_title` | Sets a custom title for the card. | string | `WakaTime Stats` | -| `layout` | Switches between two available layouts `default` & `compact`. | enum | `default` | -| `langs_count` | Limits the number of languages on the card, defaults to all reported languages. | integer | `null` | -| `api_domain` | Sets a custom API domain for the card, e.g. to use services like [Hakatime](https://github.com/mujx/hakatime) or [Wakapi](https://github.com/muety/wakapi) | string | `wakatime.com` | -| `display_format` | Sets the WakaTime stats display format. Choose `time` to display time-based stats or `percent` to show percentages. | enum | `time` | -| `disable_animations` | Disables all animations in the card. | boolean | `false` | - -:::caution[Warning] -Custom title should be URI-escaped, as specified in [Percent Encoding](https://en.wikipedia.org/wiki/Percent-encoding) (i.e: `WakaTime Stats` should become `WakaTime%20Stats`). You can use [urlencoder.org](https://www.urlencoder.org/) to help you do this automatically. -::: - -### Demo - -![Alan's WakaTime stats](https://github-stats-extended.vercel.app/api/wakatime?username=alan) - -![Alan's WakaTime stats](https://github-stats-extended.vercel.app/api/wakatime?username=alan&card_width=315&hide_progress=true) - -#### Compact layout - -![Alan's WakaTime stats](https://github-stats-extended.vercel.app/api/wakatime?username=alan&layout=compact) - ---- - -## All Demos - -### Default - -![Anurag's GitHub stats](https://github-stats-extended.vercel.app/api?username=anuraghazra) - -### Hiding specific stats - -![Anurag's GitHub stats](https://github-stats-extended.vercel.app/api?username=anuraghazra&hide=contribs,issues) - -### Showing additional stats - -![Anurag's GitHub stats](https://github-stats-extended.vercel.app/api?username=anuraghazra&show_icons=true&show=reviews,discussions_started,discussions_answered,prs_merged,prs_merged_percentage,prs_commented,prs_reviewed,issues_commented) - -### Showing stats for a specific repository - -![Anurag's GitHub stats for anuraghazra/github-readme-stats](https://github-stats-extended.vercel.app/api?username=anuraghazra&repo=anuraghazra/github-readme-stats&hide=prs,issues,stars,commits,contribs&show=prs_authored,prs_commented,prs_reviewed,issues_authored,issues_commented&hide_rank=true&custom_title=Anurag%27s%20Stats%20for%20github-readme-stats&card_width=370) - -### Showing stats for a specific organization - -![Anurag's GitHub stats for razorpay](https://github-stats-extended.vercel.app/api?username=anuraghazra&owner=razorpay&hide=prs,issues,stars,commits,contribs&show=prs_authored,prs_commented,prs_reviewed,issues_authored,issues_commented&hide_rank=true&custom_title=Anurag%27s%20Stats%20for%20razorpay&card_width=370) - -### Showing icons - -![Anurag's GitHub stats](https://github-stats-extended.vercel.app/api?username=anuraghazra&hide=issues&show_icons=true) - -### Shows GitHub logo instead rank level - -![Anurag's GitHub stats](https://github-stats-extended.vercel.app/api?username=anuraghazra&rank_icon=github) - -### Shows user rank percentile instead of rank level - -![Anurag's GitHub stats](https://github-stats-extended.vercel.app/api?username=anuraghazra&rank_icon=percentile) - -### Customize Border Color - -![Anurag's GitHub stats](https://github-stats-extended.vercel.app/api?username=anuraghazra&border_color=2e4058) - -### Include All Commits - -![Anurag's GitHub stats](https://github-stats-extended.vercel.app/api?username=anuraghazra&include_all_commits=true) - -### Themes - -Choose from any of the [default themes](#themes) - -![Anurag's GitHub stats](https://github-stats-extended.vercel.app/api?username=anuraghazra&show_icons=true&theme=radical) - -### Gradient - -![Anurag's GitHub stats](https://github-stats-extended.vercel.app/api?username=anuraghazra&bg_color=30,e96443,904e95&title_color=fff&text_color=fff) - -### Customizing stats card - -![Anurag's GitHub stats](https://github-stats-extended.vercel.app/api/?username=anuraghazra&show_icons=true&title_color=fff&icon_color=79ff97&text_color=9f9f9f&bg_color=151515) - -### Setting card locale - -![Anurag's GitHub stats](https://github-stats-extended.vercel.app/api/?username=anuraghazra&locale=es) - -### Customizing repo card - -![Customized Card](https://github-stats-extended.vercel.app/api/pin?username=anuraghazra&repo=github-readme-stats&title_color=fff&icon_color=f9f9f9&text_color=9f9f9f&bg_color=151515) - -### Gist card - -![Gist Card](https://github-stats-extended.vercel.app/api/gist?id=bbfce31e0217a3689c8d961a356cb10d) - -### Customizing gist card - -![Gist Card](https://github-stats-extended.vercel.app/api/gist?id=bbfce31e0217a3689c8d961a356cb10d&theme=calm) - -### Top languages - -![Top Langs](https://github-stats-extended.vercel.app/api/top-langs/?username=anuraghazra) - -### WakaTime card - -![Alan's WakaTime stats](https://github-stats-extended.vercel.app/api/wakatime?username=alan) - ---- - -## Quick Tip (Align The Cards) - -By default, GitHub does not lay out the cards side by side. To do that, you can use such approaches: - -### Stats and top languages cards - - -```html - - - - - - -``` - -
-👀 Show example - - - - - - - - -
- -### Pinning repositories - -```html - - - - - - -``` - -
-👀 Show example - - - - - - - - -
diff --git a/apps/frontend/src/content/docs/docs/cards/gist-pin.md b/apps/frontend/src/content/docs/docs/cards/gist-pin.md new file mode 100644 index 0000000000000..dd4648bd36edb --- /dev/null +++ b/apps/frontend/src/content/docs/docs/cards/gist-pin.md @@ -0,0 +1,32 @@ +--- +title: Gist Pin Card +--- + +GitHub gist pins allow you to pin gists in your GitHub profile using a GitHub profile readme. + +## Usage + +Copy-paste this code into your readme and change the links. + +Endpoint: `api/gist?id=bbfce31e0217a3689c8d961a356cb10d` + +```md +[![Gist Card](https://github-stats-extended.vercel.app/api/gist?id=bbfce31e0217a3689c8d961a356cb10d)](https://gist.github.com/Yizack/bbfce31e0217a3689c8d961a356cb10d/) +``` + +## Options + +You can customize the appearance and behavior of the gist card using the [common options](/frontend/docs/customization/common-options/) and exclusive options listed in the table below. + +| Name | Description | Type | Default value | +| ------------------- | ------------------------------------------------------------------------------------------------------- | ------- | ------------- | +| `show_owner` | Shows the gist's owner name. | boolean | `false` | +| `browser_rendering` | Compute text wrapping of gist description natively in the browser, instead of computing it server-side. | boolean | `false` | + +## Demo + +![Gist Card](https://github-stats-extended.vercel.app/api/gist?id=bbfce31e0217a3689c8d961a356cb10d) + +Use [show\_owner](#options) query option to include the gist's owner username + +![Gist Card](https://github-stats-extended.vercel.app/api/gist?id=bbfce31e0217a3689c8d961a356cb10d&show_owner=true) diff --git a/apps/frontend/src/content/docs/docs/cards/repo-pin.md b/apps/frontend/src/content/docs/docs/cards/repo-pin.md new file mode 100644 index 0000000000000..ff26c94032ff6 --- /dev/null +++ b/apps/frontend/src/content/docs/docs/cards/repo-pin.md @@ -0,0 +1,49 @@ +--- +title: Repo Pin Card +--- + +GitHub extra pins allow you to pin more than 6 repositories in your profile using a GitHub profile readme. + +Yay! You are no longer limited to 6 pinned repositories. + +## Usage + +Copy-paste this code into your readme and change the links. + +Endpoint: `api/pin?username=anuraghazra&repo=type-trident` + +```md +[![Readme Card](https://github-stats-extended.vercel.app/api/pin/?username=anuraghazra&repo=type-trident)](https://github.com/anuraghazra/type-trident) +``` + +## Options + +You can customize the appearance and behavior of the pinned repository card using the [common options](/frontend/docs/customization/common-options/) and exclusive options listed in the table below. + +| Name | Description | Type | Default value | +| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------- | ------------------ | +| `show_owner` | Shows the repo's owner name. | boolean | `false` | +| `browser_rendering` | Compute text wrapping of repository description natively in the browser, instead of computing it server-side. | boolean | `false` | +| `description_lines_count` | Manually set the number of lines for the description. Specified value will be clamped between 1 and 3. If this parameter is not specified, the number of lines will be automatically adjusted according to the actual length of the description. | number | `null` | +| `card_width` | Sets the card's width manually. | number | `400px (approx.)` | +| `show_icons` | Shows icons near all stats enabled via `show`. | boolean | `true` | +| `line_height` | Sets the line height between stats enabled via `show`. | integer | `22` | +| `text_bold` | Uses bold text for all stats enabled via `show`. | boolean | `false` | +| `number_format` | Switches between two available formats for displaying the numbers for all stats enabled via `show`: `short` (i.e. `6.6k`) and `long` (i.e. `6626`). | enum | `short` | +| `show` | Shows [additional items](/frontend/docs/cards/stats/#showing-additional-individual-stats) on stats card (i.e. `prs_authored`, `prs_commented`, `prs_reviewed`, `issues_authored` or `issues_commented`). | string (comma-separated values) | `null` | + +## Demo + +![Readme Card](https://github-stats-extended.vercel.app/api/pin/?username=anuraghazra&repo=github-readme-stats) + +Use [show\_owner](#options) query option to include the repo's owner username: + +![Readme Card](https://github-stats-extended.vercel.app/api/pin/?username=anuraghazra&repo=github-readme-stats&show_owner=true) + +Use [show](#options) query option to display the user's contributions to the repository: + +![Readme Card](https://github-stats-extended.vercel.app/api/pin/?username=anuraghazra&repo=github-readme-stats&show=prs_authored,prs_commented,prs_reviewed,issues_authored,issues_commented) + +You can also specify the `repo` parameter in the form `/` to pin a repository from any user or organization, not just your own. This allows you to showcase repositories you contributed to, regardless of ownership. + +![Readme Card](https://github-stats-extended.vercel.app/api/pin/?username=anuraghazra&repo=statykjs/statyk&show_owner=true&show=prs_authored,prs_commented,prs_reviewed,issues_authored,issues_commented) diff --git a/apps/frontend/src/content/docs/docs/cards/stats.md b/apps/frontend/src/content/docs/docs/cards/stats.md new file mode 100644 index 0000000000000..0522463f8871c --- /dev/null +++ b/apps/frontend/src/content/docs/docs/cards/stats.md @@ -0,0 +1,92 @@ +--- +title: Stats Card +--- + +The stats card shows a summary of your GitHub activity: stars earned, commits, pull requests, issues, contributions and an overall rank. + +:::caution[Warning] +By default, the stats card only shows statistics like stars, commits, and pull requests from public repositories. To show private statistics on the stats card, [allow GitHub-Stats-Extended to access your private contributions](/frontend/docs/fork/#private-contributions-support) or [deploy your own instance](/frontend/docs/deploy/). +::: + +:::note +Available ranks are S (top 1%), A+ (12.5%), A (25%), A- (37.5%), B+ (50%), B (62.5%), B- (75%), C+ (87.5%) and C (everyone). This ranking scheme is based on the [Japanese academic grading](https://wikipedia.org/wiki/Academic_grading_in_Japan) system. The global percentile is calculated as a weighted sum of percentiles for each statistic (number of commits, pull requests, reviews, issues, stars, and followers), based on the cumulative distribution function of the [exponential](https://wikipedia.org/wiki/exponential_distribution) and the [log-normal](https://wikipedia.org/wiki/Log-normal_distribution) distributions. The implementation can be investigated at [calculateRank.ts](https://github.com/stats-organization/github-stats-extended/blob/master/packages/core/src/calculateRank.ts). The circle around the rank shows 100 minus the global percentile. +::: + +## Hiding individual stats + +You can pass a query parameter `&hide=` to hide any specific stats with comma-separated values. + +> Options: `&hide=stars,commits,prs,issues,contribs` + +```md +![Anurag's GitHub stats](https://github-stats-extended.vercel.app/api?username=anuraghazra&hide=contribs,prs) +``` + +## Showing additional individual stats + +You can pass a query parameter `&show=` to show any specific additional stats with comma-separated values. + +> Options: `&show=reviews,discussions_started,discussions_answered,prs_merged,prs_merged_percentage,prs_authored,prs_commented,prs_reviewed,issues_authored,issues_commented` + +```md +![Anurag's GitHub stats](https://github-stats-extended.vercel.app/api?username=anuraghazra&show=reviews,discussions_started,discussions_answered,prs_merged,prs_merged_percentage,prs_authored,prs_commented,prs_reviewed,issues_authored,issues_commented) +``` + +## Showing icons + +To enable icons, you can pass `&show_icons=true` in the query param, like so: + +```md +![Anurag's GitHub stats](https://github-stats-extended.vercel.app/api?username=anuraghazra&show_icons=true) +``` + +## Filtering by repository and owner + +To compute your stats for only a specific repository, you can pass a query parameter `&repo=/`. You can also specify a comma-separated list of multiple repositories, e.g. `&repo=userA/repositoryA,organizationB/repositoryB`. And you can select all repositories owned by specific organizations or users by providing a comma-separated list of owners via the `owner` query parameter, e.g. `&owner=userA,organizationB,organizationC`. The `repo` and `owner` filters are supported by the following items: `commits` (when used with `&include_all_commits=true`), `prs_authored`, `prs_commented`, `prs_reviewed`, `issues_authored` and `issues_commented`. Note that most of these items are not displayed by default, but [you can enable them individually](#showing-additional-individual-stats). + +(Some of these mentioned items are similar to other items which are included by default, e.g. `issues_authored` is similar to `issues`. The difference is how these values are fetched - [via GraphQL or via REST API](https://github.com/anuraghazra/github-readme-stats/discussions/1770#number-of-commits-is-incorrect). The default items use GraphQL, but filtering by repository works better via REST API.) + +Alternatively, you can use the `role` parameter to specify a comma-separated list of [roles](https://docs.github.com/en/graphql/reference/enums#repositoryaffiliation). The stats will include all repositories in which the user has the specified role. By default, only repositories where the user is OWNER will be included, but you could e.g. set `&role=OWNER,ORGANIZATION_MEMBER,COLLABORATOR`. The `role` parameter is supported by all items except the following: `commits` (when used with `&include_all_commits=true`), `prs_authored`, `prs_commented`, `prs_reviewed`, `issues_authored` and `issues_commented`. + +## Showing commits count for specified year + +You can specify a year and fetch only the commits that were made in that year by passing `&commits_year=YYYY` to the parameter. + +```md +![Anurag's GitHub stats](https://github-stats-extended.vercel.app/api?username=anuraghazra&commits_year=2020) +``` + +## Options + +You can customize the appearance and behavior of the stats card using the [common options](/frontend/docs/customization/common-options/) and the exclusive options listed in the table below. + +| Name | Description | Type | Default value | +| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------- | ----------------------------------- | +| `hide` | Hides the [specified items](#hiding-individual-stats) from stats. | string (comma-separated values) | `null` | +| `hide_title` | Hides the title of your stats card. | boolean | `false` | +| `card_width` | Sets the card's width manually. | number | `500px (approx.)` | +| `hide_rank` | Hides the rank and automatically resizes the card width. | boolean | `false` | +| `rank_icon` | Shows alternative rank icon (i.e. `github`, `percentile` or `default`). | enum | `default` | +| `show_icons` | Shows icons near all stats. | boolean | `false` | +| `include_all_commits` | Count total commits instead of just the current year commits. | boolean | `false` | +| `line_height` | Sets the line height between text. | integer | `25` | +| `exclude_repo` | Excludes specified repositories. Affects only the count for "Total Stars Earned". | string (comma-separated values) | `null` | +| `repo` | Count only stats from the specified repositories. Affects only [certain items](#filtering-by-repository-and-owner). | string (comma-separated values) | `null` | +| `owner` | Count only stats from the specified organizations or users. Affects only [certain items](#filtering-by-repository-and-owner). | string (comma-separated values) | `null` | +| `role` | Include repositories where the user has one of the specified [roles](https://docs.github.com/en/graphql/reference/enums#repositoryaffiliation) (OWNER, ORGANIZATION_MEMBER, COLLABORATOR). | string (comma-separated values) | `OWNER` | +| `custom_title` | Sets a custom title for the card. | string | ` GitHub Stats` | +| `text_bold` | Uses bold text. | boolean | `true` | +| `disable_animations` | Disables all animations in the card. | boolean | `false` | +| `ring_color` | Color of the rank circle. | string (hex color) | `2f80ed` | +| `number_format` | Switches between two available formats for displaying the card values: `short` (i.e. `6.6k`) and `long` (i.e. `6626`). | enum | `short` | +| `number_precision` | Enforce the number of digits after the decimal point for `short` number format. Must be an integer between 0 and 2. Will be ignored for `long` number format. | integer (0, 1 or 2) | `null` | +| `show` | Shows [additional items](#showing-additional-individual-stats) on stats card (i.e. `reviews`, `discussions_started`, `discussions_answered`, `prs_merged` or `prs_merged_percentage`. And the following, which support the `repo` and `owner` filters: `prs_authored`, `prs_commented`, `prs_reviewed`, `issues_authored` or `issues_commented`). | string (comma-separated values) | `null` | +| `commits_year` | Filters and counts only commits made in the specified year. | integer _(YYYY)_ | ` (one year to date)` | + +:::caution[Warning] +Custom title should be URI-escaped, as specified in [Percent Encoding](https://en.wikipedia.org/wiki/Percent-encoding) (i.e: `Anurag's GitHub Stats` should become `Anurag%27s%20GitHub%20Stats`). You can use [urlencoder.org](https://www.urlencoder.org/) to help you do this automatically. +::: + +:::note +When hide\_rank=`true`, the minimum card width is 270 px + the title length and padding. +::: diff --git a/apps/frontend/src/content/docs/docs/cards/top-languages.md b/apps/frontend/src/content/docs/docs/cards/top-languages.md new file mode 100644 index 0000000000000..528593eb1fb15 --- /dev/null +++ b/apps/frontend/src/content/docs/docs/cards/top-languages.md @@ -0,0 +1,178 @@ +--- +title: Top Languages Card +--- + +The top languages card shows your most frequently used languages. + +:::caution[Warning] +By default, the language card shows language results only from public repositories. To include languages used in private repositories, [allow GitHub-Stats-Extended to access your private contributions](/frontend/docs/fork/#private-contributions-support) or [deploy your own instance](/frontend/docs/deploy/). +::: + +:::caution[Warning] +This card shows language usage only inside your own non-forked repositories, not depending on who the author of the commits is. It does not include your contributions into another users/organizations repositories. Currently there are no way to get this data from GitHub API. If you want this behavior to be improved you can support [this feature request](https://github.com/orgs/community/discussions/18230) created by [@rickstaa](https://github.com/rickstaa) inside GitHub Community. +::: + +:::caution[Warning] +Currently this card shows data only about first 1000 repositories. This is because GitHub API limitations which cause downtimes of public instances (see [#1471](https://github.com/anuraghazra/github-readme-stats/issues/1471)). In future this behavior will be improved by releasing GitHub action or providing environment variables for user's own instances. +::: + +## Usage + +Copy-paste this code into your readme and change the links. + +Endpoint: `api/top-langs?username=anuraghazra` + +```md +[![Top Langs](https://github-stats-extended.vercel.app/api/top-langs/?username=anuraghazra)](https://github.com/stats-organization/github-stats-extended) +``` + +## Options + +You can customize the appearance and behavior of the top languages card using the [common options](/frontend/docs/customization/common-options/) and exclusive options listed in the table below. + +| Name | Description | Type | Default value | +| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------- | --------------------------------------------------- | +| `hide` | Hides the [specified languages](#hide-individual-languages) from card. | string (comma-separated values) | `null` | +| `hide_title` | Hides the title of your card. | boolean | `false` | +| `layout` | Switches between five available layouts `normal` & `compact` & `donut` & `donut-vertical` & `pie`. | enum | `normal` | +| `card_width` | Sets the card's width manually. | number | `300` | +| `langs_count` | Shows more languages on the card, between 1-20. | integer | `5` for `normal` and `donut`, `6` for other layouts | +| `exclude_repo` | Excludes specified repositories. | string (comma-separated values) | `null` | +| `role` | Include repositories where the user has one of the specified [roles](https://docs.github.com/en/graphql/reference/enums#repositoryaffiliation) (OWNER, ORGANIZATION_MEMBER, COLLABORATOR). | string (comma-separated values) | `OWNER` | +| `custom_title` | Sets a custom title for the card. | string | `Most Used Languages` | +| `disable_animations` | Disables all animations in the card. | boolean | `false` | +| `prog_bar_bg_color` | Background color of the bars. (Applies only to `normal` layout.) | string (hex color) | `#ddd` | +| `hide_progress` | Uses the compact layout option, hides percentages, and removes the bars. | boolean | `false` | +| `hide_values` | Hides language percentages or bytes while keeping the progress bars or chart. | boolean | `false` | +| `size_weight` | Configures language stats algorithm (see [Language stats algorithm](#language-stats-algorithm)). | integer | `1` | +| `count_weight` | Configures language stats algorithm (see [Language stats algorithm](#language-stats-algorithm)). | integer | `0` | +| `stats_format` | Switches between two available formats for language's stats `percentages` and `bytes`. | enum | `percentages` | + +:::caution[Warning] +Language names and custom title should be URI-escaped, as specified in [Percent Encoding](https://en.wikipedia.org/wiki/Percent-encoding) (i.e: `c++` should become `c%2B%2B`, `jupyter notebook` should become `jupyter%20notebook`, `Most Used Languages` should become `Most%20Used%20Languages`, etc.) You can use [urlencoder.org](https://www.urlencoder.org/) to help you do this automatically. +::: + +## Language stats algorithm + +We use the following algorithm to calculate the languages percentages on the language card: + +```js +ranking_index = (byte_count ^ size_weight) * (repo_count ^ count_weight); +``` + +By default, only the byte count is used for determining the languages percentages shown on the language card (i.e. `size_weight=1` and `count_weight=0`). You can, however, use the `&size_weight=` and `&count_weight=` options to weight the language usage calculation. The values must be positive real numbers. [More details about the algorithm can be found here](https://github.com/anuraghazra/github-readme-stats/issues/1600#issuecomment-1046056305). + +- `&size_weight=1&count_weight=0` - _(default)_ Orders by byte count. +- `&size_weight=0.5&count_weight=0.5` - _(recommended)_ Uses both byte and repo count for ranking + - `&size_weight=0&count_weight=1` - Orders by repo count + +```md +![Top Langs](https://github-stats-extended.vercel.app/api/top-langs/?username=anuraghazra&size_weight=0.5&count_weight=0.5) +``` + +If the percentages still look wrong to you, these two comments cover most of the reasons why, and are worth reading before opening an issue: + +- +- + +## Exclude individual repositories + +You can use the `&exclude_repo=repo1,repo2` parameter to exclude individual repositories. + +```md +![Top Langs](https://github-stats-extended.vercel.app/api/top-langs/?username=anuraghazra&exclude_repo=github-readme-stats,anuraghazra.github.io) +``` + +## Hide individual languages + +You can use `&hide=language1,language2` parameter to hide individual languages. + +```md +![Top Langs](https://github-stats-extended.vercel.app/api/top-langs/?username=anuraghazra&hide=javascript,html) +``` + +Language names with spaces or symbols need to be [percent-encoded](#options), so Jupyter Notebook becomes `&hide=jupyter%20notebook` and C++ becomes `&hide=c%2B%2B`. + +## Show more languages + +You can use the `&langs_count=` option to increase or decrease the number of languages shown on the card. Valid values are integers between 1 and 20 (inclusive). By default it was set to `5` for `normal` & `donut` and `6` for other layouts. + +```md +![Top Langs](https://github-stats-extended.vercel.app/api/top-langs/?username=anuraghazra&langs_count=8) +``` + +## Compact Language Card Layout + +You can use the `&layout=compact` option to change the card design. + +```md +![Top Langs](https://github-stats-extended.vercel.app/api/top-langs/?username=anuraghazra&layout=compact) +``` + +## Donut Chart Language Card Layout + +You can use the `&layout=donut` option to change the card design. + +```md +[![Top Langs](https://github-stats-extended.vercel.app/api/top-langs/?username=anuraghazra&layout=donut)](https://github.com/stats-organization/github-stats-extended) +``` + +## Donut Vertical Chart Language Card Layout + +You can use the `&layout=donut-vertical` option to change the card design. + +```md +[![Top Langs](https://github-stats-extended.vercel.app/api/top-langs/?username=anuraghazra&layout=donut-vertical)](https://github.com/stats-organization/github-stats-extended) +``` + +## Pie Chart Language Card Layout + +You can use the `&layout=pie` option to change the card design. + +```md +[![Top Langs](https://github-stats-extended.vercel.app/api/top-langs/?username=anuraghazra&layout=pie)](https://github.com/stats-organization/github-stats-extended) +``` + +## Hide Progress Bars + +You can use the `&hide_progress=true` option to hide the percentages and the progress bars (layout will be automatically set to `compact`). + +```md +![Top Langs](https://github-stats-extended.vercel.app/api/top-langs/?username=anuraghazra&hide_progress=true) +``` + +## Change format of language's stats + +You can use the `&stats_format=bytes` option to display the stats in bytes instead of percentage. + +```md +![Top Langs](https://github-stats-extended.vercel.app/api/top-langs/?username=anuraghazra&stats_format=bytes) +``` + +## Demo + +![Top Langs](https://github-stats-extended.vercel.app/api/top-langs/?username=anuraghazra) + +### Compact layout + +![Top Langs](https://github-stats-extended.vercel.app/api/top-langs/?username=anuraghazra&layout=compact) + +### Donut Chart layout + +[![Top Langs](https://github-stats-extended.vercel.app/api/top-langs/?username=anuraghazra&layout=donut)](https://github-stats-extended.vercel.app/api/top-langs/?username=anuraghazra&layout=donut) + +### Donut Vertical Chart layout + +[![Top Langs](https://github-stats-extended.vercel.app/api/top-langs/?username=anuraghazra&layout=donut-vertical)](https://github-stats-extended.vercel.app/api/top-langs/?username=anuraghazra&layout=donut-vertical) + +### Pie Chart layout + +[![Top Langs](https://github-stats-extended.vercel.app/api/top-langs/?username=anuraghazra&layout=pie)](https://github-stats-extended.vercel.app/api/top-langs/?username=anuraghazra&layout=pie) + +### Hidden progress bars + +[![Top Langs](https://github-stats-extended.vercel.app/api/top-langs/?username=anuraghazra&hide_progress=true)](https://github-stats-extended.vercel.app/api/top-langs/?username=anuraghazra&hide_progress=true) + +### Display bytes instead of percentage + +[![Top Langs](https://github-stats-extended.vercel.app/api/top-langs/?username=anuraghazra&stats_format=bytes)](https://github-stats-extended.vercel.app/api/top-langs/?username=anuraghazra&stats_format=bytes) diff --git a/apps/frontend/src/content/docs/docs/cards/wakatime.md b/apps/frontend/src/content/docs/docs/cards/wakatime.md new file mode 100644 index 0000000000000..6801aa6534c74 --- /dev/null +++ b/apps/frontend/src/content/docs/docs/cards/wakatime.md @@ -0,0 +1,51 @@ +--- +title: WakaTime Card +--- + +The WakaTime card shows how long you have spent coding in each language, taken from your WakaTime profile. + +:::caution[Warning] +Please be aware that we currently only show data from WakaTime profiles that are public. You therefore have to make sure that **BOTH** `Display code time publicly` and `Display languages, editors, os, categories publicly` are enabled. +::: + +:::caution[Warning] +In case you just created a new WakaTime account, then it might take up to 24 hours until your stats will become visible on the WakaTime stats card. +::: + +Change the `?username=` value to your [WakaTime](https://wakatime.com) username. + +```md +[![Alan's WakaTime stats](https://github-stats-extended.vercel.app/api/wakatime?username=alan)](https://wakatime.com/@alan) +``` + +## Options + +You can customize the appearance and behavior of the WakaTime stats card using the [common options](/frontend/docs/customization/common-options/) and exclusive options listed in the table below. + +| Name | Description | Type | Default value | +| -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------- | ---------------- | +| `hide` | Hides the languages specified from the card. | string (comma-separated values) | `null` | +| `hide_title` | Hides the title of your card. | boolean | `false` | +| `card_width` | Sets the card's width manually. | number | `495` | +| `line_height` | Sets the line height between text. | integer | `25` | +| `hide_progress` | Hides the progress bar and percentage. | boolean | `false` | +| `custom_title` | Sets a custom title for the card. | string | `WakaTime Stats` | +| `layout` | Switches between two available layouts `default` & `compact`. | enum | `default` | +| `langs_count` | Limits the number of languages on the card, defaults to all reported languages. | integer | `null` | +| `api_domain` | Sets a custom API domain for the card, e.g. to use services like [Hakatime](https://github.com/mujx/hakatime) or [Wakapi](https://github.com/muety/wakapi) | string | `wakatime.com` | +| `display_format` | Sets the WakaTime stats display format. Choose `time` to display time-based stats or `percent` to show percentages. | enum | `time` | +| `disable_animations` | Disables all animations in the card. | boolean | `false` | + +:::caution[Warning] +Custom title should be URI-escaped, as specified in [Percent Encoding](https://en.wikipedia.org/wiki/Percent-encoding) (i.e: `WakaTime Stats` should become `WakaTime%20Stats`). You can use [urlencoder.org](https://www.urlencoder.org/) to help you do this automatically. +::: + +## Demo + +![Alan's WakaTime stats](https://github-stats-extended.vercel.app/api/wakatime?username=alan) + +![Alan's WakaTime stats](https://github-stats-extended.vercel.app/api/wakatime?username=alan&card_width=315&hide_progress=true) + +### Compact layout + +![Alan's WakaTime stats](https://github-stats-extended.vercel.app/api/wakatime?username=alan&layout=compact) diff --git a/apps/frontend/src/content/docs/docs/customization/aligning-cards.md b/apps/frontend/src/content/docs/docs/customization/aligning-cards.md new file mode 100644 index 0000000000000..16184123b75c2 --- /dev/null +++ b/apps/frontend/src/content/docs/docs/customization/aligning-cards.md @@ -0,0 +1,74 @@ +--- +title: Aligning Cards +--- + +By default, GitHub does not lay out the cards side by side. To do that, you can use such approaches: + +## Stats and top languages cards + + +```html + + + + + + +``` + +
+👀 Show example + + + + + + + + +
+ +## Pinning repositories + +```html + + + + + + +``` + +
+👀 Show example + + + + + + + + +
diff --git a/apps/frontend/src/content/docs/docs/customization/common-options.md b/apps/frontend/src/content/docs/docs/customization/common-options.md new file mode 100644 index 0000000000000..2d86cd82b0f2b --- /dev/null +++ b/apps/frontend/src/content/docs/docs/customization/common-options.md @@ -0,0 +1,34 @@ +--- +title: Common Options +--- + +Every card accepts the options below, on top of the exclusive options listed on its own page. + +| Name | Description | Type | Default value | +| --------------- | -------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- | ------------- | +| `title_color` | Card's title color. | string (hex color) | `2f80ed` | +| `text_color` | Body text color. | string (hex color) | `434d58` | +| `icon_color` | Icons color if available. | string (hex color) | `4c71f2` | +| `border_color` | Card's border color. Does not apply when `hide_border` is enabled. | string (hex color) | `e4e2e2` | +| `bg_color` | Card's background color. | string (hex color or a gradient in the form of _angle,start,end_) | `fffefe` | +| `hide_border` | Hides the card's border. | boolean | `false` | +| `theme` | Name of the theme, choose from [all available themes](/frontend/docs/customization/themes/). | enum | `default` | +| `cache_seconds` | Sets the cache header manually (min: 21600, max: 86400). | integer | `21600` | +| `locale` | Sets the language in the card, you can check full list of available locales [here](/frontend/docs/customization/locales/). | enum | `en` | +| `border_radius` | Corner rounding on the card. | number | `4.5` | + +:::caution[Warning] +We use caching to reduce the load on our servers (see [this discussion](https://github.com/anuraghazra/github-readme-stats/issues/1471#issuecomment-1271551425)). + +Cards generated by [GitHub Stats Extended](https://github-stats-extended.vercel.app/frontend) are cached for a few hours to several days, depending on the current server load. + +If you need your card data to update more frequently, you can [deploy your own instance](/frontend/docs/deploy/) and set the [`CACHE_SECONDS`](/frontend/docs/deploy/#available-environment-variables) environment variable to your preferred value. + +Alternatively, you can use the [GitHub Actions workflow](/frontend/docs/deploy/#github-action) to update your cards on a schedule. +::: + +## Gradient in bg\_color + +You can provide multiple comma-separated values in the bg\_color option to render a gradient with the following format: + + &bg_color=DEG,COLOR1,COLOR2,COLOR3...COLOR10 diff --git a/apps/frontend/src/content/docs/docs/customization/locales.md b/apps/frontend/src/content/docs/docs/customization/locales.md new file mode 100644 index 0000000000000..e05919febe326 --- /dev/null +++ b/apps/frontend/src/content/docs/docs/customization/locales.md @@ -0,0 +1,73 @@ +--- +title: Locales +--- + +Here is a list of all available locales: + + + +
+ +| Code | Locale | +| ------- | ---------------- | +| `ar` | Arabic | +| `az` | Azerbaijani | +| `be` | Belarusian | +| `bn` | Bengali | +| `bg` | Bulgarian | +| `my` | Burmese | +| `ca` | Catalan | +| `cn` | Chinese | +| `zh-tw` | Chinese (Taiwan) | +| `cs` | Czech | +| `nl` | Dutch | +| `en` | English | +| `fil` | Filipino | +| `fi` | Finnish | +| `fr` | French | +| `de` | German | + + + +| Code | Locale | +| ------- | --------------------- | +| `el` | Greek | +| `he` | Hebrew | +| `hi` | Hindi | +| `hu` | Hungarian | +| `id` | Indonesian | +| `it` | Italian | +| `ja` | Japanese | +| `kr` | Korean | +| `ml` | Malayalam | +| `np` | Nepali | +| `no` | Norwegian | +| `fa` | Persian (Farsi) | +| `pl` | Polish | +| `pt-br` | Portuguese (Brazil) | +| `pt-pt` | Portuguese (Portugal) | +| `ro` | Romanian | + + + +| Code | Locale | +| --------- | ------------------ | +| `ru` | Russian | +| `sa` | Sanskrit | +| `sr` | Serbian (Cyrillic) | +| `sr-latn` | Serbian (Latin) | +| `sk` | Slovak | +| `es` | Spanish | +| `sw` | Swahili | +| `se` | Swedish | +| `ta` | Tamil | +| `th` | Thai | +| `tr` | Turkish | +| `uk-ua` | Ukrainian | +| `ur` | Urdu | +| `uz` | Uzbek | +| `vi` | Vietnamese | + +
+ +If we don't support your language, please consider contributing! You can find more information about how to do it in our [contributing guidelines](https://github.com/stats-organization/github-stats-extended/blob/master/.github/CONTRIBUTING.md#translations-contribution). diff --git a/apps/frontend/src/content/docs/docs/themes.md b/apps/frontend/src/content/docs/docs/customization/themes.md similarity index 100% rename from apps/frontend/src/content/docs/docs/themes.md rename to apps/frontend/src/content/docs/docs/customization/themes.md diff --git a/apps/frontend/src/content/docs/docs/customization/theming.md b/apps/frontend/src/content/docs/docs/customization/theming.md new file mode 100644 index 0000000000000..f425251a579aa --- /dev/null +++ b/apps/frontend/src/content/docs/docs/customization/theming.md @@ -0,0 +1,103 @@ +--- +title: Theming +--- + +With inbuilt themes, you can customize the look of the card without doing any [manual customization](/frontend/docs/customization/common-options/). + +Use `&theme=THEME_NAME` parameter like so : + +```md +![Anurag's GitHub stats](https://github-stats-extended.vercel.app/api?username=anuraghazra&show_icons=true&theme=radical) +``` + +## All inbuilt themes + +GitHub Stats Extended comes with several built-in themes (e.g. `dark`, `radical`, `merko`, `gruvbox`, `tokyonight`, `onedark`, `cobalt`, `synthwave`, `highcontrast`, `dracula`). + +GitHub Stats Extended Themes + +You can look at a preview for [all available themes](/frontend/docs/customization/themes/) or checkout the [theme config file](https://github.com/stats-organization/github-stats-extended/blob/master/packages/core/src/themes/index.ts). Please note that we paused the addition of new themes to decrease maintenance efforts; all pull requests related to new themes will be closed. + +## Responsive Card Theme + +[![Anurag's GitHub stats-Dark](https://github-stats-extended.vercel.app/api?username=anuraghazra&show_icons=true&theme=dark#gh-dark-mode-only)](https://github.com/stats-organization/github-stats-extended#responsive-card-theme#gh-dark-mode-only) +[![Anurag's GitHub stats-Light](https://github-stats-extended.vercel.app/api?username=anuraghazra&show_icons=true&theme=default#gh-light-mode-only)](https://github.com/stats-organization/github-stats-extended#responsive-card-theme#gh-light-mode-only) + +Since GitHub will re-upload the cards and serve them from their [CDN](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/about-anonymized-urls), we can not infer the browser/GitHub theme on the server side. There are, however, four methods you can use to create dynamics themes on the client side. + +### Use GitHub's new media feature (recommended) + +You can use [GitHub's new media feature](https://github.blog/changelog/2022-05-19-specify-theme-context-for-images-in-markdown-beta/) in HTML to specify whether to display images for light or dark themes. This is done using the HTML `` element in combination with the `prefers-color-scheme` media feature. + + +```html + + + + + +``` + +
+👀 Show example + + + + + + + +
+ +### Use GitHub's theme context tag + +You can use [GitHub's theme context](https://github.blog/changelog/2021-11-24-specify-theme-context-for-images-in-markdown/) tags to switch the theme based on the user GitHub theme automatically. This is done by appending `#gh-dark-mode-only` or `#gh-light-mode-only` to the end of an image URL. This tag will define whether the image specified in the markdown is only shown to viewers using a light or a dark GitHub theme: + +```md +[![Anurag's GitHub stats-Dark](https://github-stats-extended.vercel.app/api?username=anuraghazra&show_icons=true&theme=dark#gh-dark-mode-only)](https://github.com/stats-organization/github-stats-extended#gh-dark-mode-only) +[![Anurag's GitHub stats-Light](https://github-stats-extended.vercel.app/api?username=anuraghazra&show_icons=true&theme=default#gh-light-mode-only)](https://github.com/stats-organization/github-stats-extended#gh-light-mode-only) +``` + +
+👀 Show example + +[![Anurag's GitHub stats-Dark](https://github-stats-extended.vercel.app/api?username=anuraghazra&show_icons=true&theme=dark#gh-dark-mode-only)](https://github.com/stats-organization/github-stats-extended#gh-dark-mode-only) +[![Anurag's GitHub stats-Light](https://github-stats-extended.vercel.app/api?username=anuraghazra&show_icons=true&theme=default#gh-light-mode-only)](https://github.com/stats-organization/github-stats-extended#gh-light-mode-only) + +
+ +### Use the transparent theme + +We have included a `transparent` theme that has a transparent background. This theme is optimized to look good on GitHub's dark and light default themes. You can enable this theme using the `&theme=transparent` parameter like so: + +```md +![Anurag's GitHub stats](https://github-stats-extended.vercel.app/api?username=anuraghazra&show_icons=true&theme=transparent) +``` + +
+👀 Show example + +![Anurag's GitHub stats](https://github-stats-extended.vercel.app/api?username=anuraghazra&show_icons=true&theme=transparent) + +
+ +### Add transparent alpha channel to a themes bg\_color + +You can use the `bg_color` parameter to make any of [the available themes](/frontend/docs/customization/themes/) transparent. This is done by setting the `bg_color` to a color with a transparent alpha channel (i.e. `bg_color=00000000`): + +```md +![Anurag's GitHub stats](https://github-stats-extended.vercel.app/api?username=anuraghazra&show_icons=true&bg_color=00000000) +``` + +
+👀 Show example + +![Anurag's GitHub stats](https://github-stats-extended.vercel.app/api?username=anuraghazra&show_icons=true&bg_color=00000000) + +
diff --git a/apps/frontend/src/content/docs/docs/demos.md b/apps/frontend/src/content/docs/docs/demos.md new file mode 100644 index 0000000000000..db91a86d299f5 --- /dev/null +++ b/apps/frontend/src/content/docs/docs/demos.md @@ -0,0 +1,81 @@ +--- +title: Demo Gallery +--- + +## Default + +![Anurag's GitHub stats](https://github-stats-extended.vercel.app/api?username=anuraghazra) + +## Hiding specific stats + +![Anurag's GitHub stats](https://github-stats-extended.vercel.app/api?username=anuraghazra&hide=contribs,issues) + +## Showing additional stats + +![Anurag's GitHub stats](https://github-stats-extended.vercel.app/api?username=anuraghazra&show_icons=true&show=reviews,discussions_started,discussions_answered,prs_merged,prs_merged_percentage,prs_commented,prs_reviewed,issues_commented) + +## Showing stats for a specific repository + +![Anurag's GitHub stats for anuraghazra/github-readme-stats](https://github-stats-extended.vercel.app/api?username=anuraghazra&repo=anuraghazra/github-readme-stats&hide=prs,issues,stars,commits,contribs&show=prs_authored,prs_commented,prs_reviewed,issues_authored,issues_commented&hide_rank=true&custom_title=Anurag%27s%20Stats%20for%20github-readme-stats&card_width=370) + +## Showing stats for a specific organization + +![Anurag's GitHub stats for razorpay](https://github-stats-extended.vercel.app/api?username=anuraghazra&owner=razorpay&hide=prs,issues,stars,commits,contribs&show=prs_authored,prs_commented,prs_reviewed,issues_authored,issues_commented&hide_rank=true&custom_title=Anurag%27s%20Stats%20for%20razorpay&card_width=370) + +## Showing icons + +![Anurag's GitHub stats](https://github-stats-extended.vercel.app/api?username=anuraghazra&hide=issues&show_icons=true) + +## Shows GitHub logo instead rank level + +![Anurag's GitHub stats](https://github-stats-extended.vercel.app/api?username=anuraghazra&rank_icon=github) + +## Shows user rank percentile instead of rank level + +![Anurag's GitHub stats](https://github-stats-extended.vercel.app/api?username=anuraghazra&rank_icon=percentile) + +## Customize Border Color + +![Anurag's GitHub stats](https://github-stats-extended.vercel.app/api?username=anuraghazra&border_color=2e4058) + +## Include All Commits + +![Anurag's GitHub stats](https://github-stats-extended.vercel.app/api?username=anuraghazra&include_all_commits=true) + +## Themes + +Choose from any of the [default themes](/frontend/docs/customization/themes/) + +![Anurag's GitHub stats](https://github-stats-extended.vercel.app/api?username=anuraghazra&show_icons=true&theme=radical) + +## Gradient + +![Anurag's GitHub stats](https://github-stats-extended.vercel.app/api?username=anuraghazra&bg_color=30,e96443,904e95&title_color=fff&text_color=fff) + +## Customizing stats card + +![Anurag's GitHub stats](https://github-stats-extended.vercel.app/api/?username=anuraghazra&show_icons=true&title_color=fff&icon_color=79ff97&text_color=9f9f9f&bg_color=151515) + +## Setting card locale + +![Anurag's GitHub stats](https://github-stats-extended.vercel.app/api/?username=anuraghazra&locale=es) + +## Customizing repo card + +![Customized Card](https://github-stats-extended.vercel.app/api/pin?username=anuraghazra&repo=github-readme-stats&title_color=fff&icon_color=f9f9f9&text_color=9f9f9f&bg_color=151515) + +## Gist card + +![Gist Card](https://github-stats-extended.vercel.app/api/gist?id=bbfce31e0217a3689c8d961a356cb10d) + +## Customizing gist card + +![Gist Card](https://github-stats-extended.vercel.app/api/gist?id=bbfce31e0217a3689c8d961a356cb10d&theme=calm) + +## Top languages + +![Top Langs](https://github-stats-extended.vercel.app/api/top-langs/?username=anuraghazra) + +## WakaTime card + +![Alan's WakaTime stats](https://github-stats-extended.vercel.app/api/wakatime?username=alan) diff --git a/apps/frontend/src/content/docs/docs/fork.md b/apps/frontend/src/content/docs/docs/fork.md index 4e69b00890d0b..fac00b26154fa 100644 --- a/apps/frontend/src/content/docs/docs/fork.md +++ b/apps/frontend/src/content/docs/docs/fork.md @@ -20,7 +20,7 @@ The frontend is based on [GitHub Trends](https://github.com/avgupta456/github-tr To include stars from repos which are not owned by you, but where you are a collaborator or organization member, add `&role=OWNER,ORGANIZATION_MEMBER,COLLABORATOR` to your stats card url. To include such repos in your language stats, you can also add the same parameter to your top languages card url. -See [here](/frontend/docs/advanced_documentation/#filtering-by-repository-and-owner) for full feature documentation. +See [here](/frontend/docs/cards/stats/#filtering-by-repository-and-owner) for full feature documentation. The resolution of this most requested feature in github-readme-stats was [originally implemented](https://github.com/anuraghazra/github-readme-stats/issues/1#issuecomment-855681098) by [@developStorm](https://github.com/developStorm). @@ -48,7 +48,7 @@ GitHub-Stats-Extended adds the ability to show contribution stats for specific r Especially for regular contributors in open source projects it might make sense to display an overview of their own contributions to these projects on their GitHub profile. -See [here](/frontend/docs/advanced_documentation/#filtering-by-repository-and-owner) for full feature documentation. +See [here](/frontend/docs/cards/stats/#filtering-by-repository-and-owner) for full feature documentation. --- diff --git a/apps/frontend/src/content/docs/docs/index.md b/apps/frontend/src/content/docs/docs/index.md index eb07f15064151..6ccd292c9d36a 100644 --- a/apps/frontend/src/content/docs/docs/index.md +++ b/apps/frontend/src/content/docs/docs/index.md @@ -4,7 +4,7 @@ title: GitHub Stats Extended Dynamically generate GitHub stats for your READMEs. -GitHub-Stats-Extended is the [extended, actively maintained successor](/frontend/docs/fork/) of [github-readme-stats](https://github.com/anuraghazra/github-readme-stats). It generates stats cards about your GitHub contributions, your top languages and more, which you can [customize](/frontend/docs/advanced_documentation/) through a large set of parameters. +GitHub-Stats-Extended is the [extended, actively maintained successor](/frontend/docs/fork/) of [github-readme-stats](https://github.com/anuraghazra/github-readme-stats). It generates stats cards about your GitHub contributions, your top languages and more, which you can [customize](/frontend/docs/customization/common-options/) through a large set of parameters. -### Top Languages Card +### [Top Languages Card](/frontend/docs/cards/top-languages/) -### WakaTime Card +### [WakaTime Card](/frontend/docs/cards/wakatime/) -### Repo Card +### [Repo Card](/frontend/docs/cards/repo-pin/) -### Gist Card +### [Gist Card](/frontend/docs/cards/gist-pin/) For more customization options check the{" "} customization documentation diff --git a/apps/frontend/src/wizard/Home/stages/Theme.tsx b/apps/frontend/src/wizard/Home/stages/Theme.tsx index 9e4da5d95b659..fc3ab86d570c4 100644 --- a/apps/frontend/src/wizard/Home/stages/Theme.tsx +++ b/apps/frontend/src/wizard/Home/stages/Theme.tsx @@ -100,7 +100,7 @@ export function ThemeStage({
{"For more theme options check the "} customization documentation diff --git a/apps/frontend/src/wizard/docsUrl.ts b/apps/frontend/src/wizard/docsUrl.ts index 0be80eeef1dc6..14cd8a0dd4b0f 100644 --- a/apps/frontend/src/wizard/docsUrl.ts +++ b/apps/frontend/src/wizard/docsUrl.ts @@ -4,13 +4,18 @@ * * @todo extract them statically from the config or other astro exports to build this dynamically. */ -type DocPage = "advanced_documentation" | "deploy" | "fork" | "themes"; +type DocPage = + | "cards/stats" + | "customization/common-options" + | "customization/theming" + | "customization/themes" + | "deploy" + | "fork"; /** * @param page - Slug of the target page. - * @param hash - Heading to scroll to, without the `#`. */ -export function docsUrl(page: DocPage, hash?: string): string { +export function docsUrl(page: DocPage): string { const base = import.meta.env.BASE_URL.replace(/\/$/, ""); - return `${base}/docs/${page}/${hash ? `#${hash}` : ""}`; + return `${base}/docs/${page}/`; } diff --git a/packages/core/scripts/generate-theme-readme.js b/packages/core/scripts/generate-theme-readme.js index fafd030b0c921..62bb2b1e101de 100644 --- a/packages/core/scripts/generate-theme-readme.js +++ b/packages/core/scripts/generate-theme-readme.js @@ -5,7 +5,8 @@ import { format, resolveConfig } from "prettier"; import { themes } from "../src/themes/index.ts"; // The docs live with the site that publishes them. -const TARGET_FILE = "../../apps/frontend/src/content/docs/docs/themes.md"; +const TARGET_FILE = + "../../apps/frontend/src/content/docs/docs/customization/themes.md"; const REPO_CARD_LINKS_FLAG = ""; const STAT_CARD_LINKS_FLAG = ""; From ae21ab528de389fa09db6d34d9bae399434b66ea Mon Sep 17 00:00:00 2001 From: Marco Pasqualetti Date: Wed, 19 Aug 2026 14:18:28 +0200 Subject: [PATCH 05/14] chore(frontend): use caret ranges for astro and starlight --- apps/frontend/package.json | 4 ++-- pnpm-lock.yaml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/frontend/package.json b/apps/frontend/package.json index 8192723d27dbd..3c1bf9f115620 100644 --- a/apps/frontend/package.json +++ b/apps/frontend/package.json @@ -14,12 +14,12 @@ }, "dependencies": { "@astrojs/react": "^6.0.2", - "@astrojs/starlight": "0.41.7", + "@astrojs/starlight": "^0.41.7", "@reduxjs/toolkit": "^2.12.0", "@stats-organization/github-readme-stats-backend": "workspace:^", "@stats-organization/github-readme-stats-core": "workspace:^", "@tailwindcss/vite": "^4.3.3", - "astro": "7.2.0", + "astro": "^7.2.0", "axios": "catalog:default", "axios-cache-interceptor": "^1.12.0", "daisyui": "^5.6.18", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e1d449279590e..6fba5d942ac8c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -116,7 +116,7 @@ importers: specifier: ^6.0.2 version: 6.0.2(@types/node@24.13.3)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(esbuild@0.28.2)(jiti@2.7.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(terser@5.49.2)(yaml@2.9.0) '@astrojs/starlight': - specifier: 0.41.7 + specifier: ^0.41.7 version: 0.41.7(@astrojs/markdown-remark@7.2.2)(astro@7.2.0(@astrojs/markdown-remark@7.2.2)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@types/node@24.13.3)(jiti@2.7.0)(terser@5.49.2)(yaml@2.9.0))(typescript@6.0.3) '@reduxjs/toolkit': specifier: ^2.12.0 @@ -131,7 +131,7 @@ importers: specifier: ^4.3.3 version: 4.3.3(vite@8.1.5(@types/node@24.13.3)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.49.2)(yaml@2.9.0)) astro: - specifier: 7.2.0 + specifier: ^7.2.0 version: 7.2.0(@astrojs/markdown-remark@7.2.2)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@types/node@24.13.3)(jiti@2.7.0)(terser@5.49.2)(yaml@2.9.0) axios: specifier: catalog:default From 743c84cc6393681ab2c119b0cd0d3736a9e3dc6b Mon Sep 17 00:00:00 2001 From: Marco Pasqualetti Date: Wed, 19 Aug 2026 14:18:28 +0200 Subject: [PATCH 06/14] chore(frontend): replace deprecated rollupOptions with rolldownOptions --- apps/frontend/astro.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/frontend/astro.config.ts b/apps/frontend/astro.config.ts index e7e909bb8d997..5724e77532e9c 100644 --- a/apps/frontend/astro.config.ts +++ b/apps/frontend/astro.config.ts @@ -94,7 +94,7 @@ export default defineConfig({ }, // The backend code the wizard reuses imports `pg`, which never runs in the browser. build: { - rollupOptions: { + rolldownOptions: { external: ["pg"], }, }, From 1e20e81920d574e9b71d37e53d96a94454e64844 Mon Sep 17 00:00:00 2001 From: Marco Pasqualetti Date: Wed, 19 Aug 2026 14:18:28 +0200 Subject: [PATCH 07/14] chore(frontend): drop redundant DOM.Iterable lib --- apps/frontend/tsconfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/frontend/tsconfig.json b/apps/frontend/tsconfig.json index 744c8368fd6f8..0cf53176a31ab 100644 --- a/apps/frontend/tsconfig.json +++ b/apps/frontend/tsconfig.json @@ -10,7 +10,7 @@ "playwright.config.ts" ], "compilerOptions": { - "lib": ["DOM", "DOM.Iterable", "ESNext"], + "lib": ["DOM", "ESNext"], "module": "esnext", "moduleResolution": "bundler", // Starlight ships TypeScript rather than declarations, and its own sources trip From 1f6755cf9619966caf35ffcdb2a637d6db10e8ec Mon Sep 17 00:00:00 2001 From: Marco Pasqualetti Date: Wed, 19 Aug 2026 14:18:28 +0200 Subject: [PATCH 08/14] docs(contributing): link the published docs instead of the markdown sources --- .github/CONTRIBUTING.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 17fece05719b0..ad0b1fafaca0b 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -10,7 +10,7 @@ pnpm run build:packages pnpm run dev:frontend ``` -The easiest way to run and test the project is to deploy it to Vercel as described in the [deployment guide](../apps/frontend/src/content/docs/docs/deploy.md). +The easiest way to run and test the project is to deploy it to Vercel as described in the [deployment guide](https://github-stats-extended.vercel.app/frontend/docs/deploy/). ## Tests @@ -46,13 +46,13 @@ Never edit the generated files by hand — change the `.graphql` file and regene ## Themes Contribution -We have stopped the addition of new themes to decrease maintenance efforts. If you are considering contributing your theme just because you are using it personally, then instead of adding it to our theme collection, you can use card [customization options](../apps/frontend/src/content/docs/docs/customization/common-options.md). +We have stopped the addition of new themes to decrease maintenance efforts. If you are considering contributing your theme just because you are using it personally, then instead of adding it to our theme collection, you can use card [customization options](https://github-stats-extended.vercel.app/frontend/docs/customization/common-options/). ## Translations Contribution -GitHub-Stats-Extended supports multiple languages. If we are missing your language, you can contribute it! You can check the currently supported languages [here](../apps/frontend/src/content/docs/docs/customization/locales.md). +GitHub-Stats-Extended supports multiple languages. If we are missing your language, you can contribute it! You can check the currently supported languages [here](https://github-stats-extended.vercel.app/frontend/docs/customization/locales/). -To contribute your language you need to edit the [backend/src/translations.js](../backend/src/translations.js) file and add a new property to each object where the key is the language code in [ISO 639-1 standard](https://www.andiamo.co.uk/resources/iso-language-codes/) and the value is the translated string. +To contribute your language you need to edit the [packages/core/src/translations.ts](../packages/core/src/translations.ts) file and add a new property to each object where the key is the language code in [ISO 639-1 standard](https://www.andiamo.co.uk/resources/iso-language-codes/) and the value is the translated string. ## Any contributions you make will be under the MIT Software License From 38401f615670822294b55b6618aeccd49f368aa1 Mon Sep 17 00:00:00 2001 From: Marco Pasqualetti Date: Wed, 19 Aug 2026 14:18:29 +0200 Subject: [PATCH 09/14] fix(frontend): make the delete modal backdrop dim the whole page --- apps/frontend/src/index.css | 10 ++++++---- apps/frontend/src/pages/index.astro | 2 +- .../Home/stages/Login/LoginAccountDeleteModal.tsx | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/apps/frontend/src/index.css b/apps/frontend/src/index.css index 725c44119a73b..14ae121a249c8 100644 --- a/apps/frontend/src/index.css +++ b/apps/frontend/src/index.css @@ -14,11 +14,13 @@ Override background color in dark mode with GitHub's dark mode background color. name: "light"; --color-primary: #2f80ed; --color-error: var(--color-red-700); + --color-error-content: #ffffff; } @plugin "daisyui/theme" { name: "dark"; --color-primary: #2f80ed; - --color-error: var(--color-red-900); + --color-error: var(--color-red-400); + --color-error-content: #0d1117; --color-base-100: #0d1117; } @@ -33,10 +35,10 @@ Override background color in dark mode with GitHub's dark mode background color. * Everything here is nested so it reaches only the wizard's own nodes: * the page background, font and reset belong to the site, which shares this page. */ +/* Scoping only: no background, so overlays mounted on `body` stay see-through. */ .wizard { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; - background-color: var(--color-base-100); color: var(--color-base-content); button { @@ -95,10 +97,10 @@ Override background color in dark mode with GitHub's dark mode background color. background-color: color-mix(in oklab, var(--color-base-100), #888 60%); } .btn-error { - color: var(--color-neutral-content); + color: var(--color-error-content); } .btn-error:hover { - color: var(--color-neutral-content); + color: var(--color-error-content); background-color: color-mix(in oklab, var(--color-error), #f00 40%); } diff --git a/apps/frontend/src/pages/index.astro b/apps/frontend/src/pages/index.astro index 042d8c4ba0d2e..ef925b121e311 100644 --- a/apps/frontend/src/pages/index.astro +++ b/apps/frontend/src/pages/index.astro @@ -16,7 +16,7 @@ import "../index.css"; }} > {/* `not-content` opts out of the prose spacing Starlight applies to markdown. */} -
+
{/* Only this page loads these rules, so the docs keep Starlight's reading column. */} diff --git a/apps/frontend/src/wizard/Home/stages/Login/LoginAccountDeleteModal.tsx b/apps/frontend/src/wizard/Home/stages/Login/LoginAccountDeleteModal.tsx index 2f8dac17f7071..e94e374522c61 100644 --- a/apps/frontend/src/wizard/Home/stages/Login/LoginAccountDeleteModal.tsx +++ b/apps/frontend/src/wizard/Home/stages/Login/LoginAccountDeleteModal.tsx @@ -43,7 +43,7 @@ export function LoginAccountDeleteModal( return createPortal( // `wizard` because this mounts on `body`, outside the element the app's styles are scoped to. -
+
Date: Wed, 19 Aug 2026 14:18:29 +0200 Subject: [PATCH 10/14] fix(frontend): restore the favicon, touch icon and web manifest --- apps/frontend/astro.config.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/apps/frontend/astro.config.ts b/apps/frontend/astro.config.ts index 5724e77532e9c..eb339e80e6374 100644 --- a/apps/frontend/astro.config.ts +++ b/apps/frontend/astro.config.ts @@ -32,6 +32,19 @@ export default defineConfig({ title: "GitHub Stats Extended", description: "Dynamically generate GitHub stats for your READMEs.", logo: { src: "./src/assets/appLogo64.png", alt: "" }, + // Starlight defaults to a favicon.svg this project does not ship. + favicon: "/favicon.ico", + head: [ + { + tag: "link", + attrs: { rel: "apple-touch-icon", href: `${base}/logo192.png` }, + }, + { + tag: "link", + attrs: { rel: "manifest", href: `${base}/manifest.json` }, + }, + { tag: "meta", attrs: { name: "theme-color", content: "#000000" } }, + ], social: [ { icon: "github", From bda98b35ebd3b2bf43e3e3a38a14e9a73bf70b11 Mon Sep 17 00:00:00 2001 From: Marco Pasqualetti Date: Wed, 19 Aug 2026 14:18:29 +0200 Subject: [PATCH 11/14] docs(customization): scope the caching note to the public deployment --- .../src/content/docs/docs/customization/common-options.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/frontend/src/content/docs/docs/customization/common-options.md b/apps/frontend/src/content/docs/docs/customization/common-options.md index 2d86cd82b0f2b..ed1014df484ef 100644 --- a/apps/frontend/src/content/docs/docs/customization/common-options.md +++ b/apps/frontend/src/content/docs/docs/customization/common-options.md @@ -18,9 +18,12 @@ Every card accepts the options below, on top of the exclusive options listed on | `border_radius` | Corner rounding on the card. | number | `4.5` | :::caution[Warning] -We use caching to reduce the load on our servers (see [this discussion](https://github.com/anuraghazra/github-readme-stats/issues/1471#issuecomment-1271551425)). +This section applies only to our public deployment at [github-stats-extended.vercel.app](https://github-stats-extended.vercel.app/frontend). Self-hosted instances, the [GitHub Actions workflow](/frontend/docs/deploy/#github-action) and the npm package may behave differently. -Cards generated by [GitHub Stats Extended](https://github-stats-extended.vercel.app/frontend) are cached for a few hours to several days, depending on the current server load. +We use caching there to reduce the load on our servers (see [this discussion](https://github.com/anuraghazra/github-readme-stats/issues/1471#issuecomment-1271551425)). + +Cards are cached for a few hours to several days. +The maintainers set that duration by hand and adjust it as load changes. If you need your card data to update more frequently, you can [deploy your own instance](/frontend/docs/deploy/) and set the [`CACHE_SECONDS`](/frontend/docs/deploy/#available-environment-variables) environment variable to your preferred value. From 338eeed26a2c780f9c9f5c4e6288cc290a24328e Mon Sep 17 00:00:00 2001 From: Marco Pasqualetti Date: Wed, 19 Aug 2026 14:18:29 +0200 Subject: [PATCH 12/14] docs(theming): stop showing GitHub-only theme switching as a live example --- .../docs/docs/customization/theming.md | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/apps/frontend/src/content/docs/docs/customization/theming.md b/apps/frontend/src/content/docs/docs/customization/theming.md index f425251a579aa..5b5bf519664a0 100644 --- a/apps/frontend/src/content/docs/docs/customization/theming.md +++ b/apps/frontend/src/content/docs/docs/customization/theming.md @@ -20,8 +20,13 @@ You can look at a preview for [all available themes](/frontend/docs/customizatio ## Responsive Card Theme -[![Anurag's GitHub stats-Dark](https://github-stats-extended.vercel.app/api?username=anuraghazra&show_icons=true&theme=dark#gh-dark-mode-only)](https://github.com/stats-organization/github-stats-extended#responsive-card-theme#gh-dark-mode-only) -[![Anurag's GitHub stats-Light](https://github-stats-extended.vercel.app/api?username=anuraghazra&show_icons=true&theme=default#gh-light-mode-only)](https://github.com/stats-organization/github-stats-extended#responsive-card-theme#gh-light-mode-only) + + + Anurag's GitHub stats + Since GitHub will re-upload the cards and serve them from their [CDN](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/about-anonymized-urls), we can not infer the browser/GitHub theme on the server side. There are, however, four methods you can use to create dynamics themes on the client side. @@ -64,13 +69,10 @@ You can use [GitHub's theme context](https://github.blog/changelog/2021-11-24-sp [![Anurag's GitHub stats-Light](https://github-stats-extended.vercel.app/api?username=anuraghazra&show_icons=true&theme=default#gh-light-mode-only)](https://github.com/stats-organization/github-stats-extended#gh-light-mode-only) ``` -
-👀 Show example - -[![Anurag's GitHub stats-Dark](https://github-stats-extended.vercel.app/api?username=anuraghazra&show_icons=true&theme=dark#gh-dark-mode-only)](https://github.com/stats-organization/github-stats-extended#gh-dark-mode-only) -[![Anurag's GitHub stats-Light](https://github-stats-extended.vercel.app/api?username=anuraghazra&show_icons=true&theme=default#gh-light-mode-only)](https://github.com/stats-organization/github-stats-extended#gh-light-mode-only) - -
+:::note +`#gh-dark-mode-only` and `#gh-light-mode-only` are honoured by GitHub when it renders +markdown, so there is no live example here: outside GitHub both images are shown. +::: ### Use the transparent theme From 7bdf536dab88c5526b6022b3f123ff7395ae771f Mon Sep 17 00:00:00 2001 From: Marco Pasqualetti Date: Wed, 19 Aug 2026 14:38:12 +0200 Subject: [PATCH 13/14] docs(aligning-cards): render the examples side by side --- .../docs/docs/customization/aligning-cards.md | 36 +++++++++---------- apps/frontend/src/styles/starlight-theme.css | 21 +++++++++++ 2 files changed, 37 insertions(+), 20 deletions(-) diff --git a/apps/frontend/src/content/docs/docs/customization/aligning-cards.md b/apps/frontend/src/content/docs/docs/customization/aligning-cards.md index 16184123b75c2..e8bbb1e8c11a7 100644 --- a/apps/frontend/src/content/docs/docs/customization/aligning-cards.md +++ b/apps/frontend/src/content/docs/docs/customization/aligning-cards.md @@ -27,20 +27,14 @@ By default, GitHub does not lay out the cards side by side. To do that, you can
👀 Show example - - - - - - +
@@ -64,11 +58,13 @@ By default, GitHub does not lay out the cards side by side. To do that, you can
👀 Show example - - - - - - +
diff --git a/apps/frontend/src/styles/starlight-theme.css b/apps/frontend/src/styles/starlight-theme.css index 7e3c2ed7d7f62..ea0852ae98102 100644 --- a/apps/frontend/src/styles/starlight-theme.css +++ b/apps/frontend/src/styles/starlight-theme.css @@ -18,3 +18,24 @@ --sl-color-bg: #0d1117; --sl-color-bg-sidebar: #0d1117; } + +/* Prose styles render images as blocks, so the row is explicit. */ +.card-row { + display: flex; + flex-wrap: nowrap; + gap: 0.5rem; + align-items: stretch; +} + +.card-row a { + flex: 1 1 0; + min-width: 0; + display: flex; +} + +.card-row img { + width: 100%; + height: 100%; + object-fit: contain; + object-position: center; +} From bc40152cac574441b478aedcdd9609b722c7f810 Mon Sep 17 00:00:00 2001 From: Marco Pasqualetti Date: Wed, 19 Aug 2026 14:38:12 +0200 Subject: [PATCH 14/14] chore: move the theme readme generator to the repo-level scripts folder --- .github/workflows/generate-theme-readme.yml | 2 +- package.json | 3 ++- packages/core/package.json | 1 - .../core/scripts => scripts}/generate-theme-readme.js | 9 ++++++--- 4 files changed, 9 insertions(+), 6 deletions(-) rename {packages/core/scripts => scripts}/generate-theme-readme.js (94%) diff --git a/.github/workflows/generate-theme-readme.yml b/.github/workflows/generate-theme-readme.yml index 204e6b65e81c2..dd6b873cb600c 100644 --- a/.github/workflows/generate-theme-readme.yml +++ b/.github/workflows/generate-theme-readme.yml @@ -37,7 +37,7 @@ jobs: uses: ./.github/actions/install-dependencies - name: Generate readme - run: pnpm --filter ./packages/core/ run generate-theme-readme + run: pnpm run generate-theme-readme - name: Create Pull Request if themes README has changed uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1 diff --git a/package.json b/package.json index 51fe436391b1e..d01d92062ed89 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,8 @@ "lint:eslint:fix": "eslint --fix", "lint:knip": "knip", "lint:deps": "node scripts/assert-deduped.ts", - "typecheck": "turbo run typecheck && tsc -p tsconfig.scripts.json" + "typecheck": "turbo run typecheck && tsc -p tsconfig.scripts.json", + "generate-theme-readme": "node scripts/generate-theme-readme" }, "lint-staged": { "*.{js,jsx,ts,tsx,css,json,jsonc,yaml,yml}": "prettier --write" diff --git a/packages/core/package.json b/packages/core/package.json index 56397184a45e0..2db995d78bd94 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -77,7 +77,6 @@ "typecheck": "tsc -p tsconfig.typecheck.json", "generate-graphql-types": "node scripts/generate-graphql-types", "check-graphql-types": "node scripts/generate-graphql-types --check", - "generate-theme-readme": "node scripts/generate-theme-readme", "generate-language-colors": "node scripts/generate-language-colors" }, "devDependencies": { diff --git a/packages/core/scripts/generate-theme-readme.js b/scripts/generate-theme-readme.js similarity index 94% rename from packages/core/scripts/generate-theme-readme.js rename to scripts/generate-theme-readme.js index 62bb2b1e101de..06d63ae227657 100644 --- a/packages/core/scripts/generate-theme-readme.js +++ b/scripts/generate-theme-readme.js @@ -1,12 +1,15 @@ import fs from "fs"; +import path from "path"; import { format, resolveConfig } from "prettier"; -import { themes } from "../src/themes/index.ts"; +import { themes } from "../packages/core/src/themes/index.ts"; // The docs live with the site that publishes them. -const TARGET_FILE = - "../../apps/frontend/src/content/docs/docs/customization/themes.md"; +const TARGET_FILE = path.join( + import.meta.dirname, + "../apps/frontend/src/content/docs/docs/customization/themes.md", +); const REPO_CARD_LINKS_FLAG = ""; const STAT_CARD_LINKS_FLAG = "";