@redocly/theme 0.49.1 → 0.50.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,6 +7,7 @@ export type ImageProps = {
7
7
  width?: string | number;
8
8
  height?: string | number;
9
9
  border?: string;
10
+ withLightbox?: boolean;
10
11
  style?: React.CSSProperties | string;
11
12
  };
12
13
  export declare function Image(props: ImageProps): JSX.Element;
@@ -8,16 +8,51 @@ const react_1 = __importDefault(require("react"));
8
8
  const styled_components_1 = __importDefault(require("styled-components"));
9
9
  const utils_1 = require("../../core/utils");
10
10
  function Image(props) {
11
- const { src, srcSet, alt, className, width, height, border, style } = props;
11
+ const { src, srcSet, alt, className, width, height, border, style, withLightbox } = props;
12
+ const [lightboxImage, setLightboxImage] = react_1.default.useState(undefined);
12
13
  const parsedSourceSetMap = react_1.default.useMemo(() => {
13
14
  return srcSet ? (0, utils_1.parseSrcSet)(srcSet) : new Map();
14
15
  }, [srcSet]);
15
- const combinedStyle = Object.assign(Object.assign({}, (border && { border })), (typeof style === 'string' ? { cssText: style } : style));
16
- return (react_1.default.createElement(react_1.default.Fragment, null, src ? (react_1.default.createElement("img", { src: src, alt: alt, className: className, width: width, height: height, style: combinedStyle })) : (Array.from(parsedSourceSetMap).map(([key, value]) => (react_1.default.createElement(ColorModeAwareImage, { key: key, colorMode: key, src: value, alt: alt, className: className, width: width, height: height, style: combinedStyle }))))));
16
+ const handleImageClick = (src) => {
17
+ if (!withLightbox || lightboxImage) {
18
+ return;
19
+ }
20
+ setLightboxImage(src);
21
+ };
22
+ const handleCloseLightbox = () => {
23
+ setLightboxImage(undefined);
24
+ };
25
+ const combinedStyles = Object.assign(Object.assign(Object.assign({}, (withLightbox && { cursor: 'pointer' })), (border && { border })), (typeof style === 'string' ? { cssText: style } : style));
26
+ return (react_1.default.createElement(react_1.default.Fragment, null,
27
+ lightboxImage ? (react_1.default.createElement(LightboxContainer, { onClick: handleCloseLightbox },
28
+ react_1.default.createElement(Image, { src: lightboxImage, alt: alt }))) : null,
29
+ src ? (react_1.default.createElement("img", { src: src, alt: alt, className: className, width: width, height: height, style: combinedStyles, onClick: () => handleImageClick(src) })) : (Array.from(parsedSourceSetMap).map(([key, value]) => (react_1.default.createElement(ColorModeAwareImage, { key: key, colorMode: key, src: value, alt: alt, className: className, width: width, height: height, "$withLightbox": withLightbox, style: combinedStyles, onClick: () => handleImageClick(value) }))))));
17
30
  }
18
31
  const ColorModeAwareImage = styled_components_1.default.img `
19
32
  html:not(.${(props) => props.colorMode}) && {
20
33
  display: none;
21
34
  }
35
+ ${({ $withLightbox }) => $withLightbox &&
36
+ `
37
+ cursor: pointer;
38
+ `}
39
+ `;
40
+ const LightboxContainer = styled_components_1.default.div `
41
+ position: fixed;
42
+ top: 0;
43
+ left: 0;
44
+ width: 100%;
45
+ height: 100%;
46
+ background-color: var(--bg-color-modal-overlay);
47
+ z-index: var(--z-index-overlay);
48
+ display: flex;
49
+ justify-content: center;
50
+ align-items: center;
51
+
52
+ img {
53
+ cursor: pointer;
54
+ max-width: 90%;
55
+ max-height: 90%;
56
+ }
22
57
  `;
23
58
  //# sourceMappingURL=Image.js.map
@@ -2,6 +2,7 @@ export type SearchAiResponseProps = {
2
2
  question: string;
3
3
  isGeneratingResponse: boolean;
4
4
  response?: string;
5
+ error: string | null;
5
6
  resources: {
6
7
  url: string;
7
8
  title: string;
@@ -13,25 +13,46 @@ const Tag_1 = require("../../components/Tag/Tag");
13
13
  const Link_1 = require("../../components/Link/Link");
14
14
  const hooks_1 = require("../../core/hooks");
15
15
  const Markdown_1 = require("../../components/Markdown/Markdown");
16
+ const Admonition_1 = require("../../components/Admonition/Admonition");
17
+ const ErrorFilledIcon_1 = require("../../icons/ErrorFilledIcon/ErrorFilledIcon");
16
18
  function SearchAiResponse(props) {
17
19
  const { useMarkdownText } = (0, hooks_1.useThemeHooks)();
18
- const { question, response, isGeneratingResponse, resources } = props;
20
+ const { question, response, isGeneratingResponse, resources, error } = props;
19
21
  const { useTranslate } = (0, hooks_1.useThemeHooks)();
20
22
  const { translate } = useTranslate();
21
23
  const markdownResponse = useMarkdownText(response || '');
22
- return (react_1.default.createElement(ResponseWrapper, { "data-component-name": "Search/SearchAiResponse" },
23
- react_1.default.createElement(ResponseHeader, null,
24
- isGeneratingResponse ? (react_1.default.createElement(Spinner_1.Spinner, { size: "20px", color: "--search-ai-spinner-icon-color" })) : (react_1.default.createElement(CheckmarkFilledIcon_1.CheckmarkFilledIcon, { size: "20px", color: "--search-ai-checkmark-icon-color" })),
25
- react_1.default.createElement(Question, null, question)),
26
- react_1.default.createElement(ResponseBody, null,
27
- response ? (react_1.default.createElement(ResponseText, Object.assign({ children: markdownResponse, as: "div" }, props))) : (react_1.default.createElement(ThinkingResponseText, { "data-translation-key": "search.ai.thinkingText" }, translate('search.ai.thinkingText', 'Thinking...'))),
28
- resources.length && !isGeneratingResponse ? (react_1.default.createElement(Resources, null,
29
- react_1.default.createElement(ResourcesTitle, { "data-translation-key": "search.ai.resourcesFound" },
30
- resources.length,
31
- " ",
32
- translate('search.ai.resourcesFound', 'resources found')),
33
- react_1.default.createElement(ResourceTags, null, resources.map((resource, idx) => (react_1.default.createElement(Link_1.Link, { key: idx, to: resource.url, target: "_blank" },
34
- react_1.default.createElement(ResourceTag, { borderless: true, icon: react_1.default.createElement(DocumentIcon_1.DocumentIcon, { color: "--search-ai-resource-tag-icon-color" }) }, resource.title))))))) : null)));
24
+ let responseContainer = null;
25
+ const hasPendingOrReceivedResponse = response || isGeneratingResponse || error;
26
+ if (hasPendingOrReceivedResponse) {
27
+ let icon;
28
+ switch (true) {
29
+ case error !== null:
30
+ icon = react_1.default.createElement(ErrorFilledIcon_1.ErrorFilledIcon, { size: "20px", color: "--error-bubble-content-color" });
31
+ break;
32
+ case isGeneratingResponse:
33
+ icon = react_1.default.createElement(Spinner_1.Spinner, { size: "20px", color: "--search-ai-spinner-icon-color" });
34
+ break;
35
+ case Boolean(response):
36
+ icon = react_1.default.createElement(CheckmarkFilledIcon_1.CheckmarkFilledIcon, { size: "20px", color: "--search-ai-checkmark-icon-color" });
37
+ break;
38
+ }
39
+ responseContainer = (react_1.default.createElement(react_1.default.Fragment, null,
40
+ react_1.default.createElement(ResponseHeader, null,
41
+ icon,
42
+ react_1.default.createElement(Question, null, question)),
43
+ react_1.default.createElement(ResponseBody, null,
44
+ response && react_1.default.createElement(ResponseText, Object.assign({ children: markdownResponse, as: "div" }, props)),
45
+ !response && isGeneratingResponse && (react_1.default.createElement(ThinkingResponseText, { "data-translation-key": "search.ai.thinkingText" }, translate('search.ai.thinkingText', 'Thinking...'))),
46
+ error && (react_1.default.createElement(Admonition_1.Admonition, { type: "danger", name: translate('search.ai.error.header', 'Oops! Something went wrong.') }, translate('search.ai.error.description', 'We encountered an issue while processing your search. Please try again later or refine your query. If the problem persists, feel free to contact support.'))),
47
+ resources.length && !isGeneratingResponse ? (react_1.default.createElement(Resources, null,
48
+ react_1.default.createElement(ResourcesTitle, { "data-translation-key": "search.ai.resourcesFound" },
49
+ resources.length,
50
+ " ",
51
+ translate('search.ai.resourcesFound', 'resources found')),
52
+ react_1.default.createElement(ResourceTags, null, resources.map((resource, idx) => (react_1.default.createElement(Link_1.Link, { key: idx, to: resource.url, target: "_blank" },
53
+ react_1.default.createElement(ResourceTag, { borderless: true, icon: react_1.default.createElement(DocumentIcon_1.DocumentIcon, { color: "--search-ai-resource-tag-icon-color" }) }, resource.title))))))) : null)));
54
+ }
55
+ return (react_1.default.createElement(ResponseWrapper, { "data-component-name": "Search/SearchAiResponse" }, responseContainer));
35
56
  }
36
57
  const ResponseWrapper = styled_components_1.default.div `
37
58
  display: flex;
@@ -116,7 +116,10 @@ function SearchDialog({ onClose, className }) {
116
116
  react_1.default.createElement(CloseIcon_1.CloseIcon, { onClick: () => setProduct(undefined), color: "--icon-color-additional" })))),
117
117
  react_1.default.createElement(SearchInput_1.SearchInput, { value: query, onChange: setQuery, placeholder: mode === 'search'
118
118
  ? translate('search.label', 'Search docs...')
119
- : translate('search.ai.label', 'Ask AI assistant'), isLoading: isSearchLoading, showReturnButton: mode === 'ai-dialog', onReturn: () => setMode('search'), onSubmit: mode === 'ai-dialog'
119
+ : translate('search.ai.label', 'Ask AI assistant'), isLoading: isSearchLoading, showReturnButton: mode === 'ai-dialog', onReturn: () => {
120
+ setMode('search');
121
+ aiSearch.clearAiSearchState();
122
+ }, onSubmit: mode === 'ai-dialog'
120
123
  ? () => {
121
124
  setQuery('');
122
125
  aiSearch.askQuestion(query);
@@ -147,7 +150,7 @@ function SearchDialog({ onClose, className }) {
147
150
  react_1.default.createElement(SearchRecent_1.SearchRecent, { onSelect: setQuery }),
148
151
  react_1.default.createElement(SearchSuggestedPages_1.SearchSuggestedPages, null)))),
149
152
  advancedSearch && mode === 'search' && isFilterOpen && (react_1.default.createElement(SearchDialogBodyFilterView, null,
150
- react_1.default.createElement(SearchFilter_1.SearchFilter, { facets: facets, filter: filter, query: query, quickFilterFields: [groupField], onFilterChange: onFilterChange, onFilterReset: onFilterReset, onFacetReset: onFacetReset }))))) : (react_1.default.createElement(SearchAiResponse_1.SearchAiResponse, { question: aiSearch.question, isGeneratingResponse: aiSearch.isGeneratingResponse, response: aiSearch.response, resources: aiSearch.resources }))),
153
+ react_1.default.createElement(SearchFilter_1.SearchFilter, { facets: facets, filter: filter, query: query, quickFilterFields: [groupField], onFilterChange: onFilterChange, onFilterReset: onFilterReset, onFacetReset: onFacetReset }))))) : (react_1.default.createElement(SearchAiResponse_1.SearchAiResponse, { question: aiSearch.question, isGeneratingResponse: aiSearch.isGeneratingResponse, response: aiSearch.response, resources: aiSearch.resources, error: aiSearch.error }))),
151
154
  react_1.default.createElement(SearchDialogFooter, null, mode === 'ai-dialog' ? (react_1.default.createElement(AiDisclaimer, null, translate('search.ai.disclaimer', 'AI search might provide incomplete or incorrect results. Verify important information.'))) : (react_1.default.createElement(react_1.default.Fragment, null,
152
155
  react_1.default.createElement(SearchShortcuts, null,
153
156
  react_1.default.createElement(SearchShortcut_1.SearchShortcut, { "data-translation-key": "search.keys.navigate", combination: "Tab", text: translate('search.keys.navigate', 'to navigate') }),
@@ -1,6 +1,6 @@
1
1
  export declare const useColorSwitcher: () => {
2
2
  isSwitcherHidden: boolean | undefined;
3
3
  initActiveColorMode: () => void;
4
- switchColorMode: () => string;
4
+ switchColorMode: (mode?: string) => void;
5
5
  activeColorMode: string;
6
6
  };
@@ -15,17 +15,20 @@ const useColorSwitcher = () => {
15
15
  const activeMode = Array.from(document.documentElement.classList).find((c) => modes.includes(c));
16
16
  setActiveColorMode(activeMode || defaultColor);
17
17
  };
18
- const switchColorMode = () => {
18
+ const switchColorMode = (mode) => {
19
+ if (mode && !modes.includes(mode)) {
20
+ return;
21
+ }
19
22
  const activeIndex = modes.indexOf(activeColorMode);
20
- const mode = activeIndex < modes.length - 1 ? modes[activeIndex + 1] : modes[0];
21
- localStorage.setItem('colorSchema', mode);
22
- document.documentElement.className = `${mode} notransition`;
23
+ // If specific mode is provided, use it, otherwise cycle through modes
24
+ const newMode = mode || (activeIndex < modes.length - 1 ? modes[activeIndex + 1] : modes[0]);
25
+ localStorage.setItem('colorSchema', newMode);
26
+ document.documentElement.className = `${newMode} notransition`;
23
27
  window.requestAnimationFrame(() => {
24
28
  document.documentElement.classList.remove('notransition');
25
29
  });
26
- telemetry.send('color_mode_switched', { from: activeColorMode, to: mode });
27
- setActiveColorMode(mode);
28
- return mode;
30
+ telemetry.send('color_mode_switched', { from: activeColorMode, to: newMode });
31
+ setActiveColorMode(newMode);
29
32
  };
30
33
  return {
31
34
  isSwitcherHidden: colorMode === null || colorMode === void 0 ? void 0 : colorMode.hide,
@@ -0,0 +1,5 @@
1
+ export type { ApiFunctionsRequest as Request } from '@redocly/config';
2
+ export type { ApiFunctionsResponse as Response } from '@redocly/config';
3
+ export type { ApiFunctionsContext as Context } from '@redocly/config';
4
+ export type { PropsContext } from '@redocly/config';
5
+ export type { PageStaticData as PropsData } from '@redocly/config';
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=api-functions.js.map
@@ -73,6 +73,8 @@ export type ThemeHooks = {
73
73
  title: string;
74
74
  url: string;
75
75
  }[];
76
+ error: null | string;
77
+ clearAiSearchState: () => void;
76
78
  };
77
79
  useMarkdownText: (text: string) => React.ReactNode;
78
80
  useFacetQuery: (field: string) => {
@@ -10,3 +10,4 @@ export * from '../../core/types/select';
10
10
  export * from '../../core/types/sidebar';
11
11
  export * from '../../core/types/filter';
12
12
  export * from '../../core/types/user-menu';
13
+ export * from '../../core/types/api-functions';
@@ -26,4 +26,5 @@ __exportStar(require("../../core/types/select"), exports);
26
26
  __exportStar(require("../../core/types/sidebar"), exports);
27
27
  __exportStar(require("../../core/types/filter"), exports);
28
28
  __exportStar(require("../../core/types/user-menu"), exports);
29
+ __exportStar(require("../../core/types/api-functions"), exports);
29
30
  //# sourceMappingURL=index.js.map
@@ -1,5 +1,5 @@
1
1
  import type { TOptions } from 'i18next';
2
- export type TranslationKey = 'dev.newApp' | 'dev.newApp.text' | 'dev.sidebar.header' | 'dev.sidebar.footer.text' | 'dev.create.app.dialog.appName.placeholder' | 'dev.create.app.dialog.appName.error' | 'dev.create.app.dialog.selectAPIs' | 'dev.create.app.dialog.description' | 'dev.create.app.dialog.description.placeholder' | 'dev.create.app.dialog.create' | 'dev.create.app.dialog.cancel' | 'dev.main.tab.appKeys' | 'dev.main.tab.logs' | 'dev.app.description.title' | 'dev.edit.description.dialog.title' | 'dev.edit.description.dialog.save' | 'dev.edit.description.dialog.cancel' | 'dev.edit.apis.dialog.selectedAPIs' | 'dev.app.key.create' | 'dev.create.key.dialog.title' | 'dev.create.key.dialog.create' | 'dev.create.key.dialog.cancel' | 'dev.app.edit' | 'dev.app.delete' | 'dev.edit.app.dialog.title' | 'dev.edit.app.dialog.save' | 'dev.edit.app.dialog.cancel' | 'dev.delete.app.dialog.title' | 'dev.delete.app.dialog.confirmation' | 'dev.delete.app.dialog.delete' | 'dev.delete.app.dialog.cancel' | 'dev.app.key.roll' | 'dev.roll.key.dialog.title' | 'dev.roll.key.dialog.apiKey' | 'dev.roll.key.dialog.expires' | 'dev.roll.key.dialog.confirmation' | 'dev.roll.key.dialog.cancel' | 'dev.roll.key.dialog.roll' | 'dev.update.key.dialog.title' | 'dev.update.key.dialog.update' | 'dev.update.key.dialog.cancel' | 'dev.app.key.api.name' | 'dev.app.key.api.status' | 'dev.app.key.api.edit' | 'dev.edit.apis.dialog.title' | 'dev.edit.apis.dialog.apiKey' | 'dev.edit.apis.dialog.save' | 'dev.edit.apis.dialog.cancel' | 'dev.select.placeholder' | 'dev.app.overview.status.pending' | 'dev.app.overview.status.approved' | 'dev.app.overview.status.revoked' | 'dev.app.overview.status' | 'dev.app.overview.non-production' | 'dev.app.overview.production' | 'dev.app.overview.clientId' | 'dev.app.overview.apiKey' | 'dev.app.key.revoke' | 'dev.revoke.key.dialog.title' | 'dev.revoke.key.dialog.apiKey' | 'dev.revoke.key.dialog.expires' | 'dev.revoke.key.dialog.confirmation' | 'dev.revoke.key.dialog.revoke' | 'dev.revoke.key.dialog.cancel' | 'dev.app.overview.expires' | 'dev.app.overview.created' | 'dev.app.overview.visibilityToggle.hide' | 'dev.app.overview.visibilityToggle.show' | 'search.loading' | 'search.noResults.title' | 'search.keys.navigate' | 'search.keys.select' | 'search.keys.exit' | 'search.label' | 'search.cancel' | 'search.recent' | 'search.navbar.label' | 'search.suggested' | 'search.showMore' | 'search.filter.title' | 'search.filter.reset' | 'search.filter.field.reset' | 'search.ai.thinkingText' | 'search.ai.resourcesFound' | 'search.ai.button' | 'search.ai.label' | 'search.ai.disclaimer' | 'toc.header' | 'footer.copyrightText' | 'page.homeButton' | 'page.forbidden.title' | 'page.notFound.title' | 'page.notFound.description' | 'page.lastUpdated.timeago' | 'page.lastUpdated.on' | 'catalog.filters.placeholder' | 'catalog.filters.title' | 'catalog.filters.clearAll' | 'catalog.filters.select.addFilter' | 'catalog.filters.select.all' | 'catalog.filters.done' | 'sidebar.menu.backLabel' | 'sidebar.menu.backToLabel' | 'sidebar.actions.show' | 'sidebar.actions.hide' | 'sidebar.actions.changeLayout' | 'versionPicker.label' | 'versionPicker.unversioned' | 'codeSnippet.copy.buttonText' | 'codeSnippet.copy.tooltipText' | 'codeSnippet.copy.toasterText' | 'markdown.editPage.text' | 'feedback.settings.comment.submitText' | 'feedback.settings.comment.label' | 'feedback.settings.comment.send' | 'feedback.settings.comment.cancel' | 'feedback.settings.comment.satisfiedLabel' | 'feedback.settings.comment.neutralLabel' | 'feedback.settings.comment.dissatisfiedLabel' | 'feedback.settings.submitText' | 'feedback.settings.label' | 'feedback.settings.reasons.label' | 'feedback.settings.reasons.send' | 'feedback.settings.comment.likeLabel' | 'feedback.settings.comment.dislikeLabel' | 'feedback.sentiment.thumbUp' | 'feedback.sentiment.thumbDown' | 'feedback.settings.leftScaleLabel' | 'feedback.settings.rightScaleLabel' | 'feedback.settings.optionalEmail.placeholder' | 'feedback.settings.optionalEmail.label' | 'codeSnippet.report.buttonText' | 'codeSnippet.report.tooltipText' | 'codeSnippet.report.label' | 'userMenu.login' | 'userMenu.logout' | 'userMenu.devOnboardingLabel' | 'mobileMenu.mainMenu' | 'mobileMenu.previous' | 'mobileMenu.products' | 'page.nextButton' | 'page.previousButton' | 'openapi.download.description.title' | 'openapi.info.title' | 'openapi.info.contact.url' | 'openapi.info.contact.name' | 'openapi.info.license' | 'openapi.info.termsOfService' | 'openapi.info.metadata.title' | 'openapi.key' | 'openapi.value' | 'openapi.enum' | 'openapi.items' | 'openapi.default' | 'openapi.variable' | 'openapi.variables' | 'openapi.actions.show' | 'openapi.actions.hide' | 'openapi.actions.more' | 'openapi.languages.title' | 'openapi.servers.title' | 'openapi.operations' | 'openapi.webhooks' | 'openapi.description' | 'openapi.badges.deprecated' | 'openapi.badges.required' | 'openapi.badges.webhook' | 'openapi.request' | 'openapi.path' | 'openapi.query' | 'openapi.cookie' | 'openapi.header' | 'openapi.body' | 'openapi.responses' | 'openapi.response' | 'openapi.callbacks' | 'openapi.callbackRequest' | 'openapi.callbackResponse' | 'openapi.payload' | 'openapi.discriminator' | 'openapi.contentType' | 'openapi.tryIt' | 'openapi.loading' | 'openapi.example' | 'openapi.examples' | 'openapi.additionalProperties' | 'openapi.patternProperties' | 'openapi.required' | 'openapi.recursive' | 'openapi.complex' | 'openapi.deprecated' | 'openapi.hideExample' | 'openapi.showExample' | 'openapi.expandAll' | 'openapi.collapseAll' | 'openapi.noResponseExample' | 'openapi.noResponseContent' | 'openapi.noRequestPayload' | 'openapi.hidePattern' | 'openapi.showPattern' | 'openapi.authorizationUrl' | 'openapi.tokenUrl' | 'openapi.refreshUrl' | 'openapi.scopes' | 'openapi.security' | 'openapi.httpAuthorizationScheme' | 'openapi.bearerFormat' | 'openapi.parameterName' | 'openapi.flowType' | 'openapi.connectUrl' | 'openapi.requiredScopes' | 'openapi.unsupportedLanguage' | 'openapi.failedToGenerateCodeSample' | 'graphql.queries' | 'graphql.mutations' | 'graphql.subscriptions' | 'graphql.directives' | 'graphql.objects' | 'graphql.interfaces' | 'graphql.unions' | 'graphql.enums' | 'graphql.inputs' | 'graphql.scalars' | 'graphql.arguments.label' | 'graphql.arguments.show' | 'graphql.arguments.hide' | 'graphql.arguments.here' | 'graphql.returnTypes.label' | 'graphql.returnTypes.show' | 'graphql.returnTypes.hide' | 'graphql.possibleTypes' | 'graphql.defaultValue' | 'graphql.deprecationReason' | 'graphql.implementedInterfaces' | 'graphql.nonNull' | 'graphql.required' | 'graphql.deprecated' | 'graphql.variables' | 'graphql.querySample' | 'graphql.mutationSample' | 'graphql.subscriptionSample' | 'graphql.responseSample' | 'graphql.locations' | 'graphql.sample' | 'graphql.referenced' | 'codeWalkthrough.download' | 'codeWalkthrough.preview';
2
+ export type TranslationKey = 'dev.newApp' | 'dev.newApp.text' | 'dev.sidebar.header' | 'dev.sidebar.footer.text' | 'dev.create.app.dialog.appName.placeholder' | 'dev.create.app.dialog.appName.error' | 'dev.create.app.dialog.selectAPIs' | 'dev.create.app.dialog.description' | 'dev.create.app.dialog.description.placeholder' | 'dev.create.app.dialog.create' | 'dev.create.app.dialog.cancel' | 'dev.main.tab.appKeys' | 'dev.main.tab.logs' | 'dev.app.description.title' | 'dev.edit.description.dialog.title' | 'dev.edit.description.dialog.save' | 'dev.edit.description.dialog.cancel' | 'dev.edit.apis.dialog.selectedAPIs' | 'dev.app.key.create' | 'dev.create.key.dialog.title' | 'dev.create.key.dialog.create' | 'dev.create.key.dialog.cancel' | 'dev.app.edit' | 'dev.app.delete' | 'dev.edit.app.dialog.title' | 'dev.edit.app.dialog.save' | 'dev.edit.app.dialog.cancel' | 'dev.delete.app.dialog.title' | 'dev.delete.app.dialog.confirmation' | 'dev.delete.app.dialog.delete' | 'dev.delete.app.dialog.cancel' | 'dev.app.key.roll' | 'dev.roll.key.dialog.title' | 'dev.roll.key.dialog.apiKey' | 'dev.roll.key.dialog.expires' | 'dev.roll.key.dialog.confirmation' | 'dev.roll.key.dialog.cancel' | 'dev.roll.key.dialog.roll' | 'dev.update.key.dialog.title' | 'dev.update.key.dialog.update' | 'dev.update.key.dialog.cancel' | 'dev.app.key.api.name' | 'dev.app.key.api.status' | 'dev.app.key.api.edit' | 'dev.edit.apis.dialog.title' | 'dev.edit.apis.dialog.apiKey' | 'dev.edit.apis.dialog.save' | 'dev.edit.apis.dialog.cancel' | 'dev.select.placeholder' | 'dev.app.overview.status.pending' | 'dev.app.overview.status.approved' | 'dev.app.overview.status.revoked' | 'dev.app.overview.status' | 'dev.app.overview.non-production' | 'dev.app.overview.production' | 'dev.app.overview.clientId' | 'dev.app.overview.apiKey' | 'dev.app.key.revoke' | 'dev.revoke.key.dialog.title' | 'dev.revoke.key.dialog.apiKey' | 'dev.revoke.key.dialog.expires' | 'dev.revoke.key.dialog.confirmation' | 'dev.revoke.key.dialog.revoke' | 'dev.revoke.key.dialog.cancel' | 'dev.app.overview.expires' | 'dev.app.overview.created' | 'dev.app.overview.visibilityToggle.hide' | 'dev.app.overview.visibilityToggle.show' | 'search.loading' | 'search.noResults.title' | 'search.keys.navigate' | 'search.keys.select' | 'search.keys.exit' | 'search.label' | 'search.cancel' | 'search.recent' | 'search.navbar.label' | 'search.suggested' | 'search.showMore' | 'search.filter.title' | 'search.filter.reset' | 'search.filter.field.reset' | 'search.ai.thinkingText' | 'search.ai.resourcesFound' | 'search.ai.button' | 'search.ai.label' | 'search.ai.disclaimer' | 'search.ai.error.description' | 'search.ai.error.header' | 'toc.header' | 'footer.copyrightText' | 'page.homeButton' | 'page.forbidden.title' | 'page.notFound.title' | 'page.notFound.description' | 'page.lastUpdated.timeago' | 'page.lastUpdated.on' | 'catalog.filters.placeholder' | 'catalog.filters.title' | 'catalog.filters.clearAll' | 'catalog.filters.select.addFilter' | 'catalog.filters.select.all' | 'catalog.filters.done' | 'sidebar.menu.backLabel' | 'sidebar.menu.backToLabel' | 'sidebar.actions.show' | 'sidebar.actions.hide' | 'sidebar.actions.changeLayout' | 'versionPicker.label' | 'versionPicker.unversioned' | 'codeSnippet.copy.buttonText' | 'codeSnippet.copy.tooltipText' | 'codeSnippet.copy.toasterText' | 'markdown.editPage.text' | 'feedback.settings.comment.submitText' | 'feedback.settings.comment.label' | 'feedback.settings.comment.send' | 'feedback.settings.comment.cancel' | 'feedback.settings.comment.satisfiedLabel' | 'feedback.settings.comment.neutralLabel' | 'feedback.settings.comment.dissatisfiedLabel' | 'feedback.settings.submitText' | 'feedback.settings.label' | 'feedback.settings.reasons.label' | 'feedback.settings.reasons.send' | 'feedback.settings.comment.likeLabel' | 'feedback.settings.comment.dislikeLabel' | 'feedback.sentiment.thumbUp' | 'feedback.sentiment.thumbDown' | 'feedback.settings.leftScaleLabel' | 'feedback.settings.rightScaleLabel' | 'feedback.settings.optionalEmail.placeholder' | 'feedback.settings.optionalEmail.label' | 'codeSnippet.report.buttonText' | 'codeSnippet.report.tooltipText' | 'codeSnippet.report.label' | 'userMenu.login' | 'userMenu.logout' | 'userMenu.devOnboardingLabel' | 'mobileMenu.mainMenu' | 'mobileMenu.previous' | 'mobileMenu.products' | 'page.nextButton' | 'page.previousButton' | 'openapi.download.description.title' | 'openapi.info.title' | 'openapi.info.contact.url' | 'openapi.info.contact.name' | 'openapi.info.license' | 'openapi.info.termsOfService' | 'openapi.info.metadata.title' | 'openapi.key' | 'openapi.value' | 'openapi.enum' | 'openapi.items' | 'openapi.default' | 'openapi.variable' | 'openapi.variables' | 'openapi.actions.show' | 'openapi.actions.hide' | 'openapi.actions.more' | 'openapi.languages.title' | 'openapi.servers.title' | 'openapi.operations' | 'openapi.webhooks' | 'openapi.description' | 'openapi.badges.deprecated' | 'openapi.badges.required' | 'openapi.badges.webhook' | 'openapi.request' | 'openapi.path' | 'openapi.query' | 'openapi.cookie' | 'openapi.header' | 'openapi.body' | 'openapi.responses' | 'openapi.response' | 'openapi.callbacks' | 'openapi.callbackRequest' | 'openapi.callbackResponse' | 'openapi.payload' | 'openapi.discriminator' | 'openapi.contentType' | 'openapi.tryIt' | 'openapi.loading' | 'openapi.example' | 'openapi.examples' | 'openapi.additionalProperties' | 'openapi.patternProperties' | 'openapi.required' | 'openapi.recursive' | 'openapi.complex' | 'openapi.deprecated' | 'openapi.hideExample' | 'openapi.showExample' | 'openapi.expandAll' | 'openapi.collapseAll' | 'openapi.noResponseExample' | 'openapi.noResponseContent' | 'openapi.noRequestPayload' | 'openapi.hidePattern' | 'openapi.showPattern' | 'openapi.authorizationUrl' | 'openapi.tokenUrl' | 'openapi.refreshUrl' | 'openapi.scopes' | 'openapi.security' | 'openapi.httpAuthorizationScheme' | 'openapi.bearerFormat' | 'openapi.parameterName' | 'openapi.flowType' | 'openapi.connectUrl' | 'openapi.requiredScopes' | 'openapi.unsupportedLanguage' | 'openapi.failedToGenerateCodeSample' | 'graphql.queries' | 'graphql.mutations' | 'graphql.subscriptions' | 'graphql.directives' | 'graphql.objects' | 'graphql.interfaces' | 'graphql.unions' | 'graphql.enums' | 'graphql.inputs' | 'graphql.scalars' | 'graphql.arguments.label' | 'graphql.arguments.show' | 'graphql.arguments.hide' | 'graphql.arguments.here' | 'graphql.returnTypes.label' | 'graphql.returnTypes.show' | 'graphql.returnTypes.hide' | 'graphql.possibleTypes' | 'graphql.defaultValue' | 'graphql.deprecationReason' | 'graphql.implementedInterfaces' | 'graphql.nonNull' | 'graphql.required' | 'graphql.deprecated' | 'graphql.variables' | 'graphql.querySample' | 'graphql.mutationSample' | 'graphql.subscriptionSample' | 'graphql.responseSample' | 'graphql.locations' | 'graphql.sample' | 'graphql.referenced' | 'codeWalkthrough.download' | 'codeWalkthrough.preview';
3
3
  export type Locale = {
4
4
  code: string;
5
5
  name: string;
@@ -0,0 +1,9 @@
1
+ import React from 'react';
2
+ import type { IconProps } from '../../icons/types';
3
+ export declare const ErrorFilledIcon: import("styled-components").StyledComponent<(props: IconProps) => React.JSX.Element, any, {
4
+ 'data-component-name': string;
5
+ } & {
6
+ color?: string;
7
+ size?: string;
8
+ className?: string;
9
+ } & React.SVGProps<SVGSVGElement>, "data-component-name">;
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.ErrorFilledIcon = void 0;
7
+ const react_1 = __importDefault(require("react"));
8
+ const styled_components_1 = __importDefault(require("styled-components"));
9
+ const utils_1 = require("../../core/utils");
10
+ const Icon = (props) => (react_1.default.createElement("svg", Object.assign({ xmlns: "http://www.w3.org/2000/svg", width: "20", height: "20", viewBox: "0 0 20 20", fill: "none" }, props),
11
+ react_1.default.createElement("path", { d: "M10.0002 1.24998C8.84911 1.24285 7.70808 1.4643 6.64326 1.9015C5.57844 2.3387 4.61101 2.98295 3.79707 3.79689C2.98313 4.61083 2.33889 5.57826 1.90168 6.64308C1.46448 7.7079 1.24303 8.84892 1.25017 9.99998C1.24303 11.151 1.46448 12.2921 1.90168 13.3569C2.33889 14.4217 2.98313 15.3891 3.79707 16.2031C4.61101 17.017 5.57844 17.6613 6.64326 18.0985C7.70808 18.5357 8.84911 18.7571 10.0002 18.75C11.1512 18.7571 12.2923 18.5357 13.3571 18.0985C14.4219 17.6613 15.3893 17.017 16.2033 16.2031C17.0172 15.3891 17.6614 14.4217 18.0986 13.3569C18.5359 12.2921 18.7573 11.151 18.7502 9.99998C18.7573 8.84892 18.5359 7.7079 18.0986 6.64308C17.6614 5.57826 17.0172 4.61083 16.2033 3.79689C15.3893 2.98295 14.4219 2.3387 13.3571 1.9015C12.2923 1.4643 11.1512 1.24285 10.0002 1.24998ZM13.4032 14.375L5.62517 6.5973L6.59748 5.62498L14.3752 13.403L13.4032 14.375Z", fill: "#F9316D" })));
12
+ exports.ErrorFilledIcon = (0, styled_components_1.default)(Icon).attrs(() => ({
13
+ 'data-component-name': 'icons/ErrorFilledIcon/ErrorFilledIcon',
14
+ })) `
15
+ path {
16
+ fill: ${({ color }) => (0, utils_1.getCssColorVariable)(color)};
17
+ }
18
+
19
+ height: ${({ size }) => size || '16px'};
20
+ width: ${({ size }) => size || '16px'};
21
+ `;
22
+ //# sourceMappingURL=ErrorFilledIcon.js.map
package/lib/index.d.ts CHANGED
@@ -212,6 +212,7 @@ export * from './icons/CharacterIcon/CharacterIcon';
212
212
  export * from './icons/FileIcon/FileIcon';
213
213
  export * from './icons/ExportIcon/ExportIcon';
214
214
  export * from './icons/CertificateIcon/CertificateIcon';
215
+ export * from './icons/ErrorFilledIcon/ErrorFilledIcon';
215
216
  export * from './layouts/RootLayout';
216
217
  export * from './layouts/PageLayout';
217
218
  export * from './layouts/NotFound';
package/lib/index.js CHANGED
@@ -264,6 +264,7 @@ __exportStar(require("./icons/CharacterIcon/CharacterIcon"), exports);
264
264
  __exportStar(require("./icons/FileIcon/FileIcon"), exports);
265
265
  __exportStar(require("./icons/ExportIcon/ExportIcon"), exports);
266
266
  __exportStar(require("./icons/CertificateIcon/CertificateIcon"), exports);
267
+ __exportStar(require("./icons/ErrorFilledIcon/ErrorFilledIcon"), exports);
267
268
  /* Layouts */
268
269
  __exportStar(require("./layouts/RootLayout"), exports);
269
270
  __exportStar(require("./layouts/PageLayout"), exports);
@@ -16,6 +16,10 @@ exports.img = {
16
16
  alt: {
17
17
  type: String,
18
18
  },
19
+ withLightbox: {
20
+ type: Boolean,
21
+ default: false,
22
+ },
19
23
  className: {
20
24
  type: String,
21
25
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@redocly/theme",
3
- "version": "0.49.1",
3
+ "version": "0.50.1",
4
4
  "description": "Shared UI components lib",
5
5
  "keywords": [
6
6
  "theme",
@@ -11,23 +11,42 @@ export type ImageProps = {
11
11
  width?: string | number;
12
12
  height?: string | number;
13
13
  border?: string;
14
+ withLightbox?: boolean;
14
15
  style?: React.CSSProperties | string;
15
16
  };
16
17
 
17
18
  export function Image(props: ImageProps): JSX.Element {
18
- const { src, srcSet, alt, className, width, height, border, style } = props;
19
+ const { src, srcSet, alt, className, width, height, border, style, withLightbox } = props;
19
20
 
21
+ const [lightboxImage, setLightboxImage] = React.useState<string | undefined>(undefined);
20
22
  const parsedSourceSetMap = React.useMemo(() => {
21
23
  return srcSet ? parseSrcSet(srcSet) : new Map();
22
24
  }, [srcSet]);
23
25
 
24
- const combinedStyle: React.CSSProperties = {
26
+ const handleImageClick = (src: string) => {
27
+ if (!withLightbox || lightboxImage) {
28
+ return;
29
+ }
30
+ setLightboxImage(src);
31
+ };
32
+
33
+ const handleCloseLightbox = () => {
34
+ setLightboxImage(undefined);
35
+ };
36
+
37
+ const combinedStyles: React.CSSProperties = {
38
+ ...(withLightbox && { cursor: 'pointer' }),
25
39
  ...(border && { border }),
26
40
  ...(typeof style === 'string' ? { cssText: style } : style),
27
41
  };
28
42
 
29
43
  return (
30
44
  <>
45
+ {lightboxImage ? (
46
+ <LightboxContainer onClick={handleCloseLightbox}>
47
+ <Image src={lightboxImage} alt={alt} />
48
+ </LightboxContainer>
49
+ ) : null}
31
50
  {src ? (
32
51
  <img
33
52
  src={src}
@@ -35,7 +54,8 @@ export function Image(props: ImageProps): JSX.Element {
35
54
  className={className}
36
55
  width={width}
37
56
  height={height}
38
- style={combinedStyle}
57
+ style={combinedStyles}
58
+ onClick={() => handleImageClick(src)}
39
59
  />
40
60
  ) : (
41
61
  Array.from(parsedSourceSetMap).map(([key, value]) => (
@@ -47,7 +67,9 @@ export function Image(props: ImageProps): JSX.Element {
47
67
  className={className}
48
68
  width={width}
49
69
  height={height}
50
- style={combinedStyle}
70
+ $withLightbox={withLightbox}
71
+ style={combinedStyles}
72
+ onClick={() => handleImageClick(value)}
51
73
  />
52
74
  ))
53
75
  )}
@@ -55,8 +77,32 @@ export function Image(props: ImageProps): JSX.Element {
55
77
  );
56
78
  }
57
79
 
58
- const ColorModeAwareImage = styled.img<{ colorMode: string }>`
80
+ const ColorModeAwareImage = styled.img<{ colorMode: string; $withLightbox?: boolean }>`
59
81
  html:not(.${(props) => props.colorMode}) && {
60
82
  display: none;
61
83
  }
84
+ ${({ $withLightbox }) =>
85
+ $withLightbox &&
86
+ `
87
+ cursor: pointer;
88
+ `}
89
+ `;
90
+
91
+ const LightboxContainer = styled.div`
92
+ position: fixed;
93
+ top: 0;
94
+ left: 0;
95
+ width: 100%;
96
+ height: 100%;
97
+ background-color: var(--bg-color-modal-overlay);
98
+ z-index: var(--z-index-overlay);
99
+ display: flex;
100
+ justify-content: center;
101
+ align-items: center;
102
+
103
+ img {
104
+ cursor: pointer;
105
+ max-width: 90%;
106
+ max-height: 90%;
107
+ }
62
108
  `;
@@ -8,11 +8,14 @@ import { Tag } from '@redocly/theme/components/Tag/Tag';
8
8
  import { Link } from '@redocly/theme/components/Link/Link';
9
9
  import { useThemeHooks } from '@redocly/theme/core/hooks';
10
10
  import { Markdown } from '@redocly/theme/components/Markdown/Markdown';
11
+ import { Admonition } from '@redocly/theme/components/Admonition/Admonition';
12
+ import { ErrorFilledIcon } from '@redocly/theme/icons/ErrorFilledIcon/ErrorFilledIcon';
11
13
 
12
14
  export type SearchAiResponseProps = {
13
15
  question: string;
14
16
  isGeneratingResponse: boolean;
15
17
  response?: string;
18
+ error: string | null;
16
19
  resources: {
17
20
  url: string;
18
21
  title: string;
@@ -21,50 +24,79 @@ export type SearchAiResponseProps = {
21
24
 
22
25
  export function SearchAiResponse(props: SearchAiResponseProps): JSX.Element {
23
26
  const { useMarkdownText } = useThemeHooks();
24
- const { question, response, isGeneratingResponse, resources } = props;
27
+ const { question, response, isGeneratingResponse, resources, error } = props;
25
28
 
26
29
  const { useTranslate } = useThemeHooks();
27
30
  const { translate } = useTranslate();
28
31
  const markdownResponse = useMarkdownText(response || '');
29
32
 
33
+ let responseContainer = null;
34
+
35
+ const hasPendingOrReceivedResponse = response || isGeneratingResponse || error;
36
+ if (hasPendingOrReceivedResponse) {
37
+ let icon;
38
+ switch (true) {
39
+ case error !== null:
40
+ icon = <ErrorFilledIcon size="20px" color="--error-bubble-content-color" />;
41
+ break;
42
+ case isGeneratingResponse:
43
+ icon = <Spinner size="20px" color="--search-ai-spinner-icon-color" />;
44
+ break;
45
+ case Boolean(response):
46
+ icon = <CheckmarkFilledIcon size="20px" color="--search-ai-checkmark-icon-color" />;
47
+ break;
48
+ }
49
+ responseContainer = (
50
+ <>
51
+ <ResponseHeader>
52
+ {icon}
53
+ <Question>{question}</Question>
54
+ </ResponseHeader>
55
+ <ResponseBody>
56
+ {response && <ResponseText children={markdownResponse} as="div" {...props} />}
57
+ {!response && isGeneratingResponse && (
58
+ <ThinkingResponseText data-translation-key="search.ai.thinkingText">
59
+ {translate('search.ai.thinkingText', 'Thinking...')}
60
+ </ThinkingResponseText>
61
+ )}
62
+ {error && (
63
+ <Admonition
64
+ type="danger"
65
+ name={translate('search.ai.error.header', 'Oops! Something went wrong.')}
66
+ >
67
+ {translate(
68
+ 'search.ai.error.description',
69
+ 'We encountered an issue while processing your search. Please try again later or refine your query. If the problem persists, feel free to contact support.',
70
+ )}
71
+ </Admonition>
72
+ )}
73
+ {resources.length && !isGeneratingResponse ? (
74
+ <Resources>
75
+ <ResourcesTitle data-translation-key="search.ai.resourcesFound">
76
+ {resources.length} {translate('search.ai.resourcesFound', 'resources found')}
77
+ </ResourcesTitle>
78
+ <ResourceTags>
79
+ {resources.map((resource, idx) => (
80
+ <Link key={idx} to={resource.url} target="_blank">
81
+ <ResourceTag
82
+ borderless
83
+ icon={<DocumentIcon color="--search-ai-resource-tag-icon-color" />}
84
+ >
85
+ {resource.title}
86
+ </ResourceTag>
87
+ </Link>
88
+ ))}
89
+ </ResourceTags>
90
+ </Resources>
91
+ ) : null}
92
+ </ResponseBody>
93
+ </>
94
+ );
95
+ }
96
+
30
97
  return (
31
98
  <ResponseWrapper data-component-name="Search/SearchAiResponse">
32
- <ResponseHeader>
33
- {isGeneratingResponse ? (
34
- <Spinner size="20px" color="--search-ai-spinner-icon-color" />
35
- ) : (
36
- <CheckmarkFilledIcon size="20px" color="--search-ai-checkmark-icon-color" />
37
- )}
38
- <Question>{question}</Question>
39
- </ResponseHeader>
40
- <ResponseBody>
41
- {response ? (
42
- <ResponseText children={markdownResponse} as="div" {...props} />
43
- ) : (
44
- <ThinkingResponseText data-translation-key="search.ai.thinkingText">
45
- {translate('search.ai.thinkingText', 'Thinking...')}
46
- </ThinkingResponseText>
47
- )}
48
- {resources.length && !isGeneratingResponse ? (
49
- <Resources>
50
- <ResourcesTitle data-translation-key="search.ai.resourcesFound">
51
- {resources.length} {translate('search.ai.resourcesFound', 'resources found')}
52
- </ResourcesTitle>
53
- <ResourceTags>
54
- {resources.map((resource, idx) => (
55
- <Link key={idx} to={resource.url} target="_blank">
56
- <ResourceTag
57
- borderless
58
- icon={<DocumentIcon color="--search-ai-resource-tag-icon-color" />}
59
- >
60
- {resource.title}
61
- </ResourceTag>
62
- </Link>
63
- ))}
64
- </ResourceTags>
65
- </Resources>
66
- ) : null}
67
- </ResponseBody>
99
+ {responseContainer}
68
100
  </ResponseWrapper>
69
101
  );
70
102
  }
@@ -146,7 +146,10 @@ export function SearchDialog({ onClose, className }: SearchDialogProps): JSX.Ele
146
146
  }
147
147
  isLoading={isSearchLoading}
148
148
  showReturnButton={mode === 'ai-dialog'}
149
- onReturn={() => setMode('search')}
149
+ onReturn={() => {
150
+ setMode('search');
151
+ aiSearch.clearAiSearchState();
152
+ }}
150
153
  onSubmit={
151
154
  mode === 'ai-dialog'
152
155
  ? () => {
@@ -250,6 +253,7 @@ export function SearchDialog({ onClose, className }: SearchDialogProps): JSX.Ele
250
253
  isGeneratingResponse={aiSearch.isGeneratingResponse}
251
254
  response={aiSearch.response}
252
255
  resources={aiSearch.resources}
256
+ error={aiSearch.error}
253
257
  />
254
258
  )}
255
259
  </SearchDialogBody>
@@ -18,19 +18,24 @@ export const useColorSwitcher = () => {
18
18
  setActiveColorMode(activeMode || defaultColor);
19
19
  };
20
20
 
21
- const switchColorMode = (): string => {
21
+ const switchColorMode = (mode?: string): void => {
22
+ if (mode && !modes.includes(mode)) {
23
+ return;
24
+ }
25
+
22
26
  const activeIndex = modes.indexOf(activeColorMode);
23
- const mode = activeIndex < modes.length - 1 ? modes[activeIndex + 1] : modes[0];
24
- localStorage.setItem('colorSchema', mode);
25
- document.documentElement.className = `${mode} notransition`;
27
+ // If specific mode is provided, use it, otherwise cycle through modes
28
+ const newMode = mode || (activeIndex < modes.length - 1 ? modes[activeIndex + 1] : modes[0]);
29
+
30
+ localStorage.setItem('colorSchema', newMode);
31
+ document.documentElement.className = `${newMode} notransition`;
26
32
 
27
33
  window.requestAnimationFrame(() => {
28
34
  document.documentElement.classList.remove('notransition');
29
35
  });
30
- telemetry.send('color_mode_switched', { from: activeColorMode, to: mode });
36
+ telemetry.send('color_mode_switched', { from: activeColorMode, to: newMode });
31
37
 
32
- setActiveColorMode(mode);
33
- return mode;
38
+ setActiveColorMode(newMode);
34
39
  };
35
40
 
36
41
  return {
@@ -0,0 +1,5 @@
1
+ export type { ApiFunctionsRequest as Request } from '@redocly/config';
2
+ export type { ApiFunctionsResponse as Response } from '@redocly/config';
3
+ export type { ApiFunctionsContext as Context } from '@redocly/config';
4
+ export type { PropsContext } from '@redocly/config';
5
+ export type { PageStaticData as PropsData } from '@redocly/config';
@@ -88,6 +88,8 @@ export type ThemeHooks = {
88
88
  title: string;
89
89
  url: string;
90
90
  }[];
91
+ error: null | string;
92
+ clearAiSearchState: () => void;
91
93
  };
92
94
  useMarkdownText: (text: string) => React.ReactNode;
93
95
  useFacetQuery: (field: string) => {
@@ -10,3 +10,4 @@ export * from '@redocly/theme/core/types/select';
10
10
  export * from '@redocly/theme/core/types/sidebar';
11
11
  export * from '@redocly/theme/core/types/filter';
12
12
  export * from '@redocly/theme/core/types/user-menu';
13
+ export * from '@redocly/theme/core/types/api-functions';
@@ -88,6 +88,8 @@ export type TranslationKey =
88
88
  | 'search.ai.button'
89
89
  | 'search.ai.label'
90
90
  | 'search.ai.disclaimer'
91
+ | 'search.ai.error.description'
92
+ | 'search.ai.error.header'
91
93
  | 'toc.header'
92
94
  | 'footer.copyrightText'
93
95
  | 'page.homeButton'
@@ -0,0 +1,33 @@
1
+ import React from 'react';
2
+ import styled from 'styled-components';
3
+
4
+ import type { IconProps } from '@redocly/theme/icons/types';
5
+
6
+ import { getCssColorVariable } from '@redocly/theme/core/utils';
7
+
8
+ const Icon = (props: IconProps) => (
9
+ <svg
10
+ xmlns="http://www.w3.org/2000/svg"
11
+ width="20"
12
+ height="20"
13
+ viewBox="0 0 20 20"
14
+ fill="none"
15
+ {...props}
16
+ >
17
+ <path
18
+ d="M10.0002 1.24998C8.84911 1.24285 7.70808 1.4643 6.64326 1.9015C5.57844 2.3387 4.61101 2.98295 3.79707 3.79689C2.98313 4.61083 2.33889 5.57826 1.90168 6.64308C1.46448 7.7079 1.24303 8.84892 1.25017 9.99998C1.24303 11.151 1.46448 12.2921 1.90168 13.3569C2.33889 14.4217 2.98313 15.3891 3.79707 16.2031C4.61101 17.017 5.57844 17.6613 6.64326 18.0985C7.70808 18.5357 8.84911 18.7571 10.0002 18.75C11.1512 18.7571 12.2923 18.5357 13.3571 18.0985C14.4219 17.6613 15.3893 17.017 16.2033 16.2031C17.0172 15.3891 17.6614 14.4217 18.0986 13.3569C18.5359 12.2921 18.7573 11.151 18.7502 9.99998C18.7573 8.84892 18.5359 7.7079 18.0986 6.64308C17.6614 5.57826 17.0172 4.61083 16.2033 3.79689C15.3893 2.98295 14.4219 2.3387 13.3571 1.9015C12.2923 1.4643 11.1512 1.24285 10.0002 1.24998ZM13.4032 14.375L5.62517 6.5973L6.59748 5.62498L14.3752 13.403L13.4032 14.375Z"
19
+ fill="#F9316D"
20
+ />
21
+ </svg>
22
+ );
23
+
24
+ export const ErrorFilledIcon = styled(Icon).attrs(() => ({
25
+ 'data-component-name': 'icons/ErrorFilledIcon/ErrorFilledIcon',
26
+ }))<IconProps>`
27
+ path {
28
+ fill: ${({ color }) => getCssColorVariable(color)};
29
+ }
30
+
31
+ height: ${({ size }) => size || '16px'};
32
+ width: ${({ size }) => size || '16px'};
33
+ `;
package/src/index.ts CHANGED
@@ -236,6 +236,7 @@ export * from '@redocly/theme/icons/CharacterIcon/CharacterIcon';
236
236
  export * from '@redocly/theme/icons/FileIcon/FileIcon';
237
237
  export * from '@redocly/theme/icons/ExportIcon/ExportIcon';
238
238
  export * from '@redocly/theme/icons/CertificateIcon/CertificateIcon';
239
+ export * from '@redocly/theme/icons/ErrorFilledIcon/ErrorFilledIcon';
239
240
  /* Layouts */
240
241
  export * from '@redocly/theme/layouts/RootLayout';
241
242
  export * from '@redocly/theme/layouts/PageLayout';
@@ -15,6 +15,10 @@ export const img: MarkdocSchemaWrapper = {
15
15
  alt: {
16
16
  type: String,
17
17
  },
18
+ withLightbox: {
19
+ type: Boolean,
20
+ default: false,
21
+ },
18
22
  className: {
19
23
  type: String,
20
24
  },