diff --git a/VERSIONING.md b/VERSIONING.md new file mode 100644 index 0000000..ffc641c --- /dev/null +++ b/VERSIONING.md @@ -0,0 +1,46 @@ +# Versioning Guide + +This project uses Docusaurus versioning to manage documentation for different releases of the Yellow Network protocol and SDK. + +## Structure + +- **`docs/`**: functionality for the **upcoming/current** development version (labeled as "Next" or derived from `package.json`). +- **`versioned_docs/version-0.5.x/`**: Frozen documentation for the `0.5.x` stable release. + +## Workflows + +### 1. Releasing a New Version + +When you are ready to release the current work, this process involves **freezing** the current version (e.g., `0.6.x`) and **creating** the next development version (e.g., `0.7.x`). + +```bash +npm run version:release 0.6.x 0.7.x +``` + +This single command will: +1. Snapshot `docs/` to `versioned_docs/version-0.6.x`. +2. Update `package.json` version to `0.7.x` (which updates the "Next" dropdown label). + +### 2. Updating an Old Version + +To fix a typo or add a note to an old version (e.g., `0.5.x`): +- Edit the files in `versioned_docs/version-0.5.x/`. +- **Do not** edit files in `docs/` for old versions; `docs/` is always the *future* version. + +### 3. Removing an Old Version + +To remove an old version that is no longer supported (e.g., `0.5.x`): + +```bash +npm run version:remove 0.5.x +``` +This performs a safe, complete deletion of the version folder and config entry. + +### 4. Resetting to Single Version + +To delete ALL history and return to a single-version project (e.g., just `0.5.x`): + +```bash +npm run version:reset 0.5.x +``` +**Warning**: This deletes all `versioned_docs` folders. You will be left with only the current content in `docs/`, labeled as `0.5.x`. diff --git a/docusaurus.config.ts b/docusaurus.config.ts index c494f5e..f8f65d2 100644 --- a/docusaurus.config.ts +++ b/docusaurus.config.ts @@ -1,5 +1,5 @@ -import {themes as prismThemes} from 'prism-react-renderer'; -import type {Config} from '@docusaurus/types'; +import { themes as prismThemes } from 'prism-react-renderer'; +import type { Config } from '@docusaurus/types'; import type * as Preset from '@docusaurus/preset-classic'; // This runs in Node.js - Don't use client-side code here (browser APIs, JSX...) @@ -12,6 +12,10 @@ const config: Config = { tagline: 'Decentralized clearing and settlement network.\nDevelop Yellow Apps with instant finality.', favicon: 'img/favicon.ico', + customFields: { + packageVersion: require('./package.json').version, + }, + // Future flags, see https://docusaurus.io/docs/api/docusaurus-config#future future: { v4: true, // Improve compatibility with the upcoming Docusaurus v4 @@ -64,6 +68,19 @@ const config: Config = { sidebarCollapsed: false, sidebarCollapsible: false, breadcrumbs: true, + // includeCurrentVersion: true, + // lastVersion: '0.5.x', + // versions: { + // current: { + // label: require('./package.json').version, + // path: 'next', + // banner: 'unreleased', + // }, + // '0.5.x': { + // label: 'v0.5.x', + // path: '', + // }, + // }, }, blog: false, theme: { @@ -207,19 +224,19 @@ const config: Config = { defaultLanguage: 'javascript', magicComments: [ { - className: 'git-diff-remove', - line: 'remove-next-line', - block: { start: 'remove-start', end: 'remove-end' }, + className: 'git-diff-remove', + line: 'remove-next-line', + block: { start: 'remove-start', end: 'remove-end' }, }, { - className: 'git-diff-add', - line: 'add-next-line', - block: { start: 'add-start', end: 'add-end' }, + className: 'git-diff-add', + line: 'add-next-line', + block: { start: 'add-start', end: 'add-end' }, }, { - className: 'theme-code-block-highlighted-line', - line: 'highlight-next-line', - block: { start: 'highlight-start', end: 'highlight-end' }, + className: 'theme-code-block-highlighted-line', + line: 'highlight-next-line', + block: { start: 'highlight-start', end: 'highlight-end' }, }, ], }, diff --git a/package.json b/package.json index b7bf1f7..a5aa28b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "docs", - "version": "0.0.0", + "version": "0.5.x", "private": true, "scripts": { "docusaurus": "docusaurus", @@ -12,7 +12,10 @@ "serve": "docusaurus serve", "write-translations": "docusaurus write-translations", "write-heading-ids": "docusaurus write-heading-ids", - "typecheck": "tsc" + "typecheck": "tsc", + "version:remove": "node scripts/remove-version.js", + "version:reset": "node scripts/reset-versions.js", + "version:release": "node scripts/release-version.js" }, "dependencies": { "@docusaurus/core": "3.9.2", @@ -53,4 +56,4 @@ "engines": { "node": ">=18.0" } -} +} \ No newline at end of file diff --git a/scripts/release-version.js b/scripts/release-version.js new file mode 100644 index 0000000..be37515 --- /dev/null +++ b/scripts/release-version.js @@ -0,0 +1,47 @@ +const { execSync } = require('child_process'); +const fs = require('fs'); +const path = require('path'); + +const versionToFreeze = process.argv[2]; +const nextDevVersion = process.argv[3]; + +if (!versionToFreeze || !nextDevVersion) { + console.error('Usage: npm run version:release '); + console.error('Example: npm run version:release 0.5.x 0.6.x'); + process.exit(1); +} + +const rootDir = path.resolve(__dirname, '..'); +const packageJsonPath = path.join(rootDir, 'package.json'); +const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')); + +// Validation: Ensure the current project version matches the version being frozen. +// This prevents mistakes like being on 0.5.x but trying to freeze 0.6.x (skipping a version). +if (pkg.version !== versionToFreeze) { + console.error(`Error: Version mismatch.`); + console.error(`You are trying to freeze/release "${versionToFreeze}", but the current project version (in package.json) is "${pkg.version}".`); + console.error(`Please update package.json to "${versionToFreeze}" first, or check your release arguments.`); + process.exit(1); +} + +try { + // 1. Snapshot the current docs as the frozen version + console.log(`Freezing current docs as ${versionToFreeze}...`); + execSync(`npm run docusaurus docs:version ${versionToFreeze}`, { stdio: 'inherit' }); + + // 2. Bump package.json to the new dev version + console.log(`Creating next version ${nextDevVersion} (updating package.json)...`); + + // Update the version in the already loaded pkg object + pkg.version = nextDevVersion; + fs.writeFileSync(packageJsonPath, JSON.stringify(pkg, null, 2)); + + console.log('---------------------------------------------------'); + console.log(`Release Complete!`); + console.log(`- Frozen Version: ${versionToFreeze} (saved in versioned_docs/)`); + console.log(`- Created Next Version: ${nextDevVersion} (active in docs/)`); + +} catch (error) { + console.error('Release failed:', error.message); + process.exit(1); +} diff --git a/scripts/remove-version.js b/scripts/remove-version.js new file mode 100644 index 0000000..271ea62 --- /dev/null +++ b/scripts/remove-version.js @@ -0,0 +1,44 @@ +const fs = require('fs'); +const path = require('path'); + +const version = process.argv[2]; + +if (!version) { + console.error('Please provide a version to remove. Usage: npm run version:remove '); + process.exit(1); +} + +const rootDir = path.resolve(__dirname, '..'); +const versionsJsonPath = path.join(rootDir, 'versions.json'); +const versionedDocsPath = path.join(rootDir, 'versioned_docs', `version-${version}`); +const versionedSidebarsPath = path.join(rootDir, 'versioned_sidebars', `version-${version}-sidebars.json`); + +console.log(`Removing version ${version}...`); + +// 1. updates versions.json +if (fs.existsSync(versionsJsonPath)) { + const versions = JSON.parse(fs.readFileSync(versionsJsonPath, 'utf8')); + const newVersions = versions.filter(v => v !== version); + fs.writeFileSync(versionsJsonPath, JSON.stringify(newVersions, null, 2)); + console.log(`Updated versions.json`); +} else { + console.warn(`versions.json not found at ${versionsJsonPath}`); +} + +// 2. Removes versioned_docs/version- +if (fs.existsSync(versionedDocsPath)) { + fs.rmSync(versionedDocsPath, { recursive: true, force: true }); + console.log(`Removed ${versionedDocsPath}`); +} else { + console.warn(`Versioned docs not found at ${versionedDocsPath}`); +} + +// 3. Removes versioned_sidebars/version--sidebars.json +if (fs.existsSync(versionedSidebarsPath)) { + fs.rmSync(versionedSidebarsPath); + console.log(`Removed ${versionedSidebarsPath}`); +} else { + console.warn(`Versioned sidebar not found at ${versionedSidebarsPath}`); +} + +console.log(`Successfully removed version ${version}.`); diff --git a/scripts/reset-versions.js b/scripts/reset-versions.js new file mode 100644 index 0000000..0e21678 --- /dev/null +++ b/scripts/reset-versions.js @@ -0,0 +1,50 @@ +const fs = require('fs'); +const path = require('path'); + +const targetVersion = process.argv[2]; + +if (!targetVersion) { + console.error('Usage: npm run version:reset '); + console.error('Example: npm run version:reset 0.5.x'); + process.exit(1); +} + +const rootDir = path.resolve(__dirname, '..'); +const versionsJsonPath = path.join(rootDir, 'versions.json'); +const versionedDocsPath = path.join(rootDir, 'versioned_docs'); +const versionedSidebarsPath = path.join(rootDir, 'versioned_sidebars'); +const packageJsonPath = path.join(rootDir, 'package.json'); + +console.log(`WARNING: This will delete ALL historical versions and reset the project to single version '${targetVersion}'.`); + +// 1. Delete versioned folders +if (fs.existsSync(versionedDocsPath)) { + fs.rmSync(versionedDocsPath, { recursive: true, force: true }); + console.log('Removed versioned_docs/'); +} + +if (fs.existsSync(versionedSidebarsPath)) { + fs.rmSync(versionedSidebarsPath, { recursive: true, force: true }); + console.log('Removed versioned_sidebars/'); +} + +// 2. Delete versions.json +if (fs.existsSync(versionsJsonPath)) { + fs.rmSync(versionsJsonPath); + console.log('Removed versions.json'); +} + +// 3. Update package.json version +if (fs.existsSync(packageJsonPath)) { + const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')); + pkg.version = targetVersion; + fs.writeFileSync(packageJsonPath, JSON.stringify(pkg, null, 2)); + console.log(`Updated package.json version to ${targetVersion}`); +} + +console.log('---------------------------------------------------'); +console.log(`Reset Complete. The project is now single-version: ${targetVersion}`); +console.log('The "Next" version in the dropdown will now show this version.'); +console.log(''); +console.log('IMPORTANT: You must manually update docusaurus.config.ts to disable default versioning'); +console.log('configuration (comment out "lastVersion", "versions", etc.) until you serve a new version.'); diff --git a/src/theme/DocSidebar/index.tsx b/src/theme/DocSidebar/index.tsx index 8145c24..1caeed6 100644 --- a/src/theme/DocSidebar/index.tsx +++ b/src/theme/DocSidebar/index.tsx @@ -1,49 +1,91 @@ -import React, {useMemo} from 'react'; -import {useLocation} from '@docusaurus/router'; -import type {Props} from '@theme/DocSidebar'; -import DocSidebar from '@theme-original/DocSidebar'; +import React, { type ReactNode } from 'react'; +import OriginalSidebar from '@theme-original/DocSidebar'; +import { useVersions, useActiveVersion } from '@docusaurus/plugin-content-docs/client'; +import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; +// @ts-ignore - CSS modules are not typed by default in docusaurus setup without extra config +import styles from './styles.module.css'; +import type { PropSidebar } from '@docusaurus/plugin-content-docs'; -function CustomDocSidebar(props: Props): JSX.Element { - const location = useLocation(); +type Props = { + path: string; + sidebar: PropSidebar; + onCollapse: () => void; + isHidden: boolean; + [key: string]: any; +}; + +function VersionSwitcher() { + const { siteConfig } = useDocusaurusContext(); + const versions = useVersions(); + const activeVersion = useActiveVersion(); + const currentVersionLabel = (siteConfig.customFields?.packageVersion as string) || 'Next'; - const filteredSidebar = useMemo(() => { - if (!props.sidebar) return null; - - // Determine current section from pathname - const pathSegments = location.pathname.split('/').filter(Boolean); - const currentSection = pathSegments[1]; // docs/[section]/... - - if (!currentSection || currentSection === 'docs') { - return props.sidebar; - } - - // Filter sidebar items to show only current section - const filteredItems = props.sidebar.filter((item) => { - if (item.type === 'category') { - // Check if category belongs to current section by examining the category's items - return item.items && item.items.some((subItem: any) => { - const subItemPath = subItem.href || subItem.id || ''; - return subItemPath.includes(currentSection); - }); - } - - if (item.type === 'doc') { - const itemPath = (item as any).href || (item as any).id || ''; - return itemPath.includes(currentSection); - } - - if (item.type === 'link') { - const itemPath = (item as any).href || ''; - return itemPath.includes(currentSection); - } - - return false; - }); - - return filteredItems; - }, [props.sidebar, location.pathname]); + return ( +
+ + + + + + +
+ ); } -export default CustomDocSidebar; \ No newline at end of file +export default function DocSidebarWrapper(props: Props): ReactNode { + return ( + <> + + + + ); +} \ No newline at end of file diff --git a/src/theme/DocSidebar/styles.module.css b/src/theme/DocSidebar/styles.module.css new file mode 100644 index 0000000..642a884 --- /dev/null +++ b/src/theme/DocSidebar/styles.module.css @@ -0,0 +1,47 @@ +.versionSwitcher { + display: flex; + align-items: center; + gap: 8px; + padding: 12px 16px; + border-bottom: 1px solid var(--ifm-color-emphasis-200); + margin-bottom: 8px; + /* Fix for overlapping with fixed Navbar */ + margin-top: calc(var(--ifm-navbar-height) + 1rem); + background-color: var(--ifm-background-surface-color); + z-index: 100; + position: relative; +} + +.githubLink { + display: flex; + align-items: center; + color: var(--ifm-color-emphasis-700); + transition: color 0.2s; +} + +.githubLink:hover { + color: var(--ifm-color-primary); +} + +.versionSelect { + background: transparent; + border: none; + font-size: 14px; + font-weight: 500; + cursor: pointer; + padding: 4px 8px; + color: var(--ifm-font-color-base); + /* appearance: none; Was hiding the arrow. Removing it to show default browser arrow for now. */ +} + +/* Custom dropdown arrow styling to match wireframe "v0.53 ▼" */ +.versionDropdown { + position: relative; + display: inline-flex; + align-items: center; + cursor: pointer; +} + +.versionSelect:focus { + outline: none; +} \ No newline at end of file