diff --git a/AGENTS.md b/AGENTS.md index eb934717..924ee246 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -78,27 +78,6 @@ bun run pw:run:webkit ## Code Style Guidelines -### Imports - -Imported modules must be ordered alphabetically with newlines between groups: - -1. Built-in modules -2. External dependencies -3. Parent directory imports -4. Sibling imports -5. Index imports - -Internal `@/**/*` paths go before parent imports. - -```typescript -// Correct order -import type { AutocompleteOptions } from '@algolia/autocomplete-core'; -import React, { type JSX } from 'react'; - -import { DocSearchButton } from './DocSearchButton'; -import type { DocSearchHit } from './types'; -``` - ### TypeScript - Use `type` imports for type-only imports: `import type { Foo } from './types'` @@ -137,9 +116,8 @@ function createStorage(key: string): StorageInterface { ### React Component Structure -- `src/components/ui/` contains reusable rendering components. +- `src/components/ui/` contains small, composable, building block components that are used to construct larger components (Button, Popover, Menu, etc...). - Prefer domain-light primitives in `src/components/ui/` when possible. -- Feature-scoped UI components may live in `src/components/ui/` when their feature scope is explicit in the filename, such as `RecentConversationsResults.tsx`. - `src/components/` contains scoped composition components that own feature flow, branching, and state orchestration. - Keep AI-specific behavior out of generic UI primitives. If a UI component is AI-specific, make that scope clear in its name. diff --git a/adapters/docusaurus-theme-search-algolia/src/__tests__/validateThemeConfig.test.ts b/adapters/docusaurus-theme-search-algolia/src/__tests__/validateThemeConfig.test.ts index 0fa4835f..529d9cd4 100644 --- a/adapters/docusaurus-theme-search-algolia/src/__tests__/validateThemeConfig.test.ts +++ b/adapters/docusaurus-theme-search-algolia/src/__tests__/validateThemeConfig.test.ts @@ -87,6 +87,20 @@ describe('validateThemeConfig', () => { }); }); + it('accepts hideMCPCallout', () => { + const docsearch: DocSearchInput = { + ...minimalDocSearchConfig, + hideMCPCallout: true, + }; + + expect(testValidateThemeConfig(docsearch)).toEqual({ + docsearch: { + ...DEFAULT_CONFIG, + ...docsearch, + }, + }); + }); + it('rejects missing docsearch config', () => { expectThrowMessage( () => testValidateThemeConfig(undefined), diff --git a/adapters/docusaurus-theme-search-algolia/src/theme-search-algolia.d.ts b/adapters/docusaurus-theme-search-algolia/src/theme-search-algolia.d.ts index 32e3ff52..be66d4f0 100644 --- a/adapters/docusaurus-theme-search-algolia/src/theme-search-algolia.d.ts +++ b/adapters/docusaurus-theme-search-algolia/src/theme-search-algolia.d.ts @@ -76,6 +76,7 @@ declare module '@docsearch/docusaurus-adapter' { | 'recentSearchesWithFavoritesLimit' | 'resultBadgeKey' | 'translations' + | 'hideMCPCallout' > & { indices: NonNullable; askAi?: AskAiConfig; diff --git a/adapters/docusaurus-theme-search-algolia/src/theme/SearchBar/index.tsx b/adapters/docusaurus-theme-search-algolia/src/theme/SearchBar/index.tsx index 33531b6f..c09432e7 100644 --- a/adapters/docusaurus-theme-search-algolia/src/theme/SearchBar/index.tsx +++ b/adapters/docusaurus-theme-search-algolia/src/theme/SearchBar/index.tsx @@ -371,6 +371,13 @@ function DocSearch({ const panelOptions = getSidePanelPanelOptions( typeof sidePanel === 'object' ? sidePanel : undefined ); + const sidepanelTranslations = { + ...panelOptions.translations, + byAlgoliaAriaLabel: + panelOptions.translations?.byAlgoliaAriaLabel ?? + props.translations?.modal?.footer?.byAlgoliaAriaLabel ?? + translations.modal?.footer?.byAlgoliaAriaLabel ?? 'DocSearch by Algolia', + }; return ( <> @@ -420,6 +427,7 @@ function DocSearch({ DocSearchSidepanel && ( ({ searchPage: SearchPageSchema, askAi: AskAiSchema.optional(), sidePanel: Joi.alternatives().try(Joi.boolean(), SidePanelSchema).optional(), + hideMCPCallout: Joi.boolean().optional(), }) .label('themeConfig.docsearch') .unknown(false); diff --git a/packages/docsearch-css/src/modal.css b/packages/docsearch-css/src/modal.css index 176746de..3cb1abd9 100644 --- a/packages/docsearch-css/src/modal.css +++ b/packages/docsearch-css/src/modal.css @@ -664,13 +664,74 @@ font-weight: 500; } -.DocSearch-Logo a { - display: flex; +.DocSearch-ByAlgolia { + display: inline-flex; + align-items: center; + gap: 0.5rem; + min-width: 131px; + color: var(--docsearch-text-color); } -.DocSearch-Logo svg { +.DocSearch-ByAlgolia-mark { color: var(--docsearch-logo-color); - margin-inline-start: 8px; + height: 1.5rem; + width: auto; + flex-shrink: 0; +} + +.DocSearch-ByAlgolia-Lockup { + display: flex; + flex-direction: column; + line-height: 1; +} + +.DocSearch-ByAlgolia-Lockup-title { + font-size: 0.9375em; + font-weight: 600; + letter-spacing: -0.02em; +} + +.DocSearch-ByAlgolia-Lockup-by { + margin-block-start: 0.125rem; + display: flex; + align-items: center; + gap: 0.25rem; + font-size: 0.625em; + font-weight: 500; + letter-spacing: 0.025em; + color: oklch(64% 0.008 75deg); + + svg { + height: 0.75rem; + width: auto; + max-width: 100%; + } +} + +.DocSearch-DocsMCPAction { + a { + font-size: 0.875em; + display: flex; + align-items: center; + gap: 0.25rem; + color: var(--docsearch-text-color); + padding-inline: 0.5rem; + padding-block: 0.25rem; + border-radius: var(--docsearch-border-radius); + background-color: var(--docsearch-hit-focus-background); + min-block-size: 1.5rem; + + &:hover, + &:focus-visible { + outline: 2px solid var(--docsearch-highlight-color); + } + } + + svg { + height: 1rem; + width: 1rem; + color: var(--docsearch-highlight-color); + } } /* Hit */ @@ -1039,15 +1100,30 @@ border-block-start: 1px solid var(--docsearch-subtle-color); border-radius: 0 0 var(--docsearch-modal-radius) var(--docsearch-modal-radius); display: flex; - flex-direction: row-reverse; flex-shrink: 0; height: var(--docsearch-footer-height); - justify-content: space-between; padding: 0 var(--docsearch-spacing); position: relative; user-select: none; width: 100%; z-index: 300; + + @media screen and (width >= 768px) { + justify-content: space-between; + } +} + +.DocSearch-Footer-Actions { + display: flex; + align-items: center; + gap: 1em; + justify-content: space-between; + width: 100%; + + @media screen and (width >= 768px) { + justify-content: unset; + width: auto; + } } .DocSearch-Commands { diff --git a/packages/docsearch-css/src/sidepanel.css b/packages/docsearch-css/src/sidepanel.css index f680280b..9d37d75f 100644 --- a/packages/docsearch-css/src/sidepanel.css +++ b/packages/docsearch-css/src/sidepanel.css @@ -552,10 +552,6 @@ padding: 0.75rem 1rem 1rem; } -.DocSearch-Sidepanel--powered-by { - align-self: flex-end; -} - .DocSearch-Sidepanel-Screen--title { font-size: 1.625rem; font-weight: 600; @@ -743,6 +739,53 @@ right: -4rem; } +.DocSearch-Sidepanel .DocSearch-ByAlgolia { + display: inline-flex; + align-items: center; + gap: 0.25rem; + min-width: 131px; + color: var(--docsearch-text-color); + + .DocSearch-ByAlgolia-mark { + color: var(--docsearch-logo-color); + height: 1rem; + width: auto; + flex-shrink: 0; + } + + .DocSearch-ByAlgolia-Lockup { + display: flex; + flex-direction: row; + align-items: center; + gap: 0.25rem; + } + + .DocSearch-ByAlgolia-Lockup-title { + font-size: 0.9em; + font-weight: 600; + letter-spacing: -0.02em; + } + + .DocSearch-ByAlgolia-Lockup-by { + margin-block-start: 0.125rem; + display: flex; + align-items: center; + gap: 0.25rem; + font-size: 0.75em; + font-weight: 500; + letter-spacing: 0.025em; + color: oklch(64% 0.008 75deg); + border-inline-start: 2px solid var(--docsearch-subtle-color); + padding-inline-start: 0.25rem; + + svg { + height: 0.85rem; + width: auto; + max-width: 100%; + } + } +} + .sr-only { clip-path: inset(50%); white-space: nowrap; diff --git a/packages/docsearch-react/src/AlgoliaLogo.tsx b/packages/docsearch-react/src/AlgoliaLogo.tsx deleted file mode 100644 index fbcb738c..00000000 --- a/packages/docsearch-react/src/AlgoliaLogo.tsx +++ /dev/null @@ -1,81 +0,0 @@ -import React, { type JSX } from 'react'; - -export type AlgoliaLogoTranslations = Partial<{ - poweredByText: string; -}>; - -type AlgoliaLogoProps = { - translations?: AlgoliaLogoTranslations; -}; - -export function AlgoliaLogo({ - translations = {}, -}: AlgoliaLogoProps): JSX.Element { - const { poweredByText = 'Powered by' } = translations; - - return ( - - {poweredByText} - - - - - - - - - - - - - - - - ); -} diff --git a/packages/docsearch-react/src/DocSearch.tsx b/packages/docsearch-react/src/DocSearch.tsx index 2276f904..0b1e8b2c 100644 --- a/packages/docsearch-react/src/DocSearch.tsx +++ b/packages/docsearch-react/src/DocSearch.tsx @@ -154,6 +154,12 @@ export interface DocSearchProps { * @default undefined */ resultBadgeKey?: string; + /** + * Hides the Docs MCP callout in the Footer. + * + * @default false + */ + hideMCPCallout?: boolean; } function DocSearchComponent( diff --git a/packages/docsearch-react/src/DocSearchAskAiModal.tsx b/packages/docsearch-react/src/DocSearchAskAiModal.tsx index 7c363a7f..086e4b55 100644 --- a/packages/docsearch-react/src/DocSearchAskAiModal.tsx +++ b/packages/docsearch-react/src/DocSearchAskAiModal.tsx @@ -101,6 +101,7 @@ export function DocSearchAskAiModal({ indices, facets, isHybridModeSupported = false, + hideMCPCallout, ...props }: DocSearchAskAiModalProps): JSX.Element { const { @@ -653,6 +654,7 @@ export function DocSearchAskAiModal({