diff --git a/apify-docs-theme/src/config.js b/apify-docs-theme/src/config.js
index 32657657d1..5b71c7d978 100644
--- a/apify-docs-theme/src/config.js
+++ b/apify-docs-theme/src/config.js
@@ -83,80 +83,132 @@ const themeConfig = {
target: '_self',
rel: 'dofollow',
},
+ // The dropdown labels below carry both `href` and `to` on purpose. `href` keeps the
+ // target absolute, so the link resolves from the client, SDK, and CLI docs sites as
+ // well. `to` tells the Docusaurus dropdown that the label is a real link, so clicking
+ // it opens the landing page instead of only toggling the menu open.
{
- label: 'APIs, SDKs & CLI',
+ label: 'API',
type: 'dropdown',
- activeBaseRegex: '^/(api|sdk|cli)/',
+ href: `${absoluteUrl}/api`,
+ to: `${absoluteUrl}/api`,
+ activeBaseRegex: '^/api(/|$)',
position: 'right',
+ target: '_self',
+ rel: 'dofollow',
items: [
{
- label: 'API Reference',
+ label: 'API reference',
href: `${absoluteUrl}/api/v2`,
target: '_self',
rel: 'dofollow',
},
{
- label: 'SDK for JavaScript',
- href: `${absoluteUrl}/sdk/js/docs/overview`,
+ label: 'Client for JavaScript',
+ href: `${absoluteUrl}/api/client/js/docs`,
target: '_self',
rel: 'dofollow',
},
{
- label: 'SDK for Python',
- href: `${absoluteUrl}/sdk/python/docs/overview`,
+ label: 'Client for Python',
+ href: `${absoluteUrl}/api/client/python/docs`,
target: '_self',
rel: 'dofollow',
},
{
- label: 'Client for JavaScript',
- href: `${absoluteUrl}/api/client/js/docs`,
+ label: 'Client for Go',
+ href: `https://github.com/apify/apify-client-go`,
target: '_self',
rel: 'dofollow',
},
{
- label: 'Client for Python',
- href: `${absoluteUrl}/api/client/python/docs`,
+ label: 'Client for PHP',
+ href: `https://github.com/apify/apify-client-php`,
target: '_self',
rel: 'dofollow',
},
{
- label: 'CLI',
- href: `${absoluteUrl}/cli/docs`,
+ label: 'Client for Java',
+ href: `https://github.com/apify/apify-client-java`,
+ target: '_self',
+ rel: 'dofollow',
+ },
+ {
+ label: 'Client for .NET',
+ href: `https://github.com/apify/apify-client-dotnet`,
target: '_self',
rel: 'dofollow',
},
{
- label: 'Experimental client for Rust',
+ label: 'Client for Rust',
href: `https://github.com/apify/apify-client-rust`,
target: '_self',
rel: 'dofollow',
},
+ ],
+ },
+ {
+ label: 'SDK',
+ type: 'dropdown',
+ href: `${absoluteUrl}/sdk`,
+ to: `${absoluteUrl}/sdk`,
+ activeBaseRegex: '^/sdk(/|$)',
+ position: 'right',
+ target: '_self',
+ rel: 'dofollow',
+ items: [
{
- label: 'Experimental client for Go',
- href: `https://github.com/apify/apify-client-go`,
+ label: 'SDK for JavaScript',
+ href: `${absoluteUrl}/sdk/js/docs/overview`,
target: '_self',
rel: 'dofollow',
},
{
- label: 'Experimental client for PHP',
- href: `https://github.com/apify/apify-client-php`,
+ label: 'SDK for Python',
+ href: `${absoluteUrl}/sdk/python/docs/overview`,
+ target: '_self',
+ rel: 'dofollow',
+ },
+ ],
+ },
+ {
+ label: 'CLI',
+ type: 'dropdown',
+ href: `${absoluteUrl}/cli`,
+ to: `${absoluteUrl}/cli`,
+ activeBaseRegex: '^/cli(/|$)',
+ position: 'right',
+ target: '_self',
+ rel: 'dofollow',
+ items: [
+ {
+ label: 'Installation',
+ href: `${absoluteUrl}/cli/docs/installation`,
target: '_self',
rel: 'dofollow',
},
{
- label: 'Experimental client for Java',
- href: `https://github.com/apify/apify-client-java`,
+ label: 'Quick start',
+ href: `${absoluteUrl}/cli/docs/quick-start`,
target: '_self',
rel: 'dofollow',
},
{
- label: 'Experimental client for .NET',
- href: `https://github.com/apify/apify-client-dotnet`,
+ label: 'Command reference',
+ href: `${absoluteUrl}/cli/docs/reference`,
target: '_self',
rel: 'dofollow',
},
],
},
+ {
+ label: 'MCP',
+ href: `${absoluteUrl}/integrations/mcp`,
+ activeBasePath: 'integrations/mcp',
+ position: 'right',
+ target: '_self',
+ rel: 'dofollow',
+ },
],
},
colorMode: {
diff --git a/apify-docs-theme/src/theme/NavbarItem/NavbarNavLink.jsx b/apify-docs-theme/src/theme/NavbarItem/NavbarNavLink.jsx
index 2296239802..23c95dab4a 100644
--- a/apify-docs-theme/src/theme/NavbarItem/NavbarNavLink.jsx
+++ b/apify-docs-theme/src/theme/NavbarItem/NavbarNavLink.jsx
@@ -6,11 +6,14 @@ import useBaseUrl from '@docusaurus/useBaseUrl';
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
import { usePluginData } from '@docusaurus/useGlobalData';
import IconExternalLink from '@theme/Icon/ExternalLink';
+import clsx from 'clsx';
import React from 'react';
export default function NavbarNavLink({
activeBasePath,
activeBaseRegex,
+ activeClassName = 'navbar__link--active',
+ className,
to,
href,
label,
@@ -30,7 +33,6 @@ export default function NavbarNavLink({
// TODO all this seems hacky
// {to: 'version'} should probably be forbidden, in favor of {to: '/version'}
const toUrl = useBaseUrl(to);
- const activeBaseUrl = useBaseUrl(activeBasePath);
const normalizedHref = useBaseUrl(href, { forcePrependBaseUrl: true });
const { siteConfig } = useDocusaurusContext();
const isInternalUrl = (url) => {
@@ -63,16 +65,18 @@ export default function NavbarNavLink({
.some((item) => (item.to || item.href).endsWith(location.pathname));
if (href) {
+ // `Link` drops `isActive`/`activeClassName` for absolute URLs (they only work with the
+ // React Router link), so the active class has to be resolved here instead.
+ const isActive =
+ (activeBaseRegex && isRegexpStringMatch(activeBaseRegex, location.pathname)) ||
+ (activeBasePath && location.pathname.startsWith(`/${activeBasePath}`)) ||
+ dropDownHasActiveItem;
+
return (
);
@@ -82,13 +86,14 @@ export default function NavbarNavLink({
activeBaseRegex
? isRegexpStringMatch(activeBaseRegex, location.pathname) || dropDownHasActiveItem
: location.pathname.startsWith(`/${activeBasePath}`),
- activeClassName: 'navbar__link--active',
+ activeClassName,
})}
{...props}
{...linkContentProps}
diff --git a/apify-docs-theme/src/theme/custom.css b/apify-docs-theme/src/theme/custom.css
index 57a745d33a..81fab7413e 100644
--- a/apify-docs-theme/src/theme/custom.css
+++ b/apify-docs-theme/src/theme/custom.css
@@ -997,6 +997,18 @@ div[class*='searchBox'] {
}
}
+/*
+ * Just above the mobile breakpoint the two halves of the nav link row run out of room and
+ * collide. The switch to the mobile sidebar is pinned to 996px in Docusaurus (both Infima
+ * and the JS `useWindowSize`), so the row has to fit down to that width instead: tighten
+ * the spacing between the right-hand links until the layout has room again.
+ */
+@media (min-width: 997px) and (max-width: 1100px) {
+ .navbar__inner--subnavbar .navbar__items--right {
+ gap: 0.4rem;
+ }
+}
+
/* @media (min-width: 997px) and (max-width: 1250px) {
.navbar__items--right a.icon {
display: none;
diff --git a/docusaurus.config.js b/docusaurus.config.js
index f23b9ccef9..46e424ad24 100644
--- a/docusaurus.config.js
+++ b/docusaurus.config.js
@@ -433,6 +433,7 @@ module.exports = {
'go',
'csharp',
'powershell',
+ 'rust',
'dart',
'objectivec',
'ocaml',
diff --git a/src/pages/api/index.tsx b/src/pages/api/index.tsx
index 55b62af000..28a3acc192 100644
--- a/src/pages/api/index.tsx
+++ b/src/pages/api/index.tsx
@@ -106,6 +106,157 @@ const BlogImageWrapper = styled.img`
height: 100%;
`;
+// `Section` centers itself with auto margins, which stops it from stretching inside the
+// page's flex column. Sections with wide children still fill the layout width, but one
+// holding only text shrinks to fit its sentence, so it needs the width spelled out.
+const TextOnlySection = styled(Section)`
+ width: 100%;
+`;
+
+interface ExperimentalClient {
+ language: string;
+ description: string;
+ repository: string;
+ installLanguage: string;
+ installSnippet: string;
+ exampleLanguage: string;
+ exampleSnippet: string;
+}
+
+const experimentalClients: ExperimentalClient[] = [
+ {
+ language: 'Go',
+ description: 'Client for Go 1.23 or newer, built almost entirely on the standard library.',
+ repository: 'https://github.com/apify/apify-client-go',
+ installLanguage: 'bash',
+ installSnippet: 'go get github.com/apify/apify-client-go',
+ exampleLanguage: 'go',
+ exampleSnippet: `package main
+
+import (
+ "context"
+ "fmt"
+ "log"
+
+ apify "github.com/apify/apify-client-go"
+)
+
+func main() {
+ client := apify.NewClient(apify.WithToken("MY-APIFY-TOKEN"))
+ ctx := context.Background()
+
+ // Starts an Actor and waits for it to finish.
+ run, err := client.Actor("john-doe/my-cool-actor").Call(ctx, nil, apify.ActorStartOptions{}, nil)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ // Fetches results from the Actor's dataset.
+ page, err := client.Dataset(run.DefaultDatasetID).ListItems(ctx, apify.DatasetListItemsOptions{})
+ if err != nil {
+ log.Fatal(err)
+ }
+ fmt.Printf("Got %d items\\n", page.Total)
+}`,
+ },
+ {
+ language: 'PHP',
+ description: 'Client for PHP 8.1 or newer, with Guzzle as the default HTTP transport.',
+ repository: 'https://github.com/apify/apify-client-php',
+ installLanguage: 'bash',
+ installSnippet: 'composer require apify/apify-client',
+ exampleLanguage: 'php',
+ exampleSnippet: `actor('john-doe/my-cool-actor')->call(null, null, null);
+
+// Fetches results from the Actor's dataset.
+$items = $client->dataset((string) $run->getDefaultDatasetId())->listItems();
+echo 'Got ' . $items->getCount() . ' items' . PHP_EOL;`,
+ },
+ {
+ language: 'Java',
+ description: 'Client for Java 17 or newer, published to Maven Central. Every call returns a CompletableFuture.',
+ repository: 'https://github.com/apify/apify-client-java',
+ installLanguage: 'xml',
+ installSnippet: `
+
+ com.apify
+ apify-client
+ 0.5.0
+`,
+ exampleLanguage: 'java',
+ exampleSnippet: `import com.apify.client.ApifyClient;
+import com.apify.client.actor.ActorStartOptions;
+import com.apify.client.dataset.DatasetListItemsOptions;
+import com.apify.client.run.ActorRun;
+
+ApifyClient client = ApifyClient.create("MY-APIFY-TOKEN");
+
+// Starts an Actor and waits for it to finish.
+ActorRun run = client.actor("john-doe/my-cool-actor").call(null, new ActorStartOptions(), 120L).join();
+
+// Fetches results from the Actor's dataset.
+var items = client.dataset(run.getDefaultDatasetId()).listItems(new DatasetListItemsOptions()).join();
+System.out.println("Got " + items.getCount() + " items");`,
+ },
+ {
+ language: '.NET',
+ description: 'Client for .NET 8.0 or newer, with cancellation-aware asynchronous calls.',
+ repository: 'https://github.com/apify/apify-client-dotnet',
+ installLanguage: 'bash',
+ installSnippet: 'dotnet add package Apify.Client',
+ exampleLanguage: 'csharp',
+ exampleSnippet: `using System;
+using Apify.Client;
+
+var client = new ApifyClient("MY-APIFY-TOKEN");
+
+// Starts an Actor and waits for it to finish.
+var run = await client.Actor("john-doe/my-cool-actor").CallAsync(null, null, null);
+
+// Fetches results from the Actor's dataset.
+var items = await client.Dataset(run.DefaultDatasetId!).ListItemsAsync();
+Console.WriteLine($"Got {items.Count} items");`,
+ },
+ {
+ language: 'Rust',
+ description: 'Async client for Tokio-based Rust applications, built on reqwest.',
+ repository: 'https://github.com/apify/apify-client-rust',
+ installLanguage: 'bash',
+ installSnippet: `cargo add apify-client serde_json
+cargo add tokio --features macros,rt-multi-thread`,
+ exampleLanguage: 'rust',
+ exampleSnippet: `use apify_client::ApifyClient;
+
+#[tokio::main]
+async fn main() -> Result<(), Box> {
+ let client = ApifyClient::new("MY-APIFY-TOKEN");
+
+ // Starts an Actor and waits for it to finish.
+ let run = client
+ .actor("john-doe/my-cool-actor")
+ .call::(None, Default::default(), None)
+ .await?;
+
+ // Fetches results from the Actor's dataset.
+ let dataset_id = run.default_dataset_id.expect("run has a default dataset");
+ let items = client
+ .dataset(&dataset_id)
+ .list_items::(Default::default())
+ .await?;
+ println!("Got {} items", items.items.len());
+
+ Ok(())
+}`,
+ },
+];
+
export default function Api() {
return (
@@ -146,6 +297,23 @@ curl -X POST -d @- \\
+
+ You can download the complete OpenAPI schema of the Apify API in the{' '}
+ YAML or{' '}
+ JSON formats. The source code is
+ also{' '}
+
+ available on GitHub
+
+ .
+ >
+ }
+ />
+
+ ({
+ title: client.language,
+ content: (
+
+ {client.description}
+
+
+
+ View reference
+
+
+
+ }
+ >
+
+ {client.installSnippet}
+ {client.exampleSnippet}
+
+
+ ),
+ }))}
+ />
+