@@ -482,7 +489,7 @@ export default function Home(): ReactNode {
to={`/docs/commands/${commandDocPath(command.name)}`}
className={styles.commandRow}>
{String(index + 1).padStart(2, "0")}
- pressship {command.name}
+ {prefix} {command.name}
{command.description}
diff --git a/website/src/theme/CodeBlock/index.tsx b/website/src/theme/CodeBlock/index.tsx
new file mode 100644
index 0000000..9804564
--- /dev/null
+++ b/website/src/theme/CodeBlock/index.tsx
@@ -0,0 +1,26 @@
+import React, { type ReactNode } from 'react';
+import CodeBlock from '@theme-original/CodeBlock';
+import type CodeBlockType from '@theme/CodeBlock';
+import type { WrapperProps } from '@docusaurus/types';
+import { useInstallMethod } from '@site/src/theme/Root';
+
+type Props = WrapperProps;
+
+export default function CodeBlockWrapper(props: Props): ReactNode {
+ const { prefix } = useInstallMethod();
+
+ let newProps = { ...props };
+
+ if (typeof props.children === 'string' && (props.language === 'bash' || props.language === 'sh' || !props.language)) {
+ let content = props.children;
+ content = content.replace(/npx pressship\b/g, prefix);
+ content = content.replace(/(^|\n)(\s*(?:\$\s+)?)(pressship\b)/g, `$1$2${prefix}`);
+ newProps.children = content;
+ }
+
+ return (
+ <>
+
+ >
+ );
+}
diff --git a/website/src/theme/NavbarItem/ComponentTypes.tsx b/website/src/theme/NavbarItem/ComponentTypes.tsx
new file mode 100644
index 0000000..62ca638
--- /dev/null
+++ b/website/src/theme/NavbarItem/ComponentTypes.tsx
@@ -0,0 +1,27 @@
+import DefaultNavbarItem from '@theme/NavbarItem/DefaultNavbarItem';
+import DropdownNavbarItem from '@theme/NavbarItem/DropdownNavbarItem';
+import LocaleDropdownNavbarItem from '@theme/NavbarItem/LocaleDropdownNavbarItem';
+import SearchNavbarItem from '@theme/NavbarItem/SearchNavbarItem';
+import HtmlNavbarItem from '@theme/NavbarItem/HtmlNavbarItem';
+import DocNavbarItem from '@theme/NavbarItem/DocNavbarItem';
+import DocSidebarNavbarItem from '@theme/NavbarItem/DocSidebarNavbarItem';
+import DocsVersionNavbarItem from '@theme/NavbarItem/DocsVersionNavbarItem';
+import DocsVersionDropdownNavbarItem from '@theme/NavbarItem/DocsVersionDropdownNavbarItem';
+import InstallMethodDropdown from '@theme/NavbarItem/InstallMethodDropdown';
+
+import type {ComponentTypesObject} from '@theme/NavbarItem/ComponentTypes';
+
+const ComponentTypes: ComponentTypesObject = {
+ default: DefaultNavbarItem,
+ localeDropdown: LocaleDropdownNavbarItem,
+ search: SearchNavbarItem,
+ dropdown: DropdownNavbarItem,
+ html: HtmlNavbarItem,
+ doc: DocNavbarItem,
+ docSidebar: DocSidebarNavbarItem,
+ docsVersion: DocsVersionNavbarItem,
+ docsVersionDropdown: DocsVersionDropdownNavbarItem,
+ 'custom-installMethod': InstallMethodDropdown as any,
+};
+
+export default ComponentTypes;
diff --git a/website/src/theme/NavbarItem/InstallMethodDropdown.tsx b/website/src/theme/NavbarItem/InstallMethodDropdown.tsx
new file mode 100644
index 0000000..7a72403
--- /dev/null
+++ b/website/src/theme/NavbarItem/InstallMethodDropdown.tsx
@@ -0,0 +1,39 @@
+import React from 'react';
+import DropdownNavbarItem from '@theme/NavbarItem/DropdownNavbarItem';
+import { useInstallMethod, type InstallMethod } from '@site/src/theme/Root';
+
+export default function InstallMethodDropdown(props: any) {
+ const { method, setMethod } = useInstallMethod();
+
+ const items = [
+ {
+ label: 'npx',
+ isNavLink: true,
+ to: '#',
+ onClick: (e: React.MouseEvent) => { e.preventDefault(); setMethod('npx'); },
+ className: method === 'npx' ? 'dropdown__link--active' : '',
+ },
+ {
+ label: 'npm',
+ isNavLink: true,
+ to: '#',
+ onClick: (e: React.MouseEvent) => { e.preventDefault(); setMethod('npm'); },
+ className: method === 'npm' ? 'dropdown__link--active' : '',
+ },
+ {
+ label: 'wp-cli',
+ isNavLink: true,
+ to: '#',
+ onClick: (e: React.MouseEvent) => { e.preventDefault(); setMethod('wp-cli'); },
+ className: method === 'wp-cli' ? 'dropdown__link--active' : '',
+ },
+ ];
+
+ return (
+
+ );
+}
diff --git a/website/src/theme/Root.tsx b/website/src/theme/Root.tsx
new file mode 100644
index 0000000..0210b1e
--- /dev/null
+++ b/website/src/theme/Root.tsx
@@ -0,0 +1,66 @@
+import React, { createContext, useContext, useState, useEffect, type ReactNode } from "react";
+
+export type InstallMethod = "npx" | "npm" | "wp-cli";
+
+export interface InstallMethodContextType {
+ method: InstallMethod;
+ setMethod: (method: InstallMethod) => void;
+ prefix: string;
+}
+
+export const InstallMethodContext = createContext({
+ method: "npx",
+ setMethod: () => {},
+ prefix: "npx pressship"
+});
+
+export const useInstallMethod = () => useContext(InstallMethodContext);
+
+export function getCommandPrefix(methodLabel: string) {
+ if (methodLabel === "npm") return "pressship";
+ if (methodLabel === "wp-cli") return "wp ship";
+ return "npx pressship";
+}
+
+export default function Root({ children }: { children: ReactNode }) {
+ const [method, setMethodState] = useState("npx");
+
+ useEffect(() => {
+ const saved = localStorage.getItem("pressship-install-method");
+ if (saved === "npx" || saved === "npm" || saved === "wp-cli") {
+ setMethodState(saved);
+ }
+
+ const onStorage = (e: StorageEvent) => {
+ if (e.key === "pressship-install-method" && (e.newValue === "npx" || e.newValue === "npm" || e.newValue === "wp-cli")) {
+ setMethodState(e.newValue);
+ }
+ };
+ window.addEventListener("storage", onStorage);
+ return () => window.removeEventListener("storage", onStorage);
+ }, []);
+
+ const setMethod = (newMethod: InstallMethod) => {
+ setMethodState(newMethod);
+ localStorage.setItem("pressship-install-method", newMethod);
+ // Dispatch a custom event so other components in the same tab can update without waiting
+ window.dispatchEvent(new Event("pressship-method-changed"));
+ };
+
+ useEffect(() => {
+ const onCustomEvent = () => {
+ const saved = localStorage.getItem("pressship-install-method");
+ if (saved === "npx" || saved === "npm" || saved === "wp-cli") {
+ setMethodState(saved);
+ }
+ };
+ window.addEventListener("pressship-method-changed", onCustomEvent);
+ return () => window.removeEventListener("pressship-method-changed", onCustomEvent);
+ }, []);
+
+ return (
+
+ {children}
+
+ );
+}