From 9d8f2518b66bf70a58a4f30b8b3b48e845d37e92 Mon Sep 17 00:00:00 2001
From: Zeno Kapitein
Date: Mon, 30 Jun 2025 15:32:54 +0200
Subject: [PATCH 01/35] Initial try-out
---
.../gitbook/src/components/Header/Header.tsx | 62 ++++----
.../src/components/Search/SearchButton.tsx | 148 ++++++++----------
.../src/components/Search/SearchContainer.tsx | 136 ++++++++++++++++
.../Search/SearchPageResultItem.tsx | 8 +-
.../src/components/Search/SearchResults.tsx | 2 +-
.../components/SpaceLayout/SpaceLayout.tsx | 11 +-
.../src/components/primitives/Button.tsx | 1 +
.../src/components/primitives/Popover.tsx | 22 ++-
.../src/components/primitives/styles.ts | 6 +-
9 files changed, 246 insertions(+), 150 deletions(-)
create mode 100644 packages/gitbook/src/components/Search/SearchContainer.tsx
diff --git a/packages/gitbook/src/components/Header/Header.tsx b/packages/gitbook/src/components/Header/Header.tsx
index b10392d1d1..addf5e6550 100644
--- a/packages/gitbook/src/components/Header/Header.tsx
+++ b/packages/gitbook/src/components/Header/Header.tsx
@@ -6,7 +6,7 @@ import { getSpaceLanguage, t } from '@/intl/server';
import { tcls } from '@/lib/tailwind';
import { AIChatButton } from '../AIChat/AIChatButton';
-import { SearchButton } from '../Search';
+import { SearchContainer } from '../Search/SearchContainer';
import { SiteSectionTabs, encodeClientSiteSections } from '../SiteSections';
import { HeaderLink } from './HeaderLink';
import { HeaderLinkMore } from './HeaderLinkMore';
@@ -123,44 +123,38 @@ export function Header(props: {
)}
>
- 1}
+ spaceTitle={siteSpace.title}
+ // style={[
+ // 'theme-bold:bg-header-link/2',
+ // 'theme-bold:hover:bg-header-link/3',
- 'theme-bold:text-header-link/8',
- 'theme-bold:hover:text-header-link',
+ // 'theme-bold:text-header-link/8',
+ // 'theme-bold:hover:text-header-link',
- 'theme-bold:ring-header-link/4',
- 'theme-bold:hover:ring-header-link/5',
+ // 'theme-bold:ring-header-link/4',
+ // 'theme-bold:hover:ring-header-link/5',
- 'theme-bold:[&_svg]:text-header-link/10',
- 'theme-bold:[&_.shortcut]:text-header-link/8',
+ // 'theme-bold:[&_svg]:text-header-link/10',
+ // 'theme-bold:[&_.shortcut]:text-header-link/8',
- 'theme-bold:contrast-more:bg-header-background',
- 'theme-bold:contrast-more:text-header-link',
- 'theme-bold:contrast-more:ring-header-link',
- 'theme-bold:contrast-more:hover:bg-header-background',
- 'theme-bold:contrast-more:hover:ring-header-link',
- 'theme-bold:contrast-more:focus:text-header-link',
- 'theme-bold:contrast-more:focus:bg-header-background',
- 'theme-bold:contrast-more:focus:ring-header-link',
+ // 'theme-bold:contrast-more:bg-header-background',
+ // 'theme-bold:contrast-more:text-header-link',
+ // 'theme-bold:contrast-more:ring-header-link',
+ // 'theme-bold:contrast-more:hover:bg-header-background',
+ // 'theme-bold:contrast-more:hover:ring-header-link',
+ // 'theme-bold:contrast-more:focus:text-header-link',
+ // 'theme-bold:contrast-more:focus:bg-header-background',
+ // 'theme-bold:contrast-more:focus:ring-header-link',
- 'theme-bold:shadow-none',
- 'theme-bold:hover:shadow-none',
- 'whitespace-nowrap',
- ]}
- >
-
- {t(
- getSpaceLanguage(customization),
- customization.aiSearch.enabled
- ? 'search_or_ask'
- : 'search'
- )}
- ...
-
-
+ // 'theme-bold:shadow-none',
+ // 'theme-bold:hover:shadow-none',
+ // 'whitespace-nowrap',
+ // ]}
+ />
{withAIChat && }
diff --git a/packages/gitbook/src/components/Search/SearchButton.tsx b/packages/gitbook/src/components/Search/SearchButton.tsx
index 1c4cd02a68..d343ac1a7f 100644
--- a/packages/gitbook/src/components/Search/SearchButton.tsx
+++ b/packages/gitbook/src/components/Search/SearchButton.tsx
@@ -1,107 +1,85 @@
'use client';
import { Icon } from '@gitbook/icons';
-import { useEffect, useState } from 'react';
+import type React from 'react';
+import { useEffect, useRef, useState } from 'react';
import { tString, useLanguage } from '@/intl/client';
-import { type ClassValue, tcls } from '@/lib/tailwind';
-import { useTrackEvent } from '../Insights';
-import { useSearch } from './useSearch';
+import { tcls } from '@/lib/tailwind';
+import { useHotkeys } from 'react-hotkeys-hook';
+
+interface SearchButtonProps {
+ onChange: (event: React.ChangeEvent) => void;
+ onKeyDown: (event: React.KeyboardEvent) => void;
+ onFocus: () => void;
+ onBlur: () => void;
+ value: string;
+ withAsk?: boolean;
+}
/**
* Button to open the search modal.
*/
-export function SearchButton(props: { children?: React.ReactNode; style?: ClassValue }) {
- const { style, children } = props;
+export function SearchButton(props: SearchButtonProps) {
+ const { onChange, onKeyDown, onFocus, onBlur, value, withAsk = false } = props;
const language = useLanguage();
- const [, setSearchState] = useSearch();
- const trackEvent = useTrackEvent();
-
- const onClick = () => {
- setSearchState({
- ask: false,
- global: false,
- query: '',
- });
-
- trackEvent({
- type: 'search_open',
- });
+
+ const inputRef = useRef(null);
+
+ const [isOpen, setIsOpen] = useState(false);
+
+ const handleFocus = () => {
+ onFocus();
+ setIsOpen(true);
};
+ const handleBlur = () => {
+ onBlur();
+ setIsOpen(false);
+ };
+
+ useEffect(() => {
+ if (isOpen === true) {
+ inputRef.current?.focus();
+ }
+ }, [isOpen]);
+
+ useHotkeys(
+ 'mod+k',
+ (e) => {
+ e.preventDefault();
+ handleFocus();
+ },
+ []
+ );
+
return (
-
);
}
diff --git a/packages/gitbook/src/components/Search/SearchModal.tsx b/packages/gitbook/src/components/Search/SearchModal.tsx
deleted file mode 100644
index 776f331dcd..0000000000
--- a/packages/gitbook/src/components/Search/SearchModal.tsx
+++ /dev/null
@@ -1,309 +0,0 @@
-'use client';
-
-import { Icon } from '@gitbook/icons';
-import { AnimatePresence, motion } from 'framer-motion';
-import { useRouter } from 'next/navigation';
-import React from 'react';
-import { useHotkeys } from 'react-hotkeys-hook';
-
-import { tString, useLanguage } from '@/intl/client';
-import { tcls } from '@/lib/tailwind';
-
-import { LoadingPane } from '../primitives/LoadingPane';
-import { SearchAskAnswer } from './SearchAskAnswer';
-import { SearchAskProvider, useSearchAskState } from './SearchAskContext';
-import { SearchResults, type SearchResultsRef } from './SearchResults';
-import { SearchScopeToggle } from './SearchScopeToggle';
-import { type SearchState, type UpdateSearchState, useSearch } from './useSearch';
-
-interface SearchModalProps {
- spaceTitle: string;
- isMultiVariants: boolean;
- withAsk: boolean;
- withAIChat: boolean;
-}
-
-/**
- * Client component to render the search modal when the url contains a search query.
- */
-export function SearchModal(props: SearchModalProps) {
- const [state, setSearchState] = useSearch();
- const searchAsk = useSearchAskState();
- const [askState] = searchAsk;
- const router = useRouter();
-
- useHotkeys(
- 'mod+k',
- (e) => {
- e.preventDefault();
- setSearchState({ ask: false, query: '', global: false });
- },
- []
- );
-
- // Add a global class on the body when the search modal is open
- const isSearchOpened = state !== null;
- React.useEffect(() => {
- if (isSearchOpened) {
- document.body.style.overflow = 'hidden';
- }
-
- return () => {
- document.body.style.overflow = 'auto';
- };
- }, [isSearchOpened]);
-
- const onClose = async (to?: string) => {
- await setSearchState(null);
- if (to) {
- router.push(to);
- }
- };
-
- return (
-
-
- {state !== null ? (
- {
- onClose();
- }}
- >
-
-
- {askState?.type === 'loading' ? (
-
-
-
- ) : null}
-
-
-
-
- ) : null}
-
-
- );
-}
-
-function SearchModalBody(
- props: SearchModalProps & {
- state: SearchState;
- setSearchState: UpdateSearchState;
- onClose: (to?: string) => void;
- }
-) {
- const { spaceTitle, withAsk, withAIChat, isMultiVariants, state, setSearchState, onClose } =
- props;
-
- const language = useLanguage();
- const resultsRef = React.useRef(null);
- const inputRef = React.useRef(null);
-
- React.useEffect(() => {
- inputRef.current?.focus();
- }, []);
-
- React.useEffect(() => {
- const handleKeyDown = (event: KeyboardEvent) => {
- if (event.key === 'Escape') {
- onClose();
- }
- };
- document.addEventListener('keydown', handleKeyDown);
-
- return () => {
- document.removeEventListener('keydown', handleKeyDown);
- };
- }, [onClose]);
-
- const onKeyDown = (event: React.KeyboardEvent) => {
- if (event.key === 'ArrowUp') {
- event.preventDefault();
- resultsRef.current?.moveUp();
- } else if (event.key === 'ArrowDown') {
- event.preventDefault();
- resultsRef.current?.moveDown();
- } else if (event.key === 'Enter') {
- event.preventDefault();
- resultsRef.current?.select();
- }
- };
-
- const onChange = (event: React.ChangeEvent) => {
- setSearchState({
- ask: false, // When typing, we go back to the default search mode
- query: event.target.value,
- global: state.global,
- });
- };
-
- const onSwitchToAsk = () => {
- setSearchState((state) => (state ? { ...state, ask: true } : null));
- };
-
- // We trim the query to avoid invalidating the search when the user is typing between words.
- const normalizedQuery = state.query.trim();
-
- return (
- {
- event.stopPropagation();
- }}
- >
-
-
-
-
-
-
- {isMultiVariants ? : null}
-
-
- {!state.ask || !withAsk ? (
-
- ) : null}
- {normalizedQuery && state.ask && withAsk ? (
-
- ) : null}
-
- );
-}
diff --git a/packages/gitbook/src/components/Search/index.ts b/packages/gitbook/src/components/Search/index.ts
index a316dad375..c068bf432f 100644
--- a/packages/gitbook/src/components/Search/index.ts
+++ b/packages/gitbook/src/components/Search/index.ts
@@ -1,2 +1 @@
-export * from './SearchButton';
-export * from './SearchModal';
+export * from './SearchInput';
diff --git a/packages/gitbook/src/components/Search/useSearch.ts b/packages/gitbook/src/components/Search/useSearch.ts
index 8f3ecfaec5..0e7a4e8981 100644
--- a/packages/gitbook/src/components/Search/useSearch.ts
+++ b/packages/gitbook/src/components/Search/useSearch.ts
@@ -89,6 +89,7 @@ export function useSearchLink(): (query: Partial) => LinkProps {
query: '',
ask: false,
global: false,
+ open: false,
...(prev ?? {}),
...query,
}));
diff --git a/packages/gitbook/src/components/primitives/Button.tsx b/packages/gitbook/src/components/primitives/Button.tsx
index e6ac7baa12..705824399d 100644
--- a/packages/gitbook/src/components/primitives/Button.tsx
+++ b/packages/gitbook/src/components/primitives/Button.tsx
@@ -17,6 +17,7 @@ type ButtonProps = {
size?: 'default' | 'medium' | 'small';
className?: ClassValue;
label?: string;
+ children?: React.ReactNode;
} & LinkInsightsProps &
HTMLAttributes;
@@ -33,11 +34,10 @@ export const variantClasses = {
'bg-transparent',
'text-tint',
'ring-0',
- 'shadow-none',
+ '!shadow-none',
'hover:bg-primary-hover',
'hover:text-primary',
'hover:scale-1',
- 'hover:shadow-none',
'contrast-more:bg-tint-subtle',
'depth-subtle:hover:translate-y-0',
],
@@ -62,6 +62,7 @@ export function Button({
label,
icon,
iconOnly = false,
+ children,
...rest
}: ButtonProps & ButtonHTMLAttributes & { target?: HTMLAttributeAnchorTarget }) {
const sizes = {
@@ -112,7 +113,7 @@ export function Button({
icon
)
) : null}
- {iconOnly ? null : label}
+ {iconOnly ? null : (children ?? label)}
);
diff --git a/packages/gitbook/src/components/primitives/styles.ts b/packages/gitbook/src/components/primitives/styles.ts
index 5b59d5f9be..1e2f295b0c 100644
--- a/packages/gitbook/src/components/primitives/styles.ts
+++ b/packages/gitbook/src/components/primitives/styles.ts
@@ -13,19 +13,23 @@ export const ButtonStyles = [
'ring-1',
'ring-tint',
'hover:ring-tint-hover',
+ 'focus-visible:ring-2',
+ 'focus-visible:ring-primary-hover',
+ 'outline-none',
- 'shadow-sm',
+ 'depth-subtle:shadow-sm',
+ 'depth-subtle:hover:shadow-md',
+ 'depth-subtle:focus-visible:shadow-md',
+ 'active:shadow-none',
'shadow-tint',
'dark:shadow-tint-1',
- 'hover:shadow-md',
- 'active:shadow-none',
- 'depth-flat:shadow-none',
'contrast-more:ring-tint-12',
'contrast-more:hover:ring-2',
'contrast-more:hover:ring-tint-12',
'depth-subtle:hover:-translate-y-px',
+ 'depth-subtle:focus-visible:-translate-y-px',
// 'depth-flat:hover:scale-100',
// 'active:scale-100',
'transition-all',
From d7ab5d99d53e183838611ec7ec9424992cf8340f Mon Sep 17 00:00:00 2001
From: Zeno Kapitein
Date: Tue, 1 Jul 2025 12:56:11 +0200
Subject: [PATCH 03/35] Style results
---
.../src/components/AIChat/AIChatButton.tsx | 5 +-
.../gitbook/src/components/Header/Header.tsx | 4 +-
.../src/components/Search/HighlightQuery.tsx | 3 +
.../src/components/Search/SearchContainer.tsx | 2 +-
.../src/components/Search/SearchInput.tsx | 6 +-
.../Search/SearchPageResultItem.tsx | 220 +++++++++++-------
.../Search/SearchQuestionResultItem.tsx | 100 +++-----
.../src/components/Search/SearchResults.tsx | 71 +++++-
.../Search/SearchSectionResultItem.tsx | 130 ++++++-----
.../src/components/primitives/Popover.tsx | 2 +-
packages/gitbook/src/intl/translations/en.ts | 1 +
11 files changed, 336 insertions(+), 208 deletions(-)
diff --git a/packages/gitbook/src/components/AIChat/AIChatButton.tsx b/packages/gitbook/src/components/AIChat/AIChatButton.tsx
index b4194d1a86..d32add3de3 100644
--- a/packages/gitbook/src/components/AIChat/AIChatButton.tsx
+++ b/packages/gitbook/src/components/AIChat/AIChatButton.tsx
@@ -1,5 +1,6 @@
'use client';
import { tString, useLanguage } from '@/intl/client';
+import { tcls } from '@/lib/tailwind';
import { useAIChatController, useAIChatState } from '../AI/useAIChat';
import { Button } from '../primitives';
import AIChatIcon from './AIChatIcon';
@@ -7,7 +8,7 @@ import AIChatIcon from './AIChatIcon';
/**
* Button to open/close the AI chat.
*/
-export function AIChatButton() {
+export function AIChatButton(props: { className?: string }) {
const chatController = useAIChatController();
const chat = useAIChatState();
@@ -19,7 +20,7 @@ export function AIChatButton() {
iconOnly
size="default"
variant="secondary"
- className="!px-3 bg-tint-base py-2.5"
+ className={tcls('!px-3 bg-tint-base py-2.5', props.className)}
label={tString(language, 'ai_chat_assistant_name')}
onClick={() => {
if (chat.opened) {
diff --git a/packages/gitbook/src/components/Header/Header.tsx b/packages/gitbook/src/components/Header/Header.tsx
index addf5e6550..19fe10a9a0 100644
--- a/packages/gitbook/src/components/Header/Header.tsx
+++ b/packages/gitbook/src/components/Header/Header.tsx
@@ -156,7 +156,9 @@ export function Header(props: {
// ]}
/>
- {withAIChat && }
+ {withAIChat && (
+
+ )}
{customization.header.links.length > 0 && (
diff --git a/packages/gitbook/src/components/Search/HighlightQuery.tsx b/packages/gitbook/src/components/Search/HighlightQuery.tsx
index f0b32d74b0..2a830a547c 100644
--- a/packages/gitbook/src/components/Search/HighlightQuery.tsx
+++ b/packages/gitbook/src/components/Search/HighlightQuery.tsx
@@ -23,6 +23,9 @@ export function HighlightQuery(props: {
'py-0.5',
'rounded',
'straight-corners:rounded-sm',
+ 'transition-colors',
+ 'group-hover:bg-primary-active',
+ 'group-hover:text-contrast-primary-active',
'group-[.is-active]:bg-primary-active',
'group-[.is-active]:text-contrast-primary-active',
],
diff --git a/packages/gitbook/src/components/Search/SearchContainer.tsx b/packages/gitbook/src/components/Search/SearchContainer.tsx
index bd57bc2e3b..76de637e2d 100644
--- a/packages/gitbook/src/components/Search/SearchContainer.tsx
+++ b/packages/gitbook/src/components/Search/SearchContainer.tsx
@@ -138,7 +138,7 @@ export function SearchContainer(props: SearchContainerProps) {
onOpenAutoFocus: (event) => event.preventDefault(),
align: 'start',
className:
- 'bg-tint-base w-[32rem] p-3 h-[32rem] max-w-[min(var(--radix-popover-content-available-width),32rem)]',
+ 'bg-tint-base scroll-py-6 w-[32rem] p-2 h-[32rem] max-w-[min(var(--radix-popover-content-available-width),32rem)]',
onInteractOutside: () => onClose(),
sideOffset: 8,
}}
diff --git a/packages/gitbook/src/components/Search/SearchInput.tsx b/packages/gitbook/src/components/Search/SearchInput.tsx
index b9d0545db6..94f7ed66ee 100644
--- a/packages/gitbook/src/components/Search/SearchInput.tsx
+++ b/packages/gitbook/src/components/Search/SearchInput.tsx
@@ -43,7 +43,7 @@ export function SearchInput(props: SearchButtonProps) {
return (
diff --git a/packages/gitbook/src/components/Search/SearchPageResultItem.tsx b/packages/gitbook/src/components/Search/SearchPageResultItem.tsx
index 5c589cd7a9..b61bb85ada 100644
--- a/packages/gitbook/src/components/Search/SearchPageResultItem.tsx
+++ b/packages/gitbook/src/components/Search/SearchPageResultItem.tsx
@@ -1,9 +1,9 @@
+import { tString, useLanguage } from '@/intl/client';
import { tcls } from '@/lib/tailwind';
import { Icon, type IconName } from '@gitbook/icons';
import React from 'react';
-
-import { Link } from '../primitives';
import { HighlightQuery } from './HighlightQuery';
+import { SearchResultItem } from './SearchResults';
import type { ComputedPageResult } from './server-actions';
export const SearchPageResultItem = React.forwardRef(function SearchPageResultItem(
@@ -15,6 +15,7 @@ export const SearchPageResultItem = React.forwardRef(function SearchPageResultIt
ref: React.Ref
) {
const { query, item, active } = props;
+ const language = useLanguage();
const breadcrumbs =
item.breadcrumbs?.map((crumb) => (
@@ -25,26 +26,12 @@ export const SearchPageResultItem = React.forwardRef(function SearchPageResultIt
)) ?? [];
return (
- }
insights={{
type: 'search_open_result',
query,
@@ -54,67 +41,138 @@ export const SearchPageResultItem = React.forwardRef(function SearchPageResultIt
},
}}
>
-
-
-
-
- {breadcrumbs.length > 0 ? (
-
- {(breadcrumbs.length > 3
- ? [
- ...breadcrumbs.slice(0, 2),
-
,
- ...breadcrumbs.slice(-1),
- ]
- : breadcrumbs
- ).map((crumb, index) => (
-
- {index !== 0 ? (
-
- ) : null}
- {crumb}
-
- ))}
-
- ) : null}
+ {breadcrumbs.length > 0 ? (
+
+ {(breadcrumbs.length > 3
+ ? [
+ ...breadcrumbs.slice(0, 2),
+ ,
+ ...breadcrumbs.slice(-1),
+ ]
+ : breadcrumbs
+ ).map((crumb, index) => (
+
+ {index !== 0 ? (
+
+ ) : null}
+ {crumb}
+
+ ))}
+
+ ) : null}
+
-
-
-
-
-
+
+
+ //
+ //
+ //
+ //
+ //
+ // {breadcrumbs.length > 0 ? (
+ //
+ // {(breadcrumbs.length > 3
+ // ? [
+ // ...breadcrumbs.slice(0, 2),
+ //
,
+ // ...breadcrumbs.slice(-1),
+ // ]
+ // : breadcrumbs
+ // ).map((crumb, index) => (
+ //
+ // {index !== 0 ? (
+ //
+ // ) : null}
+ // {crumb}
+ //
+ // ))}
+ //
+ // ) : null}
+ //
+ //
+ //
+ //
+ //
+ //
);
});
diff --git a/packages/gitbook/src/components/Search/SearchQuestionResultItem.tsx b/packages/gitbook/src/components/Search/SearchQuestionResultItem.tsx
index 4554da5ab6..01c5244e09 100644
--- a/packages/gitbook/src/components/Search/SearchQuestionResultItem.tsx
+++ b/packages/gitbook/src/components/Search/SearchQuestionResultItem.tsx
@@ -1,11 +1,10 @@
-import { Icon } from '@gitbook/icons';
import React from 'react';
-import { t, useLanguage } from '@/intl/client';
-import { tcls } from '@/lib/tailwind';
-
+import { t, tString, useLanguage } from '@/intl/client';
+import { Icon } from '@gitbook/icons';
import { useAIChatController } from '../AI';
-import { Link } from '../primitives';
+import AIChatIcon from '../AIChat/AIChatIcon';
+import { SearchResultItem } from './SearchResults';
import { useSearch, useSearchLink } from './useSearch';
export const SearchQuestionResultItem = React.forwardRef(function SearchQuestionResultItem(
@@ -25,8 +24,16 @@ export const SearchQuestionResultItem = React.forwardRef(function SearchQuestion
const [, setSearchState] = useSearch();
return (
- {
if (withAIChat) {
// If AI Chat is enabled, hijack to open the chat and post the question
@@ -37,65 +44,28 @@ export const SearchQuestionResultItem = React.forwardRef(function SearchQuestion
onClick();
}
}}
- data-testid="search-result-item"
- className={tcls(
- 'flex',
- 'px-4',
- recommended ? ['py-2', 'text-tint'] : 'py-4',
- 'hover:bg-tint-hover',
- 'first:mt-0',
- 'last:pb-3',
- active && [
- 'is-active',
- 'bg-primary',
- 'text-contrast-primary',
- 'hover:bg-primary-hover',
- ]
- )}
- {...(withAIChat
- ? { href: '#' }
- : getLinkProp({
- ask: true,
- query: question,
- }))}
- >
-
-
- {recommended ? (
- question
+ active={active}
+ leadingIcon={
+ recommended ? (
+
) : (
- <>
-
{t(language, 'search_ask', [question])}
-
- {t(language, 'search_ask_description')}
-
- >
- )}
-
-
-
-
-
+
+ )
+ }
+ className={recommended && active ? 'pr-1.5' : ''}
+ >
+ {recommended ? (
+ question
+ ) : (
+ <>
+
+ {t(language, 'search_ask', [question])}
+
+
+ {t(language, 'search_ask_description')}
+
+ >
+ )}
+
);
});
diff --git a/packages/gitbook/src/components/Search/SearchResults.tsx b/packages/gitbook/src/components/Search/SearchResults.tsx
index 0ea65d0081..da32dc2e9d 100644
--- a/packages/gitbook/src/components/Search/SearchResults.tsx
+++ b/packages/gitbook/src/components/Search/SearchResults.tsx
@@ -7,8 +7,9 @@ import React from 'react';
import { t, useLanguage } from '@/intl/client';
import { tcls } from '@/lib/tailwind';
+import { Icon } from '@gitbook/icons';
import { useTrackEvent } from '../Insights';
-import { Loading } from '../primitives';
+import { Button, Link, Loading } from '../primitives';
import { SearchPageResultItem } from './SearchPageResultItem';
import { SearchQuestionResultItem } from './SearchQuestionResultItem';
import { SearchSectionResultItem } from './SearchSectionResultItem';
@@ -238,7 +239,7 @@ export const SearchResults = React.forwardRef(function SearchResults(
) : null
) : (
<>
-
+
{results.map((item, index) => {
switch (item.type) {
case 'page': {
@@ -320,3 +321,69 @@ function withQuestionResult(results: ResultType[], query: string): ResultType[]
return [{ type: 'question', id: 'question', query }, ...(without ?? [])];
}
+
+export const SearchResultItem = React.forwardRef(function SearchResultItem(
+ props: {
+ children: React.ReactNode;
+ href: string;
+ action: string;
+ active: boolean;
+ className?: string;
+ size?: 'small' | 'medium';
+ leadingIcon?: React.ReactNode;
+ } & React.ComponentProps
,
+ ref: React.Ref
+) {
+ const {
+ children,
+ href,
+ active,
+ className,
+ leadingIcon,
+ size = 'medium',
+ action,
+ ...rest
+ } = props;
+
+ return (
+
+ {leadingIcon}
+ {children}
+ {active ? (
+
+ ) : (
+
+
+
+ )}
+
+ );
+});
diff --git a/packages/gitbook/src/components/Search/SearchSectionResultItem.tsx b/packages/gitbook/src/components/Search/SearchSectionResultItem.tsx
index 8ccda684d2..b12162dbdb 100644
--- a/packages/gitbook/src/components/Search/SearchSectionResultItem.tsx
+++ b/packages/gitbook/src/components/Search/SearchSectionResultItem.tsx
@@ -1,10 +1,9 @@
-import { Icon } from '@gitbook/icons';
import React from 'react';
+import { tString, useLanguage } from '@/intl/client';
import { tcls } from '@/lib/tailwind';
-
-import { Link } from '../primitives';
import { HighlightQuery } from './HighlightQuery';
+import { SearchResultItem } from './SearchResults';
import type { ComputedSectionResult } from './server-actions';
export const SearchSectionResultItem = React.forwardRef(function SearchSectionResultItem(
@@ -16,30 +15,15 @@ export const SearchSectionResultItem = React.forwardRef(function SearchSectionRe
ref: React.Ref
) {
const { query, item, active } = props;
+ const language = useLanguage();
return (
-
-
+
{item.title ? (
-
+
-
+
) : null}
{item.body ? highlightQueryInBody(item.body, query) : null}
-
-
-
-
+
+ //
+ //
+ // {item.title ? (
+ //
+ //
+ //
+ // ) : null}
+ // {item.body ? highlightQueryInBody(item.body, query) : null}
+ //
+ //
+ //
+ //
+ //
);
});
diff --git a/packages/gitbook/src/components/primitives/Popover.tsx b/packages/gitbook/src/components/primitives/Popover.tsx
index 882266ea4a..42ba4fa217 100644
--- a/packages/gitbook/src/components/primitives/Popover.tsx
+++ b/packages/gitbook/src/components/primitives/Popover.tsx
@@ -20,7 +20,7 @@ export function Popover(props: {
collisionPadding={contentProps?.collisionPadding ?? 16}
sideOffset={contentProps?.sideOffset ?? 4}
className={tcls(
- 'z-50 max-h-[var(--radix-popover-content-available-height)] max-w-xs animate-scaleIn overflow-y-auto circular-corners:rounded-2xl rounded-corners:rounded-md bg-tint px-4 py-3 text-sm text-tint shadow-md outline-none ring-1 ring-tint',
+ 'z-50 max-h-[var(--radix-popover-content-available-height)] max-w-xs animate-scaleIn overflow-y-auto circular-corners:rounded-3xl rounded-corners:rounded-md bg-tint px-4 py-3 text-sm text-tint shadow-md outline-none ring-1 ring-tint',
contentProps?.className
)}
style={{
diff --git a/packages/gitbook/src/intl/translations/en.ts b/packages/gitbook/src/intl/translations/en.ts
index b8536ee1e2..f80ffa8158 100644
--- a/packages/gitbook/src/intl/translations/en.ts
+++ b/packages/gitbook/src/intl/translations/en.ts
@@ -14,6 +14,7 @@ export const en = {
search_results_count: '${1} results',
search_scope_space: '${1}',
search_scope_all: 'All content',
+ ask: 'Ask',
search_ask: 'Ask "${1}"',
search_ask_description: 'Find the answer with AI',
search_ask_sources: 'Sources',
From 27dc51eaf252aeb9ff8d5bbd54b1fc2987452f3e Mon Sep 17 00:00:00 2001
From: Zeno Kapitein
Date: Tue, 1 Jul 2025 13:33:20 +0200
Subject: [PATCH 04/35] Tweak styling of non-ask empty state
---
.../src/components/Search/SearchContainer.tsx | 22 ++++++++++---------
.../src/components/Search/SearchInput.tsx | 2 +-
.../src/components/Search/SearchResults.tsx | 15 +++++++------
.../src/components/primitives/Popover.tsx | 2 +-
4 files changed, 22 insertions(+), 19 deletions(-)
diff --git a/packages/gitbook/src/components/Search/SearchContainer.tsx b/packages/gitbook/src/components/Search/SearchContainer.tsx
index 76de637e2d..289c8cdad1 100644
--- a/packages/gitbook/src/components/Search/SearchContainer.tsx
+++ b/packages/gitbook/src/components/Search/SearchContainer.tsx
@@ -61,16 +61,18 @@ export function SearchContainer(props: SearchContainerProps) {
);
const onOpen = () => {
- setSearchState({
- ask: false,
- global: false,
- query: state?.query ?? '',
- });
- setOpen(true);
+ if (withAsk || state?.query !== '') {
+ setSearchState({
+ ask: false,
+ global: false,
+ query: state?.query ?? '',
+ });
+ setOpen(true);
- trackEvent({
- type: 'search_open',
- });
+ trackEvent({
+ type: 'search_open',
+ });
+ }
};
React.useEffect(() => {
@@ -138,7 +140,7 @@ export function SearchContainer(props: SearchContainerProps) {
onOpenAutoFocus: (event) => event.preventDefault(),
align: 'start',
className:
- 'bg-tint-base scroll-py-6 w-[32rem] p-2 h-[32rem] max-w-[min(var(--radix-popover-content-available-width),32rem)]',
+ 'bg-tint-base has-[.empty]:hidden scroll-py-6 w-[32rem] p-2 h-[32rem] max-w-[min(var(--radix-popover-content-available-width),32rem)]',
onInteractOutside: () => onClose(),
sideOffset: 8,
}}
diff --git a/packages/gitbook/src/components/Search/SearchInput.tsx b/packages/gitbook/src/components/Search/SearchInput.tsx
index 94f7ed66ee..e81bb64e26 100644
--- a/packages/gitbook/src/components/Search/SearchInput.tsx
+++ b/packages/gitbook/src/components/Search/SearchInput.tsx
@@ -43,7 +43,7 @@ export function SearchInput(props: SearchButtonProps) {
return (