From a99b0ff3e981e2bfc3695c576f32ed12f8aef122 Mon Sep 17 00:00:00 2001 From: Asaf Dafner Date: Sun, 19 Apr 2026 10:32:34 +0100 Subject: [PATCH 1/7] feat: add Hebrew locale and i18n infrastructure Add Hebrew (he) locale with RTL direction, locale dropdown in navbar, and bilingual (en/he) search configuration. Co-Authored-By: Claude Opus 4.6 --- website/docusaurus.config.ts | 24 ++++++++++++++++++----- website/package-lock.json | 37 +----------------------------------- 2 files changed, 20 insertions(+), 41 deletions(-) diff --git a/website/docusaurus.config.ts b/website/docusaurus.config.ts index 5d3b674..101243b 100644 --- a/website/docusaurus.config.ts +++ b/website/docusaurus.config.ts @@ -57,12 +57,22 @@ const config: Config = { onBrokenLinks: 'warn', - // Even if you don't use internationalization, you can use this field to set - // useful metadata like html lang. For example, if your site is Chinese, you - // may want to replace "en" with "zh-Hans". + // Internationalization configuration for English and Hebrew i18n: { defaultLocale: 'en', - locales: ['en'], + locales: ['en', 'he'], + localeConfigs: { + en: { + label: 'English', + direction: 'ltr', + htmlLang: 'en-US', + }, + he: { + label: 'עברית', + direction: 'rtl', + htmlLang: 'he', + }, + }, }, markdown: { @@ -138,7 +148,7 @@ const config: Config = { require.resolve('@easyops-cn/docusaurus-search-local'), { hashed: true, - language: ['en'], + language: ['en', 'he'], highlightSearchTermsOnTargetPage: true, explicitSearchResultPath: true, indexBlog: false, @@ -205,6 +215,10 @@ const config: Config = { position: 'left', label: 'FAQ', }, + { + type: 'localeDropdown', + position: 'right', + }, { href: 'https://github.com/agenticoding/agenticoding.github.io', label: 'GitHub', diff --git a/website/package-lock.json b/website/package-lock.json index f22f5bf..694f059 100644 --- a/website/package-lock.json +++ b/website/package-lock.json @@ -247,7 +247,6 @@ "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.40.1.tgz", "integrity": "sha512-Mw6pAUF121MfngQtcUb5quZVqMC68pSYYjCRZkSITC085S3zdk+h/g7i6FxnVdbSU6OztxikSDMh1r7Z+4iPlA==", "license": "MIT", - "peer": true, "dependencies": { "@algolia/client-common": "5.40.1", "@algolia/requester-browser-xhr": "5.40.1", @@ -395,7 +394,6 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.4.tgz", "integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==", "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.3", @@ -2230,7 +2228,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=18" }, @@ -2253,7 +2250,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=18" } @@ -2363,7 +2359,6 @@ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", "license": "MIT", - "peer": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -2785,7 +2780,6 @@ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", "license": "MIT", - "peer": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -3673,7 +3667,6 @@ "resolved": "https://registry.npmjs.org/@docusaurus/plugin-content-docs/-/plugin-content-docs-3.9.2.tgz", "integrity": "sha512-C5wZsGuKTY8jEYsqdxhhFOe1ZDjH0uIYJ9T/jebHwkyxqnr4wW0jTkB72OMqNjsoQRcb0JN3PcSeTwFlVgzCZg==", "license": "MIT", - "peer": true, "dependencies": { "@docusaurus/core": "3.9.2", "@docusaurus/logger": "3.9.2", @@ -3942,7 +3935,6 @@ "resolved": "https://registry.npmjs.org/@docusaurus/theme-common/-/theme-common-3.9.2.tgz", "integrity": "sha512-6c4DAbR6n6nPbnZhY2V3tzpnKnGL+6aOsLvFL26VRqhlczli9eWG0VDUNoCQEPnGwDMhPS42UhSAnz5pThm5Ag==", "license": "MIT", - "peer": true, "dependencies": { "@docusaurus/mdx-loader": "3.9.2", "@docusaurus/module-type-aliases": "3.9.2", @@ -4786,7 +4778,6 @@ "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-3.1.1.tgz", "integrity": "sha512-f++rKLQgUVYDAtECQ6fn/is15GkEH9+nZPM3MS0RcxVqoTfawHvDlSCH7JbMhAM6uJ32v3eXLvLmLvjGu7PTQw==", "license": "MIT", - "peer": true, "dependencies": { "@types/mdx": "^2.0.0" }, @@ -5142,7 +5133,6 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz", "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==", "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -5570,7 +5560,6 @@ "resolved": "https://registry.npmjs.org/@svgr/core/-/core-8.1.0.tgz", "integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==", "license": "MIT", - "peer": true, "dependencies": { "@babel/core": "^7.21.3", "@svgr/babel-preset": "8.1.0", @@ -6211,7 +6200,6 @@ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.2.tgz", "integrity": "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==", "license": "MIT", - "peer": true, "dependencies": { "csstype": "^3.0.2" } @@ -6394,7 +6382,6 @@ "integrity": "sha512-BnOroVl1SgrPLywqxyqdJ4l3S2MsKVLDVxZvjI1Eoe8ev2r3kGDo+PcMihNmDE+6/KjkTubSJnmqGZZjQSBq/g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.46.2", "@typescript-eslint/types": "8.46.2", @@ -6841,7 +6828,6 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -6927,7 +6913,6 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -6973,7 +6958,6 @@ "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.40.1.tgz", "integrity": "sha512-iUNxcXUNg9085TJx0HJLjqtDE0r1RZ0GOGrt8KNQqQT5ugu8lZsHuMUYW/e0lHhq6xBvmktU9Bw4CXP9VQeKrg==", "license": "MIT", - "peer": true, "dependencies": { "@algolia/abtesting": "1.6.1", "@algolia/client-abtesting": "5.40.1", @@ -7607,7 +7591,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.8.9", "caniuse-lite": "^1.0.30001746", @@ -7907,7 +7890,6 @@ "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-11.0.3.tgz", "integrity": "sha512-ci2iJH6LeIkvP9eJW6gpueU8cnZhv85ELY8w8WiFtNjMHA5ad6pQLaJo9mEly/9qUyCpvqX8/POVUTf18/HFdw==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@chevrotain/cst-dts-gen": "11.0.3", "@chevrotain/gast": "11.0.3", @@ -8699,7 +8681,6 @@ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", "license": "MIT", - "peer": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -9019,7 +9000,6 @@ "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.33.1.tgz", "integrity": "sha512-iJc4TwyANnOGR1OmWhsS9ayRS3s+XQ185FmuHObThD+5AeJCakAAbWv8KimMTt08xCCLNgneQwFp+JRJOr9qGQ==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10" } @@ -9441,7 +9421,6 @@ "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", "license": "ISC", - "peer": true, "engines": { "node": ">=12" } @@ -10389,7 +10368,6 @@ "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -11262,7 +11240,6 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -17181,7 +17158,6 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -17933,7 +17909,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -18837,7 +18812,6 @@ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", "license": "MIT", - "peer": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -19674,7 +19648,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz", "integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -19684,7 +19657,6 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz", "integrity": "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==", "license": "MIT", - "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -19759,7 +19731,6 @@ "resolved": "https://registry.npmjs.org/@docusaurus/react-loadable/-/react-loadable-6.0.0.tgz", "integrity": "sha512-YMMxTUQV/QFSnbgrP3tjDzLHRg7vsbMn8e9HAa8o/1iXoiomo48b7sk/kkmWEuWNDPJVlKSJRB6Y2fHqdJk+SQ==", "license": "MIT", - "peer": true, "dependencies": { "@types/react": "*" }, @@ -19788,7 +19759,6 @@ "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.3.4.tgz", "integrity": "sha512-Ys9K+ppnJah3QuaRiLxk+jDWOR1MekYQrlytiXxC1RyfbdsZkS5pvKAzCCr031xHixZwpnsYNT5xysdFHQaYsA==", "license": "MIT", - "peer": true, "dependencies": { "@babel/runtime": "^7.12.13", "history": "^4.9.0", @@ -22135,8 +22105,7 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD", - "peer": true + "license": "0BSD" }, "node_modules/type-check": { "version": "0.4.0", @@ -22290,7 +22259,6 @@ "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", "devOptional": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -22666,7 +22634,6 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -22932,7 +22899,6 @@ "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.102.1.tgz", "integrity": "sha512-7h/weGm9d/ywQ6qzJ+Xy+r9n/3qgp/thalBbpOi5i223dPXKi04IBtqPN9nTd+jBc7QKfvDbaBnFipYp4sJAUQ==", "license": "MIT", - "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.8", @@ -23716,7 +23682,6 @@ "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.12.tgz", "integrity": "sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ==", "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } From b2ed243ae6177a58d6445de8b56f0c574528d39c Mon Sep 17 00:00:00 2001 From: Asaf Dafner Date: Sun, 19 Apr 2026 10:36:27 +0100 Subject: [PATCH 2/7] fix: make locale dropdown click-only instead of hover-triggered Swizzle DropdownNavbarItem to control open state via click, with outside-click and keyboard handlers. CSS hides the menu until the component explicitly adds dropdown--show. Co-Authored-By: Claude Opus 4.6 --- website/src/css/custom.css | 21 +++ .../DropdownNavbarItem/Desktop/index.tsx | 86 +++++++++ .../DropdownNavbarItem/Mobile/index.tsx | 164 ++++++++++++++++++ .../Mobile/styles.module.css | 3 + .../NavbarItem/DropdownNavbarItem/index.tsx | 17 ++ 5 files changed, 291 insertions(+) create mode 100644 website/src/theme/NavbarItem/DropdownNavbarItem/Desktop/index.tsx create mode 100644 website/src/theme/NavbarItem/DropdownNavbarItem/Mobile/index.tsx create mode 100644 website/src/theme/NavbarItem/DropdownNavbarItem/Mobile/styles.module.css create mode 100644 website/src/theme/NavbarItem/DropdownNavbarItem/index.tsx diff --git a/website/src/css/custom.css b/website/src/css/custom.css index db45273..6bfd961 100644 --- a/website/src/css/custom.css +++ b/website/src/css/custom.css @@ -354,3 +354,24 @@ div[class*='announcementBar'] { border-bottom: 2px solid rgba(255, 255, 255, 0.1); box-shadow: 0 2px 12px rgba(234, 88, 12, 0.3); } + +/* ======================================================================== + LOCALE DROPDOWN - Click-only behavior (disable hover) + ======================================================================== + Override Docusaurus default hover behavior for the language selector. + The dropdown now only opens on click, not on hover. + ======================================================================== */ + +/* Disable hover behavior for locale dropdown */ +.navbar__item.dropdown.dropdown--hoverable:has([class*='iconLanguage']) .dropdown__menu { + visibility: hidden; + opacity: 0; + transition: visibility 0.2s, opacity 0.2s; +} + +/* Show dropdown only when explicitly shown (via click/keyboard) */ +.navbar__item.dropdown.dropdown--hoverable.dropdown--show:has([class*='iconLanguage']) .dropdown__menu { + visibility: visible; + opacity: 1; +} + diff --git a/website/src/theme/NavbarItem/DropdownNavbarItem/Desktop/index.tsx b/website/src/theme/NavbarItem/DropdownNavbarItem/Desktop/index.tsx new file mode 100644 index 0000000..ce1e89e --- /dev/null +++ b/website/src/theme/NavbarItem/DropdownNavbarItem/Desktop/index.tsx @@ -0,0 +1,86 @@ +/** + * Swizzled from @docusaurus/theme-classic to change dropdown behavior + * from hover-to-open to click-to-open for the locale selector. + */ + +import React, {useState, useRef, useEffect, type ReactNode} from 'react'; +import clsx from 'clsx'; +import NavbarNavLink from '@theme/NavbarItem/NavbarNavLink'; +import NavbarItem from '@theme/NavbarItem'; +import type {Props} from '@theme/NavbarItem/DropdownNavbarItem/Desktop'; + +export default function DropdownNavbarItemDesktop({ + items, + position, + className, + onClick, + ...props +}: Props): ReactNode { + const dropdownRef = useRef(null); + const [showDropdown, setShowDropdown] = useState(false); + + useEffect(() => { + const handleClickOutside = ( + event: MouseEvent | TouchEvent | FocusEvent, + ) => { + if ( + !dropdownRef.current || + dropdownRef.current.contains(event.target as Node) + ) { + return; + } + setShowDropdown(false); + }; + + document.addEventListener('mousedown', handleClickOutside); + document.addEventListener('touchstart', handleClickOutside); + document.addEventListener('focusin', handleClickOutside); + + return () => { + document.removeEventListener('mousedown', handleClickOutside); + document.removeEventListener('touchstart', handleClickOutside); + document.removeEventListener('focusin', handleClickOutside); + }; + }, [dropdownRef]); + + return ( +
+ { + if (!props.to) { + e.preventDefault(); + } + setShowDropdown(!showDropdown); + }} + onKeyDown={(e) => { + if (e.key === 'Enter') { + e.preventDefault(); + setShowDropdown(!showDropdown); + } + }}> + {props.children ?? props.label} + +
    + {items.map((childItemProps, i) => ( + + ))} +
+
+ ); +} diff --git a/website/src/theme/NavbarItem/DropdownNavbarItem/Mobile/index.tsx b/website/src/theme/NavbarItem/DropdownNavbarItem/Mobile/index.tsx new file mode 100644 index 0000000..8ad3820 --- /dev/null +++ b/website/src/theme/NavbarItem/DropdownNavbarItem/Mobile/index.tsx @@ -0,0 +1,164 @@ +/** + * Swizzled from @docusaurus/theme-classic - Mobile dropdown unchanged. + * Only Desktop version modified for click-to-open behavior. + */ + +import React, {useEffect, type ReactNode, type ComponentProps} from 'react'; +import clsx from 'clsx'; +import { + isRegexpStringMatch, + useCollapsible, + Collapsible, +} from '@docusaurus/theme-common'; +import {isSamePath, useLocalPathname} from '@docusaurus/theme-common/internal'; +import {translate} from '@docusaurus/Translate'; +import NavbarNavLink from '@theme/NavbarItem/NavbarNavLink'; +import NavbarItem, {type LinkLikeNavbarItemProps} from '@theme/NavbarItem'; +import type {Props} from '@theme/NavbarItem/DropdownNavbarItem/Mobile'; +import styles from './styles.module.css'; + +function isItemActive( + item: LinkLikeNavbarItemProps, + localPathname: string, +): boolean { + if (isSamePath(item.to, localPathname)) { + return true; + } + if (isRegexpStringMatch(item.activeBaseRegex, localPathname)) { + return true; + } + if (item.activeBasePath && localPathname.startsWith(item.activeBasePath)) { + return true; + } + return false; +} + +function containsActiveItems( + items: readonly LinkLikeNavbarItemProps[], + localPathname: string, +): boolean { + return items.some((item) => isItemActive(item, localPathname)); +} + +function CollapseButton({ + collapsed, + onClick, +}: { + collapsed: boolean; + onClick: ComponentProps<'button'>['onClick']; +}) { + return ( +