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} + +
+ ), + }))} + /> +
diff --git a/src/pages/api/styles.module.css b/src/pages/api/styles.module.css index 1d54e332e8..77c34616fe 100644 --- a/src/pages/api/styles.module.css +++ b/src/pages/api/styles.module.css @@ -8,4 +8,13 @@ font-size: 1.6rem; line-height: 2.4rem; } + + /* + * The theme numbers code lines for an allowlist of languages (JavaScript, Python, JSON and + * a few more), which would number only some of the blocks on this page. These snippets are + * short and nothing refers to them by line, so drop the numbers from all of them. + */ + :global(.prism-code) :global(.token-line)::before { + content: none; + } }