chore: remove docs (#7873)
5
.github/workflows/main.yaml
vendored
|
|
@ -74,11 +74,6 @@ jobs:
|
|||
typescript:
|
||||
uses: ./.github/workflows/typescript-typecheck.yaml
|
||||
|
||||
# Deploy Website to CloudFlare Pages
|
||||
website:
|
||||
uses: ./.github/workflows/website.yaml
|
||||
secrets: inherit
|
||||
|
||||
# GraphQL Schema Publish
|
||||
graphql-schema:
|
||||
uses: ./.github/workflows/graphql-schema-publish.yaml
|
||||
|
|
|
|||
11
.github/workflows/pr.yaml
vendored
|
|
@ -65,11 +65,6 @@ jobs:
|
|||
with:
|
||||
imageTag: ${{ github.event.pull_request.head.sha }}
|
||||
|
||||
# e2e tests for docs site using Playwright
|
||||
docs-e2e:
|
||||
name: test
|
||||
uses: ./.github/workflows/tests-e2e-docs.yaml
|
||||
|
||||
# Changeset Validation
|
||||
# Validates that changesets reference valid packages that exist in the monorepo
|
||||
changeset-validation:
|
||||
|
|
@ -83,12 +78,6 @@ jobs:
|
|||
typescript:
|
||||
uses: ./.github/workflows/typescript-typecheck.yaml
|
||||
|
||||
# Deploy Website preview to CloudFlare Pages
|
||||
website-preview:
|
||||
uses: ./.github/workflows/website.yaml
|
||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
||||
secrets: inherit
|
||||
|
||||
# Release alpha version on NPM for Hive libraries
|
||||
alpha:
|
||||
uses: ./.github/workflows/release-alpha.yaml
|
||||
|
|
|
|||
34
.github/workflows/website.yaml
vendored
|
|
@ -1,34 +0,0 @@
|
|||
on:
|
||||
workflow_call:
|
||||
|
||||
jobs:
|
||||
deployment:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
- name: setup environment
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
codegen: false
|
||||
actor: website
|
||||
|
||||
- uses: the-guild-org/shared-config/website-cf@main
|
||||
name: build and deploy website
|
||||
env:
|
||||
NEXT_BASE_PATH: ${{ github.ref == 'refs/heads/main' && '/graphql/hive' || '' }}
|
||||
SITE_URL:
|
||||
${{ github.ref == 'refs/heads/main' && 'https://the-guild.dev/graphql/hive' || '' }}
|
||||
with:
|
||||
cloudflareApiToken: ${{ secrets.WEBSITE_CLOUDFLARE_API_TOKEN }}
|
||||
cloudflareAccountId: ${{ secrets.WEBSITE_CLOUDFLARE_ACCOUNT_ID }}
|
||||
githubToken: ${{ secrets.GITHUB_TOKEN }}
|
||||
projectName: hive-landing-page
|
||||
prId: ${{ github.event.pull_request.number }}
|
||||
mainBranch: main
|
||||
websiteDirectory: ./
|
||||
buildScript: cd packages/web/docs && pnpm build
|
||||
artifactDir: packages/web/docs/out
|
||||
|
|
@ -18,7 +18,7 @@
|
|||
"pnpm": ">=10.16.0"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "pnpm turbo build --filter='@hive/app' --concurrency=1 --color && pnpm turbo build --concurrency=4 --color --filter '!@hive/docs' --filter '!@hive/app'",
|
||||
"build": "pnpm turbo build --filter='@hive/app' --concurrency=1 --color && pnpm turbo build --concurrency=4 --color --filter '!@hive/app'",
|
||||
"build:libraries": "pnpm prebuild && pnpm graphql:generate && pnpm turbo build --filter=./packages/libraries/* --color",
|
||||
"build:services": "pnpm prebuild && pnpm turbo build --filter=./packages/services/**/* --filter=./packages/migrations --color",
|
||||
"build:web": "pnpm prebuild && pnpm turbo build --filter=./packages/web/* --color",
|
||||
|
|
@ -177,12 +177,9 @@
|
|||
"oclif": "patches/oclif.patch",
|
||||
"graphiql": "patches/graphiql.patch",
|
||||
"@graphiql/react": "patches/@graphiql__react.patch",
|
||||
"countup.js": "patches/countup.js.patch",
|
||||
"@oclif/core@4.0.6": "patches/@oclif__core@4.0.6.patch",
|
||||
"p-cancelable@4.0.1": "patches/p-cancelable@4.0.1.patch",
|
||||
"bentocache": "patches/bentocache.patch",
|
||||
"nextra": "patches/nextra.patch",
|
||||
"nextra-theme-docs": "patches/nextra-theme-docs.patch",
|
||||
"@graphql-codegen/schema-ast": "patches/@graphql-codegen__schema-ast.patch",
|
||||
"@fastify/vite": "patches/@fastify__vite.patch"
|
||||
},
|
||||
|
|
|
|||
9
packages/web/docs/.gitignore
vendored
|
|
@ -1,9 +0,0 @@
|
|||
out
|
||||
build
|
||||
temp
|
||||
public/sitemap.xml
|
||||
public/changelog.json
|
||||
public/_pagefind/
|
||||
|
||||
# Playwright
|
||||
.playwright/
|
||||
|
|
@ -1,124 +0,0 @@
|
|||
import { expect, test } from '@playwright/test';
|
||||
|
||||
test.describe('Blog & Content User Journeys', () => {
|
||||
test('developer discovers Hive through blog and explores docs', async ({ page }) => {
|
||||
await page.goto('/blog');
|
||||
|
||||
await expect(page.getByRole('heading', { name: 'Blog' })).toBeVisible();
|
||||
|
||||
const posts = page.getByRole('article');
|
||||
await expect(posts.first()).toBeVisible();
|
||||
|
||||
const firstPostLink = page
|
||||
.locator('a[href^="/blog/"]')
|
||||
.filter({ has: page.getByRole('article') })
|
||||
.first();
|
||||
await firstPostLink.click();
|
||||
|
||||
await expect(page.getByRole('heading', { level: 1 })).toBeVisible();
|
||||
|
||||
await expect(page.locator('article p').first()).toBeVisible();
|
||||
|
||||
const docsLink = page.getByRole('contentinfo').getByRole('link', { name: 'Documentation' });
|
||||
await docsLink.scrollIntoViewIfNeeded();
|
||||
await expect(docsLink).toBeVisible();
|
||||
await docsLink.click();
|
||||
await page.waitForURL(/docs/);
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
await expect(page).toHaveURL(/docs/);
|
||||
await expect(page.getByRole('heading', { level: 1 })).toBeVisible();
|
||||
});
|
||||
|
||||
test('user reads case study to evaluate Hive for their company', async ({ page }) => {
|
||||
await page.goto('/case-studies');
|
||||
|
||||
await expect(page.getByRole('heading', { level: 1 })).toBeVisible();
|
||||
|
||||
// Target the "Explore customer stories" section which contains case study cards
|
||||
// FeaturedCaseStudiesGrid is hidden on mobile (max-xl:hidden) so we use AllCaseStudiesList
|
||||
const storiesSection = page.locator('section', {
|
||||
has: page.getByRole('heading', { name: 'Explore customer stories' }),
|
||||
});
|
||||
const caseStudyLinks = storiesSection.locator('a[href^="/case-studies/"]');
|
||||
await caseStudyLinks.first().scrollIntoViewIfNeeded();
|
||||
await expect(caseStudyLinks.first()).toBeVisible();
|
||||
|
||||
await caseStudyLinks.first().click();
|
||||
|
||||
await expect(page.getByRole('heading', { level: 1 })).toBeVisible();
|
||||
|
||||
await expect(page.locator('p').first()).toBeVisible();
|
||||
});
|
||||
|
||||
test('user checks product updates to see recent improvements', async ({ page }) => {
|
||||
// User wants to know what's new
|
||||
await page.goto('/product-updates');
|
||||
|
||||
await expect(page.getByRole('heading', { level: 1 })).toBeVisible();
|
||||
|
||||
// Sees list of updates
|
||||
const updates = page.locator('a[href^="/product-updates/"]');
|
||||
await expect(updates.first()).toBeVisible();
|
||||
|
||||
// Clicks on recent update
|
||||
await updates.first().click();
|
||||
|
||||
// Reads update details
|
||||
await expect(page.getByRole('heading', { level: 1 })).toBeVisible();
|
||||
});
|
||||
|
||||
test('developer reads multiple blog posts in a session', async ({ page }) => {
|
||||
await page.goto('/blog');
|
||||
|
||||
const firstPostLink = page
|
||||
.locator('a[href^="/blog/"]')
|
||||
.filter({ has: page.getByRole('article') })
|
||||
.first();
|
||||
await expect(firstPostLink).toBeVisible();
|
||||
const firstHref = await firstPostLink.getAttribute('href');
|
||||
await firstPostLink.click();
|
||||
/* Next.js prefetch can interrupt navigation without this wait */
|
||||
await page.waitForLoadState('networkidle');
|
||||
await expect(page.getByRole('heading', { level: 1 })).toBeVisible();
|
||||
|
||||
await page.goto('/blog');
|
||||
/* Prevents navigation race with Next.js prefetch on mobile */
|
||||
await page.waitForLoadState('domcontentloaded');
|
||||
await expect(page.getByRole('heading', { name: 'Blog' })).toBeVisible();
|
||||
|
||||
const secondPostLink = page
|
||||
.locator(`a[href^="/blog/"]:not([href="${firstHref}"])`)
|
||||
.filter({ has: page.getByRole('article') })
|
||||
.first();
|
||||
await expect(secondPostLink).toBeVisible();
|
||||
await secondPostLink.click();
|
||||
await expect(page.getByRole('heading', { level: 1 })).toBeVisible();
|
||||
|
||||
await expect(page.locator('article p').first()).toBeVisible();
|
||||
});
|
||||
|
||||
test('user explores ecosystem and partner pages', async ({ page }) => {
|
||||
// User wants to understand the Hive ecosystem
|
||||
await page.goto('/ecosystem');
|
||||
await expect(page.getByRole('heading', { level: 1 })).toBeVisible();
|
||||
|
||||
// Navigates to partners page
|
||||
await page.goto('/partners');
|
||||
await expect(page.getByRole('heading', { level: 1 })).toBeVisible();
|
||||
|
||||
// Partners page has content (paragraphs or list items)
|
||||
await expect(page.locator('p').first()).toBeVisible();
|
||||
});
|
||||
|
||||
test('user checks OSS friends page and discovers related projects', async ({ page }) => {
|
||||
await page.goto('/oss-friends');
|
||||
|
||||
await expect(page.getByRole('heading', { level: 1 })).toBeVisible();
|
||||
|
||||
const projectLinks = page.locator('dl a[href^="http"]');
|
||||
|
||||
await projectLinks.first().scrollIntoViewIfNeeded();
|
||||
await expect(projectLinks.first()).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
|
@ -1,80 +0,0 @@
|
|||
import { expect, test } from '@playwright/test';
|
||||
|
||||
// Note: /docs requires Nextra to be properly built. These tests may fail in dev mode.
|
||||
// Run against built static site for reliable results.
|
||||
|
||||
test.describe('Documentation User Journeys', () => {
|
||||
test.beforeEach(async ({ page }) => {
|
||||
// Check if docs page loads without error
|
||||
const response = await page.goto('/docs');
|
||||
if (!response?.ok()) {
|
||||
test.skip(true, 'Docs page not available (needs build)');
|
||||
}
|
||||
});
|
||||
|
||||
test('docs landing page shows content', async ({ page, isMobile }) => {
|
||||
await expect(page.getByRole('heading', { level: 1 })).toBeVisible();
|
||||
|
||||
if (isMobile) {
|
||||
const sidebar = page.getByRole('complementary');
|
||||
await expect(sidebar.first()).toBeVisible();
|
||||
} else {
|
||||
const nav = page.getByRole('navigation');
|
||||
await expect(nav.first()).toBeVisible();
|
||||
}
|
||||
});
|
||||
|
||||
test('developer navigates to schema registry via sidebar', async ({ page }) => {
|
||||
// Schema Registry is under "Hive Console" section
|
||||
const sidebar = page.getByRole('complementary');
|
||||
|
||||
// HeadlessUI overlays intercept pointer events, use JS click to bypass
|
||||
const schemaRegistryLink = sidebar.locator('a[href="/docs/schema-registry"]');
|
||||
await expect(schemaRegistryLink).toBeVisible();
|
||||
await schemaRegistryLink.evaluate((el: HTMLElement) => el.click());
|
||||
|
||||
await expect(page).toHaveURL(/schema-registry/);
|
||||
await expect(page.getByRole('heading', { level: 1 })).toBeVisible();
|
||||
});
|
||||
|
||||
test('developer navigates to gateway via sidebar', async ({ page }) => {
|
||||
const sidebar = page.getByRole('complementary');
|
||||
|
||||
const gatewayLink = sidebar.locator('a[href="/docs/gateway"]');
|
||||
await expect(gatewayLink).toBeVisible();
|
||||
await gatewayLink.evaluate((el: HTMLElement) => el.click());
|
||||
await page.waitForURL(/gateway/);
|
||||
|
||||
await expect(page).toHaveURL(/gateway/);
|
||||
await expect(page.getByRole('heading', { level: 1 })).toBeVisible();
|
||||
});
|
||||
|
||||
test('documentation shows code examples', async ({ page }) => {
|
||||
// Navigate to CLI reference which has code examples
|
||||
await page.goto('/docs/api-reference/cli');
|
||||
|
||||
// Check for code blocks
|
||||
const codeBlock = page.locator('pre').first();
|
||||
await expect(codeBlock).toBeVisible();
|
||||
});
|
||||
|
||||
test('sidebar shows active section', async ({ page }) => {
|
||||
await page.goto('/docs/schema-registry');
|
||||
|
||||
const sidebar = page.getByRole('complementary');
|
||||
const hiveConsoleButton = sidebar.getByRole('button', { name: 'Hive Console' });
|
||||
await expect(hiveConsoleButton).toBeVisible();
|
||||
await expect(hiveConsoleButton).toHaveAttribute('data-href', '/docs/schema-registry');
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Documentation API Reference', () => {
|
||||
test('CLI reference page loads', async ({ page }) => {
|
||||
const response = await page.goto('/docs/api-reference/cli');
|
||||
if (!response?.ok()) {
|
||||
test.skip(true, 'Page not available');
|
||||
}
|
||||
|
||||
await expect(page.getByRole('heading', { level: 1 })).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
|
@ -1,127 +0,0 @@
|
|||
import { expect, test } from '@playwright/test';
|
||||
|
||||
test.describe('Landing Page User Journeys', () => {
|
||||
test('new visitor explores Hive and decides to sign up', async ({ page, isMobile }) => {
|
||||
await page.goto('/');
|
||||
|
||||
await expect(page.getByRole('heading', { level: 1 })).toBeVisible();
|
||||
|
||||
if (!isMobile) {
|
||||
const heroList = page.locator('ul').first();
|
||||
await expect(heroList.getByRole('listitem').first()).toBeVisible();
|
||||
}
|
||||
|
||||
const featureTabs = page.getByRole('tablist').first();
|
||||
await featureTabs.scrollIntoViewIfNeeded();
|
||||
await expect(featureTabs).toBeVisible();
|
||||
|
||||
const tabs = featureTabs.getByRole('tab');
|
||||
await expect(tabs.first()).toBeVisible();
|
||||
|
||||
if (isMobile) {
|
||||
// On mobile, tabs render as a dropdown - only active tab is visible.
|
||||
// Clicking the active tab opens the dropdown, revealing all tabs.
|
||||
await tabs.first().click();
|
||||
// Now the second tab should be visible and clickable
|
||||
}
|
||||
await tabs.nth(1).click();
|
||||
await expect(page.getByRole('tabpanel').first()).toBeVisible();
|
||||
|
||||
const signUpCta = page.getByRole('link', { name: /get started/i }).first();
|
||||
await expect(signUpCta).toHaveAttribute('href', /app\.graphql-hive\.com/);
|
||||
});
|
||||
|
||||
test('developer navigates to federation page', async ({ page }) => {
|
||||
await page.goto('/');
|
||||
|
||||
const federationLink = page
|
||||
.locator('p')
|
||||
.getByRole('link', { name: /federation/i })
|
||||
.first();
|
||||
/* JS click bypasses Next.js prefetch event handlers that can intercept navigation */
|
||||
await federationLink.evaluate(el => (el as HTMLElement).click());
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
await expect(page).toHaveURL('/federation');
|
||||
await expect(page.getByRole('heading', { level: 1 })).toBeVisible();
|
||||
});
|
||||
|
||||
test('developer navigates to gateway page', async ({ page }) => {
|
||||
await page.goto('/');
|
||||
|
||||
await page.getByRole('link', { name: 'gateway', exact: true }).click();
|
||||
/* Prevents navigation race with Next.js prefetch */
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
await expect(page).toHaveURL('/gateway');
|
||||
await expect(page.getByRole('heading', { level: 1 }).first()).toBeVisible();
|
||||
});
|
||||
|
||||
test('user navigates to pricing via nav', async ({ page, isMobile }) => {
|
||||
await page.goto('/');
|
||||
|
||||
if (isMobile) {
|
||||
await page.getByRole('button', { name: 'Menu' }).click();
|
||||
await page
|
||||
.getByRole('complementary')
|
||||
.getByRole('link', { name: /pricing/i })
|
||||
.click();
|
||||
} else {
|
||||
const nav = page.getByRole('navigation').first();
|
||||
await nav.getByRole('link', { name: /pricing/i }).click();
|
||||
}
|
||||
/* Prevents navigation race with Next.js prefetch */
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
await expect(page).toHaveURL('/pricing');
|
||||
await expect(page.getByRole('heading', { level: 1 })).toBeVisible();
|
||||
});
|
||||
|
||||
test('FAQ accordion expands on click', async ({ page }) => {
|
||||
await page.goto('/');
|
||||
|
||||
// Find FAQ section by its accordion structure (Radix UI)
|
||||
const faqAccordion = page.locator('[data-orientation="vertical"]').first();
|
||||
await faqAccordion.scrollIntoViewIfNeeded();
|
||||
|
||||
// Click first accordion trigger
|
||||
const faqTrigger = faqAccordion.getByRole('button').first();
|
||||
await expect(faqTrigger).toBeVisible();
|
||||
await faqTrigger.click();
|
||||
|
||||
// Content should expand (data-state changes)
|
||||
await expect(page.locator('[data-state="open"]').first()).toBeVisible();
|
||||
});
|
||||
|
||||
test('testimonials section shows company tabs', async ({ page }) => {
|
||||
await page.goto('/');
|
||||
|
||||
// Find testimonials by tablist structure (second tablist on page after features)
|
||||
const tabLists = page.getByRole('tablist');
|
||||
const testimonialTabs = tabLists.nth(1);
|
||||
await testimonialTabs.scrollIntoViewIfNeeded();
|
||||
|
||||
// Tabs exist for companies
|
||||
const tabs = testimonialTabs.getByRole('tab');
|
||||
await expect(tabs.first()).toBeVisible();
|
||||
expect(await tabs.count()).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
test('navigation menu is accessible', async ({ page, isMobile }) => {
|
||||
await page.goto('/');
|
||||
|
||||
if (isMobile) {
|
||||
const menuButton = page.getByRole('button', { name: 'Menu' });
|
||||
await expect(menuButton).toBeVisible();
|
||||
await menuButton.click();
|
||||
const sidebar = page.getByRole('complementary');
|
||||
await expect(sidebar).toBeVisible();
|
||||
await expect(sidebar.getByRole('link', { name: /pricing/i })).toBeVisible();
|
||||
} else {
|
||||
const nav = page.getByRole('navigation').first();
|
||||
await expect(nav).toBeVisible();
|
||||
await expect(nav.getByRole('button').first()).toBeVisible();
|
||||
await expect(nav.getByRole('link', { name: /pricing/i })).toBeVisible();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
// eslint-disable-next-line import/no-extraneous-dependencies -- test config
|
||||
import { defineConfig, devices } from '@playwright/test';
|
||||
|
||||
const baseURL = process.env.BASE_URL || 'http://localhost:3000';
|
||||
|
||||
export default defineConfig({
|
||||
testDir: '.',
|
||||
testMatch: '**/*.e2e.ts',
|
||||
outputDir: '../.playwright/test-results',
|
||||
fullyParallel: true,
|
||||
forbidOnly: !!process.env.CI,
|
||||
retries: process.env.CI ? 2 : 1,
|
||||
workers: process.env.CI ? 1 : 2,
|
||||
reporter: [['html', { outputFolder: '../.playwright/report' }]],
|
||||
use: {
|
||||
baseURL,
|
||||
trace: 'on-first-retry',
|
||||
screenshot: 'only-on-failure',
|
||||
navigationTimeout: 30_000,
|
||||
},
|
||||
projects: [
|
||||
{
|
||||
name: 'chromium',
|
||||
use: { ...devices['Desktop Chrome'] },
|
||||
},
|
||||
{
|
||||
name: 'mobile',
|
||||
use: { ...devices['iPhone 13'], defaultBrowserType: 'webkit' },
|
||||
},
|
||||
],
|
||||
webServer: process.env.CI
|
||||
? {
|
||||
command: 'pnpm exec serve out -l 3000',
|
||||
url: baseURL,
|
||||
reuseExistingServer: false,
|
||||
cwd: '..',
|
||||
}
|
||||
: {
|
||||
command: 'pnpm dev',
|
||||
url: baseURL,
|
||||
reuseExistingServer: true,
|
||||
timeout: 120_000,
|
||||
},
|
||||
});
|
||||
|
|
@ -1,86 +0,0 @@
|
|||
import { expect, test } from '@playwright/test';
|
||||
|
||||
test.describe('Search User Journeys', () => {
|
||||
const searchPlaceholder = 'Search documentation…';
|
||||
|
||||
test('developer uses search to find federation info', async ({ page, isMobile }) => {
|
||||
await page.goto('/');
|
||||
|
||||
if (isMobile) {
|
||||
await page.getByRole('button', { name: 'Menu' }).click();
|
||||
}
|
||||
|
||||
const searchInput = page.getByRole('combobox', { name: searchPlaceholder });
|
||||
await expect(searchInput).toBeVisible();
|
||||
|
||||
await searchInput.click();
|
||||
await searchInput.fill('federation');
|
||||
|
||||
await expect(searchInput).toHaveValue('federation');
|
||||
const results = page.getByRole('option');
|
||||
const errorMessage = page.getByText('Failed to load search index');
|
||||
const noResults = page.getByText('No results found');
|
||||
await expect(results.first().or(errorMessage).or(noResults)).toBeVisible({ timeout: 30_000 });
|
||||
});
|
||||
|
||||
test('user opens search with keyboard shortcut', async ({ page, isMobile }) => {
|
||||
await page.goto('/');
|
||||
|
||||
if (isMobile) {
|
||||
await page.getByRole('button', { name: 'Menu' }).click();
|
||||
}
|
||||
|
||||
await page.keyboard.press('Meta+k');
|
||||
|
||||
const searchInput = page.getByRole('combobox', { name: searchPlaceholder });
|
||||
await searchInput.fill('gateway');
|
||||
|
||||
await expect(searchInput).toHaveValue('gateway');
|
||||
const results = page.getByRole('option');
|
||||
const errorMessage = page.getByText('Failed to load search index');
|
||||
const noResults = page.getByText('No results found');
|
||||
await expect(results.first().or(errorMessage).or(noResults)).toBeVisible({ timeout: 30_000 });
|
||||
});
|
||||
|
||||
test('search results navigate to docs', async ({ page, isMobile }) => {
|
||||
test.skip(!process.env.CI, 'Search index only available in CI (production build)');
|
||||
|
||||
await page.goto('/');
|
||||
|
||||
if (isMobile) {
|
||||
await page.getByRole('button', { name: 'Menu' }).click();
|
||||
}
|
||||
|
||||
const searchInput = page.getByRole('combobox', { name: searchPlaceholder });
|
||||
await searchInput.click();
|
||||
await searchInput.fill('schema registry');
|
||||
|
||||
const firstResult = page.getByRole('option').first();
|
||||
await expect(firstResult).toBeVisible();
|
||||
await firstResult.click();
|
||||
|
||||
await expect(page).toHaveURL(/docs/);
|
||||
});
|
||||
|
||||
test('search is available on pricing page', async ({ page, isMobile }) => {
|
||||
await page.goto('/pricing');
|
||||
|
||||
if (isMobile) {
|
||||
await page.getByRole('button', { name: 'Menu' }).click();
|
||||
}
|
||||
|
||||
const searchInput = page.getByRole('combobox', { name: searchPlaceholder });
|
||||
await expect(searchInput).toBeVisible();
|
||||
});
|
||||
|
||||
test('search is available on blog page', async ({ page, isMobile }) => {
|
||||
await page.goto('/blog');
|
||||
|
||||
if (isMobile) {
|
||||
await page.getByRole('button', { name: 'Menu' }).click();
|
||||
}
|
||||
|
||||
const searchInput = page.getByRole('combobox', { name: searchPlaceholder });
|
||||
await expect(searchInput).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
|
@ -1 +0,0 @@
|
|||
export { useHiveMDXComponents as useMDXComponents } from '@theguild/components/server';
|
||||
6
packages/web/docs/next-env.d.ts
vendored
|
|
@ -1,6 +0,0 @@
|
|||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
/// <reference path="./.next/types/routes.d.ts" />
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
/** @type {import('next-sitemap').IConfig} */
|
||||
export default {
|
||||
siteUrl: process.env.SITE_URL || 'https://graphql-hive.com',
|
||||
generateIndexSitemap: false,
|
||||
additionalPaths: config => [
|
||||
{
|
||||
loc: config.siteUrl.replace(/\/$/, '') + '/federation-gateway-audit',
|
||||
lastmod: new Date().toISOString(),
|
||||
changefreq: 'weekly',
|
||||
priority: 0.7,
|
||||
},
|
||||
{
|
||||
loc: config.siteUrl.replace(/\/$/, '') + '/federation-gateway-performance',
|
||||
lastmod: new Date().toISOString(),
|
||||
changefreq: 'weekly',
|
||||
priority: 0.7,
|
||||
},
|
||||
],
|
||||
output: 'export',
|
||||
};
|
||||
|
|
@ -1,358 +0,0 @@
|
|||
import withBundleAnalyzer from '@next/bundle-analyzer';
|
||||
import { withGuildDocs } from '@theguild/components/next.config';
|
||||
|
||||
let config = withGuildDocs({
|
||||
output: 'export',
|
||||
eslint: {
|
||||
ignoreDuringBuilds: true,
|
||||
},
|
||||
experimental: {
|
||||
turbo: {
|
||||
treeShaking: true,
|
||||
},
|
||||
},
|
||||
|
||||
nextraConfig: {
|
||||
contentDirBasePath: '/docs',
|
||||
},
|
||||
redirects: async () => [
|
||||
{
|
||||
source: '/docs/access-tokens',
|
||||
destination: '/docs/schema-registry/management/access-tokens',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/docs/get-started/organizations',
|
||||
destination: '/docs/management/organizations',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/docs/get-started/projects',
|
||||
destination: '/docs/management/projects',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/docs/get-started/targets',
|
||||
destination: '/docs/management/targets',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/docs/features/tokens',
|
||||
destination: '/docs/management/targets#manage-tokens',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/docs/features/publish-schema',
|
||||
destination: '/docs/schema-registry#publish-a-schema',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/docs/features/checking-schema',
|
||||
destination: '/docs/schema-registry#check-a-schema',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/docs/features/delete-schema',
|
||||
destination: '/docs/schema-registry#delete-a-service',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/docs/features/registry-usage',
|
||||
destination: '/docs/schema-registry/high-availability-cdn',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/docs/features/high-availability-cdn',
|
||||
destination: '/docs/schema-registry/high-availability-cdn',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/docs/high-availability-cdn',
|
||||
destination: '/docs/schema-registry/high-availability-cdn',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/docs/features/monitoring',
|
||||
destination: '/docs/schema-registry/usage-reporting',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/docs/features/usage-reporting',
|
||||
destination: '/docs/schema-registry/usage-reporting',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/docs/features/schema-history',
|
||||
destination: '/docs/schema-registry#schema-history-and-changelog',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/docs/features/integrations',
|
||||
destination: '/docs/management/organizations#integrations',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/docs/features/alerts-notifications',
|
||||
destination: '/docs/management/projects#alerts-and-notifications',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/docs/management/external-schema-composition',
|
||||
destination: '/docs/schema-registry/external-schema-composition',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/docs/features/external-schema-composition',
|
||||
destination: '/docs/schema-registry/external-schema-composition',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/docs/specs/schema-reports',
|
||||
destination: '/docs/api-reference/cli#publish-a-schema',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/docs/self-hosting/apollo-federation-2',
|
||||
destination: '/docs/schema-registry/self-hosting/external-composition',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/docs/features/sso-oidc-provider',
|
||||
destination: '/docs/management/sso-oidc-provider',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/docs/features/schema-registry',
|
||||
destination: '/docs/schema-registry',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/docs/management/external-schema-composition',
|
||||
destination: '/docs/schema-registry/external-schema-composition',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/docs/features/laboratory',
|
||||
destination: '/docs/schema-registry/laboratory',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/docs/features/laboratory/:path*',
|
||||
destination: '/docs/schema-registry/laboratory/:path*',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/docs/management/contracts',
|
||||
destination: '/docs/schema-registry/contracts',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/docs/features/schema-policy',
|
||||
destination: '/docs/schema-registry/schema-policy',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/docs/features/app-deployments',
|
||||
destination: '/docs/schema-registry/app-deployments',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/docs/use-cases/apollo-studio',
|
||||
destination: '/docs/use-cases/apollo-graphos',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/docs/schema-registry/link-specifications',
|
||||
destination: '/docs/api-reference/link-specifications',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/docs/specs/link-specifications',
|
||||
destination: '/docs/api-reference/link-specifications',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/docs/graphql-api',
|
||||
destination: '/docs/api-reference/graphql-api',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/docs/graphql-api/:path*',
|
||||
destination: '/docs/api-reference/graphql-api/:path*',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/docs/specs/usage-reports',
|
||||
destination: '/docs/api-reference/usage-reports',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/docs/get-started/:path*',
|
||||
destination: '/docs/schema-registry/get-started/:path*',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/docs/management/:path*',
|
||||
destination: '/docs/schema-registry/management/:path*',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
// SEO: Redirect to the new URL
|
||||
source: '/docs/self-hosting/federation-2',
|
||||
destination: '/docs/schema-registry/self-hosting/external-composition',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/docs/integrations',
|
||||
destination: '/docs/other-integrations',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/docs/integrations/:path*',
|
||||
destination: '/docs/other-integrations/:path*',
|
||||
permanent: false,
|
||||
},
|
||||
{
|
||||
source: '/docs/api-reference/gateway/cli',
|
||||
destination: '/docs/api-reference/gateway-cli',
|
||||
permanent: true,
|
||||
},
|
||||
// Broken links found in Google Search Console
|
||||
{
|
||||
source: '/docs/api-reference',
|
||||
destination: '/docs/api-reference/cli',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/docs/self-hosting',
|
||||
destination: '/docs/schema-registry/self-hosting/get-started',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/docs/dashboard',
|
||||
destination: '/docs/schema-registry/usage-reporting',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/docs/dashboard/insights',
|
||||
destination: '/docs/schema-registry/usage-reporting',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/docs/dashboard/explorer',
|
||||
destination: '/docs/schema-registry/explorer',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/docs/dashboard/laboratory/:path*',
|
||||
destination: '/docs/schema-registry/laboratory/:path*',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/docs/integrations/code-first',
|
||||
destination: '/docs/other-integrations/code-first',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/product-updates/2024-01-25-schema-contracts-for-federation',
|
||||
destination: '/product-updates/2024-02-06-schema-contracts-for-federation',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/docs/integrations/apollo-router',
|
||||
destination: '/docs/other-integrations/apollo-router',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/docs/integrations/envelop',
|
||||
destination: '/docs/other-integrations/envelop',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/docs/integrations/schema-stitching',
|
||||
destination: '/docs/other-integrations/schema-stitching',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/docs/integrations/apollo-gateway',
|
||||
destination: '/docs/other-integrations/apollo-gateway',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/docs/integrations/graphql-code-generator',
|
||||
destination: '/docs/other-integrations/graphql-code-generator',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/docs/integrations/ci-cd',
|
||||
destination: '/docs/other-integrations/ci-cd',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/docs/integrations/apollo-server',
|
||||
destination: '/docs/other-integrations/apollo-server',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/docs/integrations/graphql-ruby',
|
||||
destination: '/docs/other-integrations/graphql-ruby',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/docs/integrations/graphql-mesh',
|
||||
destination: '/docs/gateway',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/docs/integrations/graphql-yoga',
|
||||
destination: '/docs/other-integrations/graphql-yoga',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/docs/gateway/deployment/node-frameworks',
|
||||
destination: '/docs/gateway/deployment/runtimes/nodejs',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/docs/gateway/other-features/performance/deduplicate-request',
|
||||
destination: '/docs/gateway/other-features/performance/deduplicate-inflight-requests',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/docs/self-hosting/:path*',
|
||||
destination: '/docs/schema-registry/self-hosting/:path*',
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: '/docs/gateway/other-features/router-runtime',
|
||||
destination: '/docs/gateway/other-features/rust-query-planner',
|
||||
permanent: true,
|
||||
},
|
||||
],
|
||||
env: {
|
||||
SITE_URL: 'https://the-guild.dev/graphql/hive',
|
||||
NEXT_BASE_PATH: process.env.NEXT_BASE_PATH,
|
||||
},
|
||||
webpack: (config, { webpack }) => {
|
||||
config.externals['node:fs'] = 'commonjs node:fs';
|
||||
config.externals['node:path'] = 'commonjs node:path';
|
||||
|
||||
config.resolve.fallback = {
|
||||
...config.resolve.fallback,
|
||||
fs: false,
|
||||
};
|
||||
config.plugins.push(
|
||||
new webpack.NormalModuleReplacementPlugin(/^node:/, resource => {
|
||||
resource.request = resource.request.replace(/^node:/, '');
|
||||
}),
|
||||
);
|
||||
|
||||
return config;
|
||||
},
|
||||
});
|
||||
|
||||
if (process.env.ANALYZE === 'true') {
|
||||
config = withBundleAnalyzer({ enabled: true })(config);
|
||||
}
|
||||
|
||||
export default config;
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
{
|
||||
"name": "@hive/docs",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"imports": {
|
||||
"#components/*": "./src/components/*"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "next build && next-sitemap",
|
||||
"build:analyze": "ANALYZE=true next build",
|
||||
"dev": "next --turbopack",
|
||||
"postbuild": "pagefind --site .next/server/app --output-path out/_pagefind",
|
||||
"prettier": "prettier --cache --write --list-different --ignore-unknown src",
|
||||
"test:e2e": "playwright test --config e2e/playwright.config.ts",
|
||||
"validate-mdx-links": "pnpx validate-mdx-links@1.1.0 --files 'src/**/*.mdx'"
|
||||
},
|
||||
"dependencies": {
|
||||
"@radix-ui/react-accordion": "1.2.2",
|
||||
"@radix-ui/react-icons": "1.3.2",
|
||||
"@radix-ui/react-tabs": "1.1.2",
|
||||
"@radix-ui/react-tooltip": "1.1.6",
|
||||
"@theguild/components": "9.11.3",
|
||||
"@types/rss": "^0.0.32",
|
||||
"class-variance-authority": "0.7.1",
|
||||
"clsx": "2.1.1",
|
||||
"date-fns": "4.1.0",
|
||||
"lucide-react": "0.548.0",
|
||||
"next": "15.5.10",
|
||||
"react": "19.2.4",
|
||||
"react-avatar": "5.0.3",
|
||||
"react-countup": "6.5.3",
|
||||
"react-dom": "19.2.4",
|
||||
"react-icons": "5.4.0",
|
||||
"rehype-frontmatter-mdx-imports": "0.1.1",
|
||||
"tailwind-merge": "2.6.0",
|
||||
"zod": "3.25.76"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@mdx-js/typescript-plugin": "^0.0.8",
|
||||
"@next/bundle-analyzer": "^16.0.0",
|
||||
"@playwright/test": "^1.58.0",
|
||||
"@tailwindcss/typography": "0.5.16",
|
||||
"@theguild/tailwind-config": "0.6.3",
|
||||
"@types/react": "18.3.18",
|
||||
"next-sitemap": "4.2.3",
|
||||
"pagefind": "^1.2.0",
|
||||
"postcss": "8.4.49",
|
||||
"postcss-nesting": "^13.0.1",
|
||||
"rss": "1.2.2",
|
||||
"serve": "14.2.6",
|
||||
"tailwindcss": "3.4.17",
|
||||
"tailwindcss-animate": "1.0.7",
|
||||
"tailwindcss-radix": "3.0.5"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
import config from '@theguild/tailwind-config/postcss.config';
|
||||
|
||||
export default {
|
||||
...config,
|
||||
plugins: {
|
||||
...config.plugins,
|
||||
'postcss-nesting': {},
|
||||
},
|
||||
};
|
||||
|
Before Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 88 KiB |
|
|
@ -1,68 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -u
|
||||
|
||||
COMMIT="latest"
|
||||
OS="$(uname -s)"
|
||||
|
||||
# Parse Flags
|
||||
parse_args() {
|
||||
while [[ $# -gt 0 ]]; do
|
||||
key="$1"
|
||||
|
||||
case $key in
|
||||
-c | --commit | -v | --version)
|
||||
COMMIT="$2"
|
||||
shift # past commit argument
|
||||
shift # past commit value
|
||||
;;
|
||||
*)
|
||||
echo "Unrecognized argument $key"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
set_target() {
|
||||
case "$OS" in
|
||||
Linux)
|
||||
TARGET="linux"
|
||||
EXT=""
|
||||
;;
|
||||
|
||||
Darwin)
|
||||
TARGET=macos
|
||||
EXT=""
|
||||
;;
|
||||
|
||||
MINGW* | MSYS* | CYGWIN*)
|
||||
TARGET=win
|
||||
EXT=".exe"
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "OS $OS is not supported."
|
||||
echo "If you think that's a bug - please file an issue to https://github.com/graphql-hive/platform/issues"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
download() {
|
||||
DOWNLOAD_DIR=$(mktemp -d)
|
||||
|
||||
URL="https://apollo-router.theguild.workers.dev/$TARGET/$COMMIT"
|
||||
echo "Downloading $URL"
|
||||
|
||||
if ! curl --progress-bar --fail -L "$URL" -o "$DOWNLOAD_DIR/router.tar.gz"; then
|
||||
echo "Download failed."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
tar xzf "$DOWNLOAD_DIR/router.tar.gz"
|
||||
}
|
||||
|
||||
parse_args "$@"
|
||||
set_target
|
||||
download
|
||||
|
Before Width: | Height: | Size: 116 KiB |
|
|
@ -1,9 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<browserconfig>
|
||||
<msapplication>
|
||||
<tile>
|
||||
<square150x150logo src="/mstile-150x150.png"/>
|
||||
<TileColor>#da532c</TileColor>
|
||||
</tile>
|
||||
</msapplication>
|
||||
</browserconfig>
|
||||
|
Before Width: | Height: | Size: 94 KiB |
|
Before Width: | Height: | Size: 120 KiB |
|
Before Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 72 KiB |
|
Before Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 8.9 KiB |
|
Before Width: | Height: | Size: 158 KiB |
|
Before Width: | Height: | Size: 128 KiB |
|
Before Width: | Height: | Size: 187 KiB |
|
Before Width: | Height: | Size: 111 KiB |
|
Before Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 60 KiB |
|
Before Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 88 KiB |
|
Before Width: | Height: | Size: 60 KiB |
|
Before Width: | Height: | Size: 81 KiB |
|
Before Width: | Height: | Size: 370 KiB |
|
Before Width: | Height: | Size: 450 KiB |
|
Before Width: | Height: | Size: 181 KiB |
|
Before Width: | Height: | Size: 182 KiB |
|
Before Width: | Height: | Size: 270 KiB |
|
Before Width: | Height: | Size: 82 KiB |
|
Before Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 99 KiB |
|
Before Width: | Height: | Size: 73 KiB |
|
Before Width: | Height: | Size: 100 KiB |
|
Before Width: | Height: | Size: 89 KiB |
|
Before Width: | Height: | Size: 116 KiB |
|
Before Width: | Height: | Size: 128 KiB |
|
Before Width: | Height: | Size: 74 KiB |
|
Before Width: | Height: | Size: 98 KiB |
|
Before Width: | Height: | Size: 340 KiB |
|
Before Width: | Height: | Size: 78 KiB |
|
Before Width: | Height: | Size: 155 KiB |
|
Before Width: | Height: | Size: 86 KiB |
|
Before Width: | Height: | Size: 168 KiB |
|
Before Width: | Height: | Size: 386 KiB |
|
Before Width: | Height: | Size: 196 KiB |
|
Before Width: | Height: | Size: 84 KiB |
|
Before Width: | Height: | Size: 174 KiB |
|
Before Width: | Height: | Size: 139 KiB |
|
Before Width: | Height: | Size: 354 KiB |
|
Before Width: | Height: | Size: 141 KiB |
|
Before Width: | Height: | Size: 240 KiB |
|
Before Width: | Height: | Size: 309 KiB |
|
Before Width: | Height: | Size: 133 KiB |
|
Before Width: | Height: | Size: 257 KiB |
|
Before Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 41 KiB |
|
Before Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 192 KiB |
|
Before Width: | Height: | Size: 148 KiB |
|
Before Width: | Height: | Size: 9 KiB |
|
Before Width: | Height: | Size: 150 KiB |
|
Before Width: | Height: | Size: 82 KiB |
|
Before Width: | Height: | Size: 96 KiB |
|
Before Width: | Height: | Size: 88 KiB |
|
Before Width: | Height: | Size: 202 KiB |
|
Before Width: | Height: | Size: 70 KiB |
|
Before Width: | Height: | Size: 69 KiB |
|
Before Width: | Height: | Size: 94 KiB |
|
Before Width: | Height: | Size: 141 KiB |