@redocly/theme 0.61.1 → 0.62.0-custom.0

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.
Files changed (188) hide show
  1. package/lib/components/AsyncApiDocs/hooks/AfterAsyncApiChannelDescription.d.ts +1 -0
  2. package/lib/components/AsyncApiDocs/hooks/AfterAsyncApiChannelDescription.js +12 -0
  3. package/lib/components/Badge/Badge.d.ts +2 -1
  4. package/lib/components/Badge/Badge.js +24 -2
  5. package/lib/components/Banner/Banner.js +19 -1
  6. package/lib/components/Banner/variables.js +1 -0
  7. package/lib/components/Breadcrumbs/Breadcrumb.js +1 -1
  8. package/lib/components/Breadcrumbs/BreadcrumbDropdown.js +9 -6
  9. package/lib/components/Breadcrumbs/Breadcrumbs.js +24 -15
  10. package/lib/components/Buttons/AIAssistantButton.js +7 -4
  11. package/lib/components/Catalog/CatalogEntities.js +10 -8
  12. package/lib/components/Catalog/CatalogEntity/CatalogEntity.js +2 -2
  13. package/lib/components/Catalog/CatalogEntity/CatalogEntityHistory/CatalogEntityHistorySidebar.js +1 -1
  14. package/lib/components/Catalog/CatalogEntity/CatalogEntityProperties/TagsProperty.js +2 -2
  15. package/lib/components/Catalog/CatalogEntity/CatalogEntityRelations/CatalogEntityRelationsTable.js +13 -11
  16. package/lib/components/Catalog/CatalogEntity/CatalogEntitySchema.js +7 -5
  17. package/lib/components/Catalog/CatalogFilter/CatalogFilterCheckboxes.js +9 -7
  18. package/lib/components/Catalog/CatalogTableView/CatalogTableViewRow.js +1 -1
  19. package/lib/components/Catalog/CatalogTagsWithTooltip.js +2 -2
  20. package/lib/components/CatalogClassic/CatalogClassicInfoBlock.js +1 -1
  21. package/lib/components/CodeBlock/CodeBlockControls.js +8 -6
  22. package/lib/components/Filter/FilterCheckboxes.js +1 -1
  23. package/lib/components/JsonViewer/JsonViewer.js +2 -2
  24. package/lib/components/JsonViewer/{Helpers.js → helpers.js} +2 -1
  25. package/lib/components/LanguagePicker/LanguagePicker.js +1 -1
  26. package/lib/components/Markdown/Markdown.js +2 -2
  27. package/lib/components/Menu/MenuItem.js +41 -15
  28. package/lib/components/Navbar/NavbarItem.js +1 -1
  29. package/lib/components/OpenApiDocs/hooks/AdditionalOverviewInfo.d.ts +1 -0
  30. package/lib/components/OpenApiDocs/hooks/AdditionalOverviewInfo.js +12 -0
  31. package/lib/components/OpenApiDocs/hooks/AfterOpenApiDescription.d.ts +1 -0
  32. package/lib/components/OpenApiDocs/hooks/AfterOpenApiDescription.js +6 -0
  33. package/lib/components/PageActions/PageActions.js +25 -8
  34. package/lib/components/Search/SearchAiDialog.d.ts +4 -2
  35. package/lib/components/Search/SearchAiDialog.js +23 -4
  36. package/lib/components/Search/SearchAiMessage.d.ts +4 -2
  37. package/lib/components/Search/SearchAiMessage.js +82 -23
  38. package/lib/components/Search/SearchDialog.js +50 -25
  39. package/lib/components/Select/variables.js +2 -2
  40. package/lib/components/SvgViewer/SvgViewer.d.ts +15 -0
  41. package/lib/components/SvgViewer/SvgViewer.js +312 -0
  42. package/lib/components/SvgViewer/variables.d.ts +1 -0
  43. package/lib/components/SvgViewer/variables.dark.d.ts +1 -0
  44. package/lib/components/SvgViewer/variables.dark.js +8 -0
  45. package/lib/components/SvgViewer/variables.js +17 -0
  46. package/lib/components/Tag/Tag.js +1 -1
  47. package/lib/components/Tag/variables.dark.js +6 -0
  48. package/lib/components/Tag/variables.js +6 -0
  49. package/lib/components/Tooltip/Tooltip.d.ts +2 -3
  50. package/lib/components/Tooltip/Tooltip.js +66 -113
  51. package/lib/components/Tooltip/variables.dark.js +4 -0
  52. package/lib/components/Tooltip/variables.js +3 -3
  53. package/lib/components/UserMenu/LoginButton.d.ts +8 -2
  54. package/lib/components/UserMenu/LoginButton.js +4 -3
  55. package/lib/core/constants/search.d.ts +5 -1
  56. package/lib/core/constants/search.js +24 -1
  57. package/lib/core/hooks/search/use-search-dialog.js +2 -2
  58. package/lib/core/hooks/use-color-switcher.js +3 -1
  59. package/lib/core/hooks/use-mcp-config.js +2 -1
  60. package/lib/core/hooks/use-modal-scroll-lock.js +24 -10
  61. package/lib/core/hooks/use-outside-click.d.ts +3 -1
  62. package/lib/core/hooks/use-outside-click.js +8 -4
  63. package/lib/core/hooks/use-page-actions.d.ts +1 -1
  64. package/lib/core/hooks/use-page-actions.js +44 -11
  65. package/lib/core/hooks/use-product-picker.js +1 -1
  66. package/lib/core/hooks/use-unique-svg-ids.d.ts +6 -0
  67. package/lib/core/hooks/use-unique-svg-ids.js +15 -0
  68. package/lib/core/openapi/index.d.ts +1 -0
  69. package/lib/core/openapi/index.js +3 -1
  70. package/lib/core/styles/dark.js +2 -0
  71. package/lib/core/styles/global.js +31 -15
  72. package/lib/core/types/catalog.d.ts +1 -1
  73. package/lib/core/types/hooks.d.ts +23 -2
  74. package/lib/core/types/l10n.d.ts +1 -1
  75. package/lib/core/types/search.d.ts +24 -0
  76. package/lib/core/types/search.js +9 -1
  77. package/lib/core/utils/content-segments.d.ts +2 -0
  78. package/lib/core/utils/content-segments.js +22 -0
  79. package/lib/core/utils/index.d.ts +1 -0
  80. package/lib/core/utils/index.js +1 -0
  81. package/lib/core/utils/transform-revisions-to-version-history.js +8 -51
  82. package/lib/ext/process-scorecard.d.ts +5 -0
  83. package/lib/ext/process-scorecard.js +11 -0
  84. package/lib/icons/FitToViewIcon/FitToViewIcon.d.ts +9 -0
  85. package/lib/icons/FitToViewIcon/FitToViewIcon.js +25 -0
  86. package/lib/index.d.ts +8 -0
  87. package/lib/index.js +8 -0
  88. package/lib/layouts/DocumentationLayout.js +4 -25
  89. package/lib/layouts/DocumentationLayoutBottom.d.ts +11 -0
  90. package/lib/layouts/DocumentationLayoutBottom.js +28 -0
  91. package/lib/layouts/DocumentationLayoutTop.d.ts +13 -0
  92. package/lib/layouts/DocumentationLayoutTop.js +33 -0
  93. package/lib/layouts/Forbidden.js +22 -18
  94. package/lib/markdoc/components/Cards/Card.js +1 -0
  95. package/lib/markdoc/components/CodeWalkthrough/CodeFilters.js +1 -1
  96. package/lib/markdoc/components/Heading/Heading.js +40 -2
  97. package/lib/markdoc/components/LoginButton/LoginButton.d.ts +9 -0
  98. package/lib/markdoc/components/LoginButton/LoginButton.js +48 -0
  99. package/lib/markdoc/components/Mermaid/Mermaid.js +70 -2
  100. package/lib/markdoc/components/default.d.ts +1 -0
  101. package/lib/markdoc/components/default.js +1 -0
  102. package/lib/markdoc/default.d.ts +6 -0
  103. package/lib/markdoc/default.js +2 -0
  104. package/lib/markdoc/tags/login-button.d.ts +2 -0
  105. package/lib/markdoc/tags/login-button.js +32 -0
  106. package/package.json +8 -8
  107. package/src/components/AsyncApiDocs/hooks/AfterAsyncApiChannelDescription.tsx +10 -0
  108. package/src/components/Badge/Badge.tsx +18 -2
  109. package/src/components/Banner/Banner.tsx +23 -1
  110. package/src/components/Banner/variables.ts +1 -0
  111. package/src/components/Breadcrumbs/Breadcrumb.tsx +3 -3
  112. package/src/components/Breadcrumbs/BreadcrumbDropdown.tsx +11 -8
  113. package/src/components/Breadcrumbs/Breadcrumbs.tsx +24 -15
  114. package/src/components/Buttons/AIAssistantButton.tsx +7 -4
  115. package/src/components/Catalog/CatalogEntities.tsx +10 -8
  116. package/src/components/Catalog/CatalogEntity/CatalogEntity.tsx +1 -1
  117. package/src/components/Catalog/CatalogEntity/CatalogEntityHistory/CatalogEntityHistorySidebar.tsx +1 -2
  118. package/src/components/Catalog/CatalogEntity/CatalogEntityProperties/TagsProperty.tsx +1 -1
  119. package/src/components/Catalog/CatalogEntity/CatalogEntityRelations/CatalogEntityRelationsTable.tsx +13 -11
  120. package/src/components/Catalog/CatalogEntity/CatalogEntitySchema.tsx +7 -5
  121. package/src/components/Catalog/CatalogFilter/CatalogFilterCheckboxes.tsx +9 -7
  122. package/src/components/Catalog/CatalogTableView/CatalogTableViewRow.tsx +1 -2
  123. package/src/components/Catalog/CatalogTagsWithTooltip.tsx +9 -5
  124. package/src/components/CatalogClassic/CatalogClassicInfoBlock.tsx +3 -1
  125. package/src/components/CodeBlock/CodeBlockControls.tsx +16 -10
  126. package/src/components/Filter/FilterCheckboxes.tsx +1 -1
  127. package/src/components/JsonViewer/JsonViewer.tsx +1 -2
  128. package/src/components/JsonViewer/{Helpers.tsx → helpers.tsx} +1 -0
  129. package/src/components/LanguagePicker/LanguagePicker.tsx +1 -1
  130. package/src/components/Markdown/Markdown.tsx +2 -2
  131. package/src/components/Menu/MenuItem.tsx +61 -16
  132. package/src/components/Navbar/NavbarItem.tsx +3 -1
  133. package/src/components/OpenApiDocs/hooks/AdditionalOverviewInfo.tsx +10 -0
  134. package/src/components/OpenApiDocs/hooks/AfterOpenApiDescription.tsx +2 -0
  135. package/src/components/PageActions/PageActions.tsx +38 -15
  136. package/src/components/Search/SearchAiDialog.tsx +31 -2
  137. package/src/components/Search/SearchAiMessage.tsx +103 -17
  138. package/src/components/Search/SearchDialog.tsx +70 -37
  139. package/src/components/Select/variables.ts +2 -2
  140. package/src/components/SvgViewer/SvgViewer.tsx +405 -0
  141. package/src/components/SvgViewer/variables.dark.ts +5 -0
  142. package/src/components/SvgViewer/variables.ts +14 -0
  143. package/src/components/Tag/Tag.tsx +2 -1
  144. package/src/components/Tag/variables.dark.ts +6 -0
  145. package/src/components/Tag/variables.ts +6 -0
  146. package/src/components/Tooltip/Tooltip.tsx +77 -120
  147. package/src/components/Tooltip/variables.dark.ts +4 -0
  148. package/src/components/Tooltip/variables.ts +3 -3
  149. package/src/components/UserMenu/LoginButton.tsx +23 -8
  150. package/src/core/constants/search.ts +27 -1
  151. package/src/core/hooks/__mocks__/use-theme-hooks.ts +10 -1
  152. package/src/core/hooks/search/use-search-dialog.ts +2 -2
  153. package/src/core/hooks/use-color-switcher.ts +3 -1
  154. package/src/core/hooks/use-mcp-config.ts +2 -1
  155. package/src/core/hooks/use-modal-scroll-lock.ts +29 -10
  156. package/src/core/hooks/use-outside-click.ts +16 -5
  157. package/src/core/hooks/use-page-actions.ts +66 -25
  158. package/src/core/hooks/use-product-picker.ts +1 -1
  159. package/src/core/hooks/use-unique-svg-ids.ts +12 -0
  160. package/src/core/openapi/index.ts +1 -0
  161. package/src/core/styles/dark.ts +2 -0
  162. package/src/core/styles/global.ts +31 -15
  163. package/src/core/types/catalog.ts +1 -1
  164. package/src/core/types/hooks.ts +29 -1
  165. package/src/core/types/l10n.ts +12 -1
  166. package/src/core/types/search.ts +19 -0
  167. package/src/core/utils/content-segments.ts +27 -0
  168. package/src/core/utils/index.ts +1 -0
  169. package/src/core/utils/transform-revisions-to-version-history.ts +8 -80
  170. package/src/ext/process-scorecard.ts +14 -0
  171. package/src/icons/FitToViewIcon/FitToViewIcon.tsx +26 -0
  172. package/src/index.ts +8 -0
  173. package/src/layouts/DocumentationLayout.tsx +4 -30
  174. package/src/layouts/DocumentationLayoutBottom.tsx +42 -0
  175. package/src/layouts/DocumentationLayoutTop.tsx +52 -0
  176. package/src/layouts/Forbidden.tsx +36 -21
  177. package/src/markdoc/components/Cards/Card.tsx +1 -0
  178. package/src/markdoc/components/CodeWalkthrough/CodeFilters.tsx +1 -1
  179. package/src/markdoc/components/Heading/Heading.tsx +52 -4
  180. package/src/markdoc/components/LoginButton/LoginButton.tsx +38 -0
  181. package/src/markdoc/components/Mermaid/Mermaid.tsx +57 -8
  182. package/src/markdoc/components/default.ts +1 -0
  183. package/src/markdoc/default.ts +2 -0
  184. package/src/markdoc/tags/login-button.ts +30 -0
  185. package/lib/components/Tooltip/TooltipWrapper.d.ts +0 -12
  186. package/lib/components/Tooltip/TooltipWrapper.js +0 -34
  187. package/src/components/Tooltip/TooltipWrapper.tsx +0 -70
  188. /package/lib/components/JsonViewer/{Helpers.d.ts → helpers.d.ts} +0 -0
@@ -32,6 +32,7 @@ const DEFAULT_ENABLED_ACTIONS = [
32
32
  'claude',
33
33
  'docs-mcp-cursor',
34
34
  'docs-mcp-vscode',
35
+ 'docs-mcp-json',
35
36
  ] as const;
36
37
 
37
38
  export type PageActionType =
@@ -41,6 +42,7 @@ export type PageActionType =
41
42
  | 'claude'
42
43
  | 'docs-mcp-cursor'
43
44
  | 'docs-mcp-vscode'
45
+ | 'docs-mcp-json'
44
46
  | 'mcp-cursor'
45
47
  | 'mcp-vscode';
46
48
 
@@ -88,10 +90,12 @@ export function usePageActions(
88
90
  translate,
89
91
  onClickCallback: isDocsMcp
90
92
  ? () =>
91
- telemetry.sendPageActionsButtonClickedMessage({
92
- ...createPageActionResource(pageSlug, pageUrl),
93
- action_type: `docs-mcp-${clientType}` as const,
94
- })
93
+ telemetry.sendPageActionsButtonClickedMessage([
94
+ {
95
+ ...createPageActionResource(pageSlug, pageUrl),
96
+ action_type: `docs-mcp-${clientType}` as const,
97
+ },
98
+ ])
95
99
  : undefined,
96
100
  });
97
101
  },
@@ -99,7 +103,7 @@ export function usePageActions(
99
103
  );
100
104
 
101
105
  const result: PageAction[] = useMemo(() => {
102
- if (shouldHideAllActions) {
106
+ if (shouldHideAllActions && !actions?.length) {
103
107
  return [];
104
108
  }
105
109
 
@@ -131,10 +135,12 @@ export function usePageActions(
131
135
  }
132
136
  const text = await result.text();
133
137
  ClipboardService.copyCustom(text);
134
- telemetry.sendPageActionsButtonClickedMessage({
135
- ...createPageActionResource(pageSlug, pageUrl),
136
- action_type: 'copy',
137
- });
138
+ telemetry.sendPageActionsButtonClickedMessage([
139
+ {
140
+ ...createPageActionResource(pageSlug, pageUrl),
141
+ action_type: 'copy',
142
+ },
143
+ ]);
138
144
  } catch (error) {
139
145
  console.error(error);
140
146
  }
@@ -148,10 +154,13 @@ export function usePageActions(
148
154
  iconComponent: MarkdownFullIcon,
149
155
  link: mdPageUrl,
150
156
  onClick: () => {
151
- telemetry.sendPageActionsButtonClickedMessage({
152
- ...createPageActionResource(pageSlug, pageUrl),
153
- action_type: 'view',
154
- });
157
+ telemetry.sendPageActionsButtonClickedMessage([
158
+ {
159
+ ...createPageActionResource(pageSlug, pageUrl),
160
+ action_type: 'view',
161
+ },
162
+ ]);
163
+ window.location.href = mdPageUrl;
155
164
  },
156
165
  }),
157
166
 
@@ -159,17 +168,21 @@ export function usePageActions(
159
168
  if (!isPublic) {
160
169
  return null;
161
170
  }
171
+ const link = getExternalAiPromptLink('https://chat.openai.com', mdPageUrl);
162
172
  return {
163
173
  buttonText: translate('page.actions.chatGptButtonText', 'Open in ChatGPT'),
164
174
  title: translate('page.actions.chatGptTitle', 'Open in ChatGPT'),
165
175
  description: translate('page.actions.chatGptDescription', 'Get insights from ChatGPT'),
166
176
  iconComponent: ChatGptIcon,
167
- link: getExternalAiPromptLink('https://chat.openai.com', mdPageUrl),
177
+ link,
168
178
  onClick: () => {
169
- telemetry.sendPageActionsButtonClickedMessage({
170
- ...createPageActionResource(pageSlug, pageUrl),
171
- action_type: 'chatgpt',
172
- });
179
+ telemetry.sendPageActionsButtonClickedMessage([
180
+ {
181
+ ...createPageActionResource(pageSlug, pageUrl),
182
+ action_type: 'chatgpt',
183
+ },
184
+ ]);
185
+ window.location.href = link;
173
186
  },
174
187
  };
175
188
  },
@@ -178,17 +191,43 @@ export function usePageActions(
178
191
  if (!isPublic) {
179
192
  return null;
180
193
  }
194
+ const link = getExternalAiPromptLink('https://claude.ai/new', mdPageUrl);
181
195
  return {
182
196
  buttonText: translate('page.actions.claudeButtonText', 'Open in Claude'),
183
197
  title: translate('page.actions.claudeTitle', 'Open in Claude'),
184
198
  description: translate('page.actions.claudeDescription', 'Get insights from Claude'),
185
199
  iconComponent: ClaudeIcon,
186
- link: getExternalAiPromptLink('https://claude.ai/new', mdPageUrl),
200
+ link,
187
201
  onClick: () => {
188
- telemetry.sendPageActionsButtonClickedMessage({
189
- ...createPageActionResource(pageSlug, pageUrl),
190
- action_type: 'claude',
191
- });
202
+ telemetry.sendPageActionsButtonClickedMessage([
203
+ {
204
+ ...createPageActionResource(pageSlug, pageUrl),
205
+ action_type: 'claude',
206
+ },
207
+ ]);
208
+ window.location.href = link;
209
+ },
210
+ };
211
+ },
212
+ 'docs-mcp-json': () => {
213
+ return {
214
+ buttonText: 'Copy MCP configuration',
215
+ title: 'Copy MCP JSON configuration',
216
+ description: 'Copy MCP JSON configuration',
217
+ iconComponent: CopyIcon,
218
+ onClick: async () => {
219
+ ClipboardService.copyCustom(
220
+ JSON.stringify(
221
+ {
222
+ 'mcp-server': {
223
+ url: mcpUrl,
224
+ description: 'MCP Server',
225
+ },
226
+ },
227
+ null,
228
+ 2,
229
+ ),
230
+ );
192
231
  },
193
232
  };
194
233
  },
@@ -205,6 +244,7 @@ export function usePageActions(
205
244
  translate,
206
245
  isPublic,
207
246
  createMCPHandler,
247
+ mcpUrl,
208
248
  telemetry,
209
249
  ]);
210
250
 
@@ -280,9 +320,10 @@ function shouldHidePageActions(
280
320
  }
281
321
 
282
322
  // Page is excluded from search
323
+ const isOpenApiPage =
324
+ pageProps?.metadata?.type === 'openapi' || pageProps?.metadata?.subType === 'openapi-operation';
283
325
  const isPageExcludedFromSearch =
284
- pageProps?.frontmatter?.excludeFromSearch ||
285
- (pageProps?.metadata?.type === 'openapi' && openapiExcludeFromSearch);
326
+ pageProps?.frontmatter?.excludeFromSearch || (isOpenApiPage && openapiExcludeFromSearch);
286
327
 
287
328
  if (isPageExcludedFromSearch) {
288
329
  return true;
@@ -14,7 +14,7 @@ export function useProductPicker() {
14
14
  const loadAndNavigate = useLoadAndNavigate();
15
15
  function setProduct(product: typeof currentProduct) {
16
16
  if (!product) return;
17
- telemetry.sendProductPickedMessage({ product: product.slug });
17
+ telemetry.sendProductPickedMessage([{ object: 'product', product: product.slug }]);
18
18
  if (typeof document === 'undefined') return;
19
19
 
20
20
  if (product.name) {
@@ -0,0 +1,12 @@
1
+ import { useId } from 'react';
2
+
3
+ /**
4
+ * Returns a function that appends a per-component-instance suffix to SVG ids.
5
+ * This prevents collisions when multiple identical SVGs are rendered on the same page,
6
+ * which can break `url(#...)` references (gradients, clipPath, masks, filters) on reflow.
7
+ */
8
+ export function useUniqueSvgIds(): (id: string) => string {
9
+ const reactId = useId();
10
+ const safeSuffix = reactId.replace(/:/g, '_');
11
+ return (id: string): string => `${id}-${safeSuffix}`;
12
+ }
@@ -38,3 +38,4 @@ export { SecurityVariablesEnvSuffix } from '../constants/environments';
38
38
  export { isUndefined, isString, isNotNull, isObject } from '../utils/type-guards';
39
39
  export { ThemeDataContext, type ThemeDataTransferObject } from '../contexts/ThemeDataContext';
40
40
  export { SearchSessionProvider, SearchSessionContext } from '../contexts/SearchContext';
41
+ export { useUniqueSvgIds } from '../hooks/use-unique-svg-ids';
@@ -16,6 +16,7 @@ import { pageActionsDarkMode } from '@redocly/theme/components/PageActions/varia
16
16
  import { tooltipDarkMode } from '@redocly/theme/components/Tooltip/variables.dark';
17
17
  import { bannerDarkMode } from '@redocly/theme/components/Banner/variables.dark';
18
18
  import { admonitionDarkMode } from '@redocly/theme/components/Admonition/variables.dark';
19
+ import { svgViewerDarkMode } from '@redocly/theme/components/SvgViewer/variables.dark';
19
20
 
20
21
  const replayDarkMode = css`
21
22
  /**
@@ -332,6 +333,7 @@ export const darkMode = css`
332
333
  ${tooltipDarkMode}
333
334
  ${bannerDarkMode}
334
335
  ${admonitionDarkMode}
336
+ ${svgViewerDarkMode}
335
337
 
336
338
  /**
337
339
  * @tokens Dark Theme Scrollbar Config
@@ -42,6 +42,7 @@ import { cards } from '@redocly/theme/markdoc/components/Cards/variables';
42
42
  import { codeWalkthrough } from '@redocly/theme/markdoc/components/CodeWalkthrough/variables';
43
43
  import { skipContent } from '@redocly/theme/components/SkipContent/variables';
44
44
  import { pageActions } from '@redocly/theme/components/PageActions/variables';
45
+ import { svgViewer } from '@redocly/theme/components/SvgViewer/variables';
45
46
 
46
47
  import { darkMode } from './dark';
47
48
 
@@ -971,23 +972,41 @@ const pages = css`
971
972
  */
972
973
 
973
974
  --page-403-font-family: var(--font-family-base); // @presenter FontFamily
975
+ --page-403-margin-vertical: var(--spacing-xl); // @presenter Spacing
976
+ --page-403-margin-horizontal: calc(var(--spacing-xxl) * 2); // @presenter Spacing
977
+ --page-403-gap: var(--spacing-lg); // @presenter Spacing
978
+ --page-403-max-width: 680px; // @presenter Width
979
+
980
+ --page-403-status-text-color: var(--text-color-helper); // @presenter Color
981
+ --page-403-status-font-size: var(--font-size-lg); // @presenter FontSize
982
+ --page-403-status-font-weight: var(--font-weight-semibold); // @presenter FontWeight
983
+ --page-403-status-line-height: var(--line-height-lg); // @presenter LineHeight
984
+
985
+ --page-403-title-text-color: var(--text-color-primary); // @presenter Color
986
+ --page-403-title-font-size: 42px; // @presenter FontSize
987
+ --page-403-title-font-weight: var(--font-weight-bold); // @presenter FontWeight
988
+ --page-403-title-line-height: 50px; // @presenter LineHeight
989
+
990
+ --page-403-description-text-color: var(--text-color-secondary); // @presenter Color
991
+ --page-403-description-font-size: var(--font-size-xl); // @presenter FontSize
992
+ --page-403-description-font-weight: var(--font-weight-regular); // @presenter FontWeight
993
+ --page-403-description-line-height: var(--line-height-xl); // @presenter LineHeight
994
+
995
+ // @tokens End
996
+
997
+ /**
998
+ * @tokens 403 Page OIDC Forbidden
999
+ * @presenter Color
1000
+ */
974
1001
 
975
- --page-403-header-text-color: var(--h1-text-color);
1002
+ --page-403-header-text-color: var(--h1-text-color); // @presenter Color
976
1003
  --page-403-header-font-size: var(--h1-font-size); // @presenter FontSize
977
1004
  --page-403-header-font-weight: var(--h1-font-weight); // @presenter FontWeight
978
1005
  --page-403-header-line-height: var(--h1-line-height); // @presenter LineHeight
979
1006
  --page-403-header-margin: 0; // @presenter Spacing
980
-
981
- --page-403-description-text-color: var(--text-color-secondary);
982
- --page-403-description-font-size: 1.5em; // @presenter FontSize
983
- --page-403-description-font-weight: var(--font-weight-regular); // @presenter FontWeight
984
- --page-403-description-line-height: 1; // @presenter LineHeight
985
1007
  --page-403-description-margin: 0; // @presenter Spacing
986
-
987
- --page-403-button-margin: 4em; // @presenter Spacing
988
-
989
- --page-403-oidc-description-font-size: var(--font-size-lg);
990
- --page-403-oidc-description-margin: var(--spacing-md) var(--spacing-sm);
1008
+ --page-403-oidc-description-font-size: var(--font-size-lg); // @presenter FontSize
1009
+ --page-403-oidc-description-margin: var(--spacing-md) var(--spacing-sm); // @presenter Spacing
991
1010
 
992
1011
  // @tokens End
993
1012
 
@@ -1085,10 +1104,6 @@ const error = css`
1085
1104
  `;
1086
1105
 
1087
1106
  const modal = css`
1088
- body:has(.scroll-lock) {
1089
- overflow: hidden;
1090
- }
1091
-
1092
1107
  --modal-box-shadow: var(--bg-raised-shadow);
1093
1108
  --modal-bg-color: var(--bg-color);
1094
1109
  `;
@@ -1301,6 +1316,7 @@ export const styles = css`
1301
1316
  ${replay}
1302
1317
  ${skipContent}
1303
1318
  ${pageActions}
1319
+ ${svgViewer}
1304
1320
 
1305
1321
  background-color: var(--bg-color);
1306
1322
  color: var(--text-color-primary);
@@ -154,7 +154,7 @@ export type BffCatalogRelatedEntity = {
154
154
  key: string;
155
155
  title: string;
156
156
  summary?: string | null;
157
- readonly source: 'api' | 'file';
157
+ readonly source: 'remote' | 'file';
158
158
  relationRole: 'source' | 'target';
159
159
  relationType: EntityRelationType;
160
160
  sourceFile?: string | null;
@@ -28,6 +28,8 @@ import type {
28
28
  SearchFilterItem,
29
29
  SearchFacetQuery,
30
30
  AiSearchConversationItem,
31
+ ToolCallName,
32
+ ContentSegment,
31
33
  } from './search';
32
34
  import type { SubmitFeedbackParams } from './feedback';
33
35
  import type { TFunction } from './l10n';
@@ -127,6 +129,13 @@ export type ThemeHooks = {
127
129
  error: null | AiSearchError;
128
130
  conversation: AiSearchConversationItem[];
129
131
  setConversation: React.Dispatch<React.SetStateAction<AiSearchConversationItem[]>>;
132
+ toolCalls: Array<{
133
+ name: ToolCallName;
134
+ args: unknown;
135
+ position: number;
136
+ result?: { documentCount: number };
137
+ }>;
138
+ contentSegments: ContentSegment[];
130
139
  };
131
140
  useMarkdownText: (text: string) => React.ReactNode;
132
141
  useFacetQuery: (field: string) => {
@@ -177,7 +186,26 @@ export type ThemeHooks = {
177
186
  | 'startSpan'
178
187
  | 'constructCloudEvent'
179
188
  | 'sendToOtelService'
180
- >;
189
+ > & {
190
+ send<TEventType extends AsyncApiRealmUI.EventType>(
191
+ event: TEventType,
192
+ data?: AsyncApiRealmUI.EventPayload<TEventType>,
193
+ ): void;
194
+ /**
195
+ * @deprecated This method is deprecated. Use send(event, data) instead.
196
+ */
197
+ send<TEventType extends AsyncApiRealmUI.EventType>(
198
+ params: AsyncApiRealmUI.SendEventParams<TEventType>,
199
+ ): void;
200
+ };
201
+ /**
202
+ * @deprecated This hook is deprecated. Use `useTelemetry` instead.
203
+ */
204
+ useOtelTelemetry: () => {
205
+ send<TEventType extends AsyncApiRealmUI.EventType>(
206
+ params: AsyncApiRealmUI.SendEventParams<TEventType>,
207
+ ): void;
208
+ };
181
209
  useUserTeams: () => string[];
182
210
  usePageData: () => PageData | null;
183
211
  usePageSharedData: <T = unknown>(dataId: string) => T;
@@ -87,6 +87,7 @@ export type TranslationKey =
87
87
  | 'search.ai.newConversation'
88
88
  | 'search.ai.backToSearch'
89
89
  | 'search.ai.back'
90
+ | 'search.ai.assistant'
90
91
  | 'search.ai.placeholder'
91
92
  | 'search.ai.generatingResponse'
92
93
  | 'search.ai.followUpQuestion'
@@ -98,6 +99,9 @@ export type TranslationKey =
98
99
  | 'search.ai.feedback.title'
99
100
  | 'search.ai.feedback.detailsPlaceholder'
100
101
  | 'search.ai.feedback.thanks'
102
+ | 'search.ai.toolResult.found'
103
+ | 'search.ai.toolResult.found.documents'
104
+ | 'search.ai.toolCall.searching'
101
105
  | 'search.ai.button'
102
106
  | 'search.ai.label'
103
107
  | 'search.ai.disclaimer'
@@ -116,6 +120,7 @@ export type TranslationKey =
116
120
  | 'footer.copyrightText'
117
121
  | 'page.homeButton'
118
122
  | 'page.forbidden.title'
123
+ | 'page.forbidden.description'
119
124
  | 'page.notFound.title'
120
125
  | 'page.notFound.description'
121
126
  | 'page.lastUpdated.timeago'
@@ -412,7 +417,13 @@ export type TranslationKey =
412
417
  | 'select.noResults'
413
418
  | 'loaders.loading'
414
419
  | 'filter.dateRange.from'
415
- | 'filter.dateRange.to';
420
+ | 'filter.dateRange.to'
421
+ | 'mermaid.openFullscreen'
422
+ | 'mermaid.zoomIn'
423
+ | 'mermaid.zoomOut'
424
+ | 'mermaid.reset'
425
+ | 'mermaid.close'
426
+ | 'mermaid.viewer';
416
427
 
417
428
  export type Locale = { code: string; name: string };
418
429
 
@@ -104,10 +104,29 @@ export type SearchAiMessageResource = {
104
104
  title: string;
105
105
  };
106
106
 
107
+ export type ToolCall = {
108
+ name: ToolCallName;
109
+ args: unknown;
110
+ position: number;
111
+ result?: { documentCount: number };
112
+ };
113
+
114
+ export type ContentSegment = { type: 'text'; text: string } | { type: 'tool'; toolCall: ToolCall };
115
+
107
116
  export type AiSearchConversationItem = {
108
117
  role: AiSearchConversationRole;
109
118
  content: string;
110
119
  resources?: SearchAiMessageResource[];
111
120
  messageId?: string;
112
121
  feedback?: FeedbackType;
122
+ toolCalls?: ToolCall[];
123
+ contentSegments?: ContentSegment[];
113
124
  };
125
+
126
+ export enum ToolCallName {
127
+ SearchDocumentation = 'search_documentation',
128
+ ListApis = 'list-apis',
129
+ GetEndpoints = 'get-endpoints',
130
+ GetSecuritySchemes = 'get-security-schemes',
131
+ GetFullApiDescription = 'get-full-api-description',
132
+ }
@@ -0,0 +1,27 @@
1
+ import type { ToolCall, ContentSegment } from '../types/search';
2
+
3
+ export function splitContentByToolCalls(
4
+ content: string | undefined,
5
+ toolCalls: ToolCall[] | undefined,
6
+ ): ContentSegment[] {
7
+ if (!toolCalls || toolCalls.length === 0) {
8
+ return [{ type: 'text', text: content || '' }];
9
+ }
10
+
11
+ const segments: ContentSegment[] = [];
12
+ let lastPos = 0;
13
+
14
+ for (const toolCall of toolCalls) {
15
+ if (content && toolCall.position > lastPos) {
16
+ segments.push({ type: 'text', text: content.substring(lastPos, toolCall.position) });
17
+ }
18
+ segments.push({ type: 'tool', toolCall });
19
+ lastPos = toolCall.position;
20
+ }
21
+
22
+ if (content && lastPos < content.length) {
23
+ segments.push({ type: 'text', text: content.substring(lastPos) });
24
+ }
25
+
26
+ return segments;
27
+ }
@@ -43,3 +43,4 @@ export * from './get-operation-color';
43
43
  export * from './frontmatter-translate';
44
44
  export * from './transform-revisions-to-version-history';
45
45
  export * from './build-revision-url';
46
+ export * from './content-segments';
@@ -8,72 +8,6 @@ import { VERSION_NOT_SPECIFIED } from '@redocly/theme/core/constants';
8
8
 
9
9
  import { toLocalizedShortDate, toLocalizedShortDateTime } from './date';
10
10
 
11
- function compareVersionsDescending(versionA: string, versionB: string): number {
12
- return versionB.localeCompare(versionA, undefined, { numeric: true });
13
- }
14
-
15
- function extractGroupComparisonData(group: CatalogEntityVersionHistoryGroup): {
16
- version: string;
17
- isNotSpecified: boolean;
18
- time: number;
19
- hasCurrent: boolean;
20
- } {
21
- const version = group.version;
22
- const isNotSpecified = version === VERSION_NOT_SPECIFIED;
23
-
24
- // Get version dates (oldest revision date for each group - the version's creation date)
25
- // This ensures versions stay in their original order even when new revisions are added
26
- const date =
27
- group.singleRevisionDate ||
28
- (group.revisions && group.revisions.length > 0
29
- ? group.revisions[group.revisions.length - 1]?.revisionDate
30
- : undefined) ||
31
- '';
32
- const time = date ? new Date(date).getTime() : 0;
33
-
34
- const hasCurrent = group.hasCurrentRevisionFromBackend ?? false;
35
-
36
- return { version, isNotSpecified, time, hasCurrent };
37
- }
38
-
39
- function compareVersionGroupsDescending(
40
- groupA: CatalogEntityVersionHistoryGroup,
41
- groupB: CatalogEntityVersionHistoryGroup,
42
- ): number {
43
- const {
44
- version: versionA,
45
- isNotSpecified: isNotSpecifiedA,
46
- time: timeA,
47
- hasCurrent: hasCurrentA,
48
- } = extractGroupComparisonData(groupA);
49
- const {
50
- version: versionB,
51
- isNotSpecified: isNotSpecifiedB,
52
- time: timeB,
53
- hasCurrent: hasCurrentB,
54
- } = extractGroupComparisonData(groupB);
55
-
56
- // First, compare by version date (oldest first, so most recent versions come first)
57
- const dateComparison = timeB - timeA;
58
- if (dateComparison !== 0) {
59
- return dateComparison;
60
- }
61
-
62
- // If dates are equal, prioritize the one with isCurrent from backend
63
- if (hasCurrentA !== hasCurrentB) {
64
- return hasCurrentB ? 1 : -1; // hasCurrentB comes first if true
65
- }
66
-
67
- // If both have same isCurrent status, compare by version
68
- // If both are unknown or both are real versions, compare by version
69
- if (isNotSpecifiedA === isNotSpecifiedB) {
70
- return compareVersionsDescending(versionA, versionB);
71
- }
72
-
73
- // If one is unknown and one is a real version, unknown comes last
74
- return isNotSpecifiedA ? 1 : -1;
75
- }
76
-
77
11
  export type TransformRevisionsToVersionHistoryParams = {
78
12
  revisions: BffCatalogEntityRevision[];
79
13
  currentRevisionDate?: string;
@@ -100,13 +34,7 @@ export function transformRevisionsToVersionHistory({
100
34
  });
101
35
 
102
36
  const versionGroups = Array.from(versionMap.entries()).map(([version, versionRevisions]) => {
103
- const sortedRevisions = [...versionRevisions].sort((a, b) => {
104
- const dateA = a.revision ? new Date(a.revision).getTime() : 0;
105
- const dateB = b.revision ? new Date(b.revision).getTime() : 0;
106
- return dateB - dateA;
107
- });
108
-
109
- const latestRevision = sortedRevisions[0];
37
+ const latestRevision = versionRevisions[0];
110
38
 
111
39
  const versionMatches =
112
40
  normalizedCurrentVersion === undefined || normalizedCurrentVersion === version;
@@ -114,7 +42,7 @@ export function transformRevisionsToVersionHistory({
114
42
  let isCurrent: boolean;
115
43
  if (currentRevisionDate !== undefined) {
116
44
  // Check if revision matches AND version matches
117
- const revisionMatches = sortedRevisions.some((rev) => rev.revision === currentRevisionDate);
45
+ const revisionMatches = versionRevisions.some((rev) => rev.revision === currentRevisionDate);
118
46
  isCurrent = revisionMatches && versionMatches;
119
47
  } else {
120
48
  // When no revision is specified, use isCurrent flag from the latest revision
@@ -123,11 +51,11 @@ export function transformRevisionsToVersionHistory({
123
51
  }
124
52
 
125
53
  // Check if any revision in this version group is the default version
126
- const isDefaultVersion = sortedRevisions.some((rev) => rev.isDefaultVersion === true);
54
+ const isDefaultVersion = versionRevisions.some((rev) => rev.isDefaultVersion === true);
127
55
 
128
56
  const revisions =
129
- sortedRevisions.length > 1
130
- ? sortedRevisions.map((rev, index): CatalogEntityRevision => {
57
+ versionRevisions.length > 1
58
+ ? versionRevisions.map((rev, index): CatalogEntityRevision => {
131
59
  const revisionMatches = currentRevisionDate
132
60
  ? rev.revision === currentRevisionDate
133
61
  : false;
@@ -138,7 +66,7 @@ export function transformRevisionsToVersionHistory({
138
66
  index === 0 &&
139
67
  isCurrent;
140
68
  return {
141
- name: `r.${sortedRevisions.length - index}`,
69
+ name: `r.${versionRevisions.length - index}`,
142
70
  date: toLocalizedShortDateTime(rev.revision, locale),
143
71
  revisionDate: rev.revision,
144
72
  isActive: isActiveRevision || isCurrentByDefault,
@@ -155,9 +83,9 @@ export function transformRevisionsToVersionHistory({
155
83
  hasCurrentRevisionFromBackend: latestRevision?.isCurrent ?? false,
156
84
  isDefaultVersion,
157
85
  revisions,
158
- singleRevisionDate: sortedRevisions.length === 1 ? sortedRevisions[0]?.revision : undefined,
86
+ singleRevisionDate: versionRevisions.length === 1 ? versionRevisions[0]?.revision : undefined,
159
87
  };
160
88
  });
161
89
 
162
- return versionGroups.sort(compareVersionGroupsDescending);
90
+ return versionGroups;
163
91
  }
@@ -0,0 +1,14 @@
1
+ import { useCallback } from 'react';
2
+
3
+ import type { CatalogItem } from '@redocly/theme';
4
+
5
+ export function useProcessScorecard(): {
6
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
7
+ processScorecard: <T = any>(scorecard: T, api: CatalogItem) => T;
8
+ processInfo: <T>(info: T) => T;
9
+ } {
10
+ return {
11
+ processScorecard: useCallback((scorecard) => scorecard, []),
12
+ processInfo: useCallback((info) => info, []),
13
+ };
14
+ }
@@ -0,0 +1,26 @@
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 viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" {...props}>
10
+ <path d="M1 1H5V2.5H2.5V5H1V1Z" />
11
+ <path d="M15 1H11V2.5H13.5V5H15V1Z" />
12
+ <path d="M1 15H5V13.5H2.5V11H1V15Z" />
13
+ <path d="M15 15H11V13.5H13.5V11H15V15Z" />
14
+ </svg>
15
+ );
16
+
17
+ export const FitToViewIcon = styled(Icon).attrs(() => ({
18
+ 'data-component-name': 'icons/FitToViewIcon/FitToViewIcon',
19
+ }))<IconProps>`
20
+ path {
21
+ fill: ${({ color }) => getCssColorVariable(color)};
22
+ }
23
+
24
+ height: ${({ size }) => size || '16px'};
25
+ width: ${({ size }) => size || '16px'};
26
+ `;