@redocly/theme 0.67.0 → 0.68.0-next.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.
Files changed (64) hide show
  1. package/lib/components/Buttons/ConnectMCPButton.d.ts +1 -1
  2. package/lib/components/Buttons/ConnectMCPButton.js +50 -17
  3. package/lib/components/Buttons/variables.js +3 -0
  4. package/lib/components/Catalog/CatalogEntities.js +2 -11
  5. package/lib/components/Catalog/CatalogEntity/CatalogEntityRelations/CatalogEntityRelationsTable.js +3 -16
  6. package/lib/components/Catalog/CatalogEntity/CatalogEntitySchema.d.ts +1 -1
  7. package/lib/components/Catalog/CatalogEntity/CatalogEntitySchema.js +3 -9
  8. package/lib/components/Catalog/CatalogFilter/CatalogFilterCheckboxes.js +1 -7
  9. package/lib/components/Feedback/Feedback.js +4 -1
  10. package/lib/components/Feedback/Reasons.js +2 -3
  11. package/lib/components/Menu/MenuItem.js +2 -1
  12. package/lib/components/Search/SearchAiDialog.js +2 -1
  13. package/lib/components/Search/SearchAiResponse.js +2 -1
  14. package/lib/components/Search/SearchTrigger.js +3 -3
  15. package/lib/core/constants/common.d.ts +1 -0
  16. package/lib/core/constants/common.js +2 -1
  17. package/lib/core/constants/search.d.ts +1 -0
  18. package/lib/core/constants/search.js +2 -1
  19. package/lib/core/hooks/search/use-search-dialog.js +2 -1
  20. package/lib/core/hooks/use-active-heading.js +5 -2
  21. package/lib/core/hooks/use-telemetry-fallback.d.ts +1 -1
  22. package/lib/core/hooks/use-telemetry-fallback.js +1 -1
  23. package/lib/core/styles/dark.js +11 -0
  24. package/lib/core/styles/global.js +36 -34
  25. package/lib/markdoc/components/NumberedList/NumberedItem.d.ts +25 -0
  26. package/lib/markdoc/components/NumberedList/NumberedItem.js +217 -0
  27. package/lib/markdoc/components/NumberedList/NumberedList.d.ts +5 -0
  28. package/lib/markdoc/components/NumberedList/NumberedList.js +28 -0
  29. package/lib/markdoc/components/NumberedList/variables.d.ts +1 -0
  30. package/lib/markdoc/components/NumberedList/variables.js +42 -0
  31. package/lib/markdoc/components/default.d.ts +2 -0
  32. package/lib/markdoc/components/default.js +2 -0
  33. package/lib/markdoc/default.js +4 -0
  34. package/lib/markdoc/tags/numbered-item.d.ts +2 -0
  35. package/lib/markdoc/tags/numbered-item.js +35 -0
  36. package/lib/markdoc/tags/numbered-list.d.ts +2 -0
  37. package/lib/markdoc/tags/numbered-list.js +28 -0
  38. package/package.json +3 -3
  39. package/src/components/Buttons/ConnectMCPButton.tsx +110 -63
  40. package/src/components/Buttons/variables.ts +3 -0
  41. package/src/components/Catalog/CatalogEntities.tsx +7 -11
  42. package/src/components/Catalog/CatalogEntity/CatalogEntityRelations/CatalogEntityRelationsTable.tsx +8 -9
  43. package/src/components/Catalog/CatalogEntity/CatalogEntitySchema.tsx +6 -8
  44. package/src/components/Catalog/CatalogFilter/CatalogFilterCheckboxes.tsx +2 -4
  45. package/src/components/Feedback/Feedback.tsx +11 -1
  46. package/src/components/Feedback/Reasons.tsx +2 -4
  47. package/src/components/Menu/MenuItem.tsx +5 -2
  48. package/src/components/Search/SearchAiDialog.tsx +1 -1
  49. package/src/components/Search/SearchAiResponse.tsx +1 -1
  50. package/src/components/Search/SearchTrigger.tsx +3 -3
  51. package/src/core/constants/common.ts +2 -0
  52. package/src/core/constants/search.ts +2 -0
  53. package/src/core/hooks/search/use-search-dialog.ts +2 -1
  54. package/src/core/hooks/use-active-heading.ts +7 -2
  55. package/src/core/hooks/use-telemetry-fallback.ts +1 -1
  56. package/src/core/styles/dark.ts +12 -0
  57. package/src/core/styles/global.ts +2 -0
  58. package/src/markdoc/components/NumberedList/NumberedItem.tsx +274 -0
  59. package/src/markdoc/components/NumberedList/NumberedList.tsx +37 -0
  60. package/src/markdoc/components/NumberedList/variables.ts +39 -0
  61. package/src/markdoc/components/default.ts +2 -0
  62. package/src/markdoc/default.ts +4 -0
  63. package/src/markdoc/tags/numbered-item.ts +46 -0
  64. package/src/markdoc/tags/numbered-list.ts +34 -0
@@ -1,10 +1,9 @@
1
- import React, { useMemo } from 'react';
1
+ import React, { useState, useRef } from 'react';
2
2
  import styled from 'styled-components';
3
3
 
4
4
  import type { JSX } from 'react';
5
5
  import type { MCPOption } from '@redocly/theme/core/types';
6
6
 
7
- import { Dropdown } from '@redocly/theme/components/Dropdown/Dropdown';
8
7
  import { DropdownMenu } from '@redocly/theme/components/Dropdown/DropdownMenu';
9
8
  import { DropdownMenuItem } from '@redocly/theme/components/Dropdown/DropdownMenuItem';
10
9
  import { Button } from '@redocly/theme/components/Button/Button';
@@ -12,21 +11,9 @@ import { CursorIcon } from '@redocly/theme/icons/CursorIcon/CursorIcon';
12
11
  import { VSCodeIcon } from '@redocly/theme/icons/VSCodeIcon/VSCodeIcon';
13
12
  import { CopyIcon } from '@redocly/theme/icons/CopyIcon/CopyIcon';
14
13
  import { CheckmarkFilledIcon } from '@redocly/theme/icons/CheckmarkFilledIcon/CheckmarkFilledIcon';
15
- import { ConnectIcon } from '@redocly/theme/icons/ConnectIcon/ConnectIcon';
16
- import { useConnectMCPButton } from '@redocly/theme/core/hooks';
17
- import { useThemeHooks } from '@redocly/theme/core/hooks';
18
-
19
- type TriggerButtonProps = {
20
- text: string;
21
- };
22
-
23
- const TriggerButton = React.memo(({ text }: TriggerButtonProps): JSX.Element => {
24
- return (
25
- <StyledButton variant="secondary" icon={<ConnectIcon />}>
26
- {text}
27
- </StyledButton>
28
- );
29
- });
14
+ import { ChevronDownIcon } from '@redocly/theme/icons/ChevronDownIcon/ChevronDownIcon';
15
+ import { ChevronUpIcon } from '@redocly/theme/icons/ChevronUpIcon/ChevronUpIcon';
16
+ import { useConnectMCPButton, useOutsideClick, useThemeHooks } from '@redocly/theme/core/hooks';
30
17
 
31
18
  type MenuOption = {
32
19
  key: MCPOption;
@@ -74,57 +61,93 @@ export function ConnectMCPButton({
74
61
  placement = 'bottom',
75
62
  alignment = 'end',
76
63
  options = ['cursor', 'vscode', 'copy'],
77
- }: ConnectMCPButtonProps): JSX.Element {
64
+ }: ConnectMCPButtonProps): JSX.Element | null {
78
65
  const { useTranslate } = useThemeHooks();
79
66
  const { translate } = useTranslate();
67
+ const [isOpen, setIsOpen] = useState(false);
68
+ const wrapperRef = useRef<HTMLDivElement>(null);
80
69
 
81
- const { isCopied, triggerButtonText, visibleOptions, handleAction } = useConnectMCPButton({
82
- options,
83
- });
70
+ const { isCopied, visibleOptions, handleAction } = useConnectMCPButton({ options });
84
71
 
85
- const menuOptions = useMemo(
86
- () => MENU_OPTIONS.filter((option) => visibleOptions.includes(option.key)),
87
- [visibleOptions],
88
- );
72
+ useOutsideClick(wrapperRef, () => setIsOpen(false));
73
+
74
+ const menuOptions = MENU_OPTIONS.filter((option) => visibleOptions.includes(option.key));
75
+ const primaryOption = menuOptions[0];
76
+ const dropdownOptions = menuOptions.slice(1);
77
+
78
+ if (!primaryOption) return null;
79
+
80
+ const PrimaryIcon = primaryOption.icon;
81
+ // When copy is the primary action there is no dropdown item to show the copied state, so the
82
+ // primary button itself must reflect it.
83
+ const showPrimaryCopied = primaryOption.key === 'copy' && isCopied;
84
+
85
+ const handleMenuAction = (key: MCPOption) => {
86
+ handleAction(key);
87
+ setIsOpen(false);
88
+ };
89
89
 
90
90
  return (
91
- <ConnectMCPButtonWrapper data-component-name="Buttons/ConnectMCPButton">
92
- <Dropdown
93
- trigger={<TriggerButton text={triggerButtonText} />}
94
- triggerEvent="hover"
95
- placement={placement}
96
- alignment={alignment}
97
- closeOnClick={false}
98
- >
99
- <DropdownMenu>
100
- {menuOptions.map((option) => {
101
- const Icon = option.icon;
102
- const showCheckmark = option.key === 'copy' && isCopied;
103
-
104
- return (
105
- <DropdownMenuItem key={option.key} onAction={() => handleAction(option.key)}>
106
- <MenuItemContent>
107
- <MenuItemIcon>
108
- {showCheckmark ? (
109
- <CheckmarkFilledIcon color="var(--color-success-base)" />
110
- ) : (
111
- <Icon />
112
- )}
113
- </MenuItemIcon>
114
- <MenuItemText>
115
- <MenuItemTitle>
116
- {translate(option.titleTranslationKey, option.titleDefault)}
117
- </MenuItemTitle>
118
- <MenuItemDescription>
119
- {translate(option.descriptionTranslationKey, option.descriptionDefault)}
120
- </MenuItemDescription>
121
- </MenuItemText>
122
- </MenuItemContent>
123
- </DropdownMenuItem>
124
- );
125
- })}
126
- </DropdownMenu>
127
- </Dropdown>
91
+ <ConnectMCPButtonWrapper ref={wrapperRef} data-component-name="Buttons/ConnectMCPButton">
92
+ <SplitButtonGroup>
93
+ <PrimaryButton
94
+ variant="secondary"
95
+ icon={
96
+ showPrimaryCopied ? (
97
+ <CheckmarkFilledIcon color="var(--color-success-base)" />
98
+ ) : (
99
+ <PrimaryIcon />
100
+ )
101
+ }
102
+ onClick={() => handleAction(primaryOption.key)}
103
+ >
104
+ {translate(primaryOption.titleTranslationKey, primaryOption.titleDefault)}
105
+ </PrimaryButton>
106
+ {dropdownOptions.length > 0 && (
107
+ <ChevronButton
108
+ variant="secondary"
109
+ aria-label={translate('page.actions.connectMcp.moreOptions', 'More options')}
110
+ aria-expanded={isOpen}
111
+ aria-haspopup="menu"
112
+ onClick={() => setIsOpen((prev) => !prev)}
113
+ >
114
+ {isOpen ? <ChevronUpIcon /> : <ChevronDownIcon />}
115
+ </ChevronButton>
116
+ )}
117
+ </SplitButtonGroup>
118
+
119
+ {isOpen && dropdownOptions.length > 0 && (
120
+ <DropdownWrapper $placement={placement} $alignment={alignment}>
121
+ <DropdownMenu>
122
+ {dropdownOptions.map((option) => {
123
+ const Icon = option.icon;
124
+ const showCheckmark = option.key === 'copy' && isCopied;
125
+
126
+ return (
127
+ <DropdownMenuItem key={option.key} onAction={() => handleMenuAction(option.key)}>
128
+ <MenuItemContent>
129
+ <MenuItemIcon>
130
+ {showCheckmark ? (
131
+ <CheckmarkFilledIcon color="var(--color-success-base)" />
132
+ ) : (
133
+ <Icon />
134
+ )}
135
+ </MenuItemIcon>
136
+ <MenuItemText>
137
+ <MenuItemTitle>
138
+ {translate(option.titleTranslationKey, option.titleDefault)}
139
+ </MenuItemTitle>
140
+ <MenuItemDescription>
141
+ {translate(option.descriptionTranslationKey, option.descriptionDefault)}
142
+ </MenuItemDescription>
143
+ </MenuItemText>
144
+ </MenuItemContent>
145
+ </DropdownMenuItem>
146
+ );
147
+ })}
148
+ </DropdownMenu>
149
+ </DropdownWrapper>
150
+ )}
128
151
  </ConnectMCPButtonWrapper>
129
152
  );
130
153
  }
@@ -134,8 +157,32 @@ const ConnectMCPButtonWrapper = styled.div`
134
157
  position: relative;
135
158
  `;
136
159
 
137
- const StyledButton = styled(Button)`
160
+ const SplitButtonGroup = styled.div`
161
+ display: inline-flex;
162
+ align-items: stretch;
163
+ `;
164
+
165
+ const PrimaryButton = styled(Button)`
138
166
  --button-gap: var(--connect-mcp-button-gap);
167
+ border-top-right-radius: 0;
168
+ border-bottom-right-radius: 0;
169
+ `;
170
+
171
+ const ChevronButton = styled(Button)`
172
+ border-top-left-radius: 0;
173
+ border-bottom-left-radius: 0;
174
+ border-left: none;
175
+ padding-inline: var(--connect-mcp-button-chevron-padding-inline);
176
+ `;
177
+
178
+ const DropdownWrapper = styled.div<{ $placement?: string; $alignment?: string }>`
179
+ position: absolute;
180
+ top: ${({ $placement }) => ($placement === 'top' ? 'auto' : '100%')};
181
+ bottom: ${({ $placement }) => ($placement === 'top' ? '100%' : 'auto')};
182
+ left: ${({ $alignment }) => ($alignment === 'start' ? '0' : 'auto')};
183
+ right: ${({ $alignment }) => ($alignment === 'end' ? '0' : 'auto')};
184
+ padding-top: var(--dropdown-menu-padding-top);
185
+ z-index: var(--dropdown-menu-z-index);
139
186
  `;
140
187
 
141
188
  const MenuItemContent = styled.div`
@@ -58,6 +58,9 @@ export const connectMCPButton = css`
58
58
  /* Button gap */
59
59
  --connect-mcp-button-gap: var(--spacing-xs);
60
60
 
61
+ /* Chevron button */
62
+ --connect-mcp-button-chevron-padding-inline: var(--spacing-xs);
63
+
61
64
  /* Menu item layout */
62
65
  --connect-mcp-button-menu-item-gap: var(--spacing-sm);
63
66
  --connect-mcp-button-menu-item-padding-block: var(--spacing-xxs);
@@ -3,11 +3,8 @@ import React, { JSX, useEffect } from 'react';
3
3
  import type { CatalogEntityConfig } from '@redocly/config';
4
4
 
5
5
  import { BffCatalogEntity, BffCatalogEntityList } from '@redocly/theme/core/types';
6
- import {
7
- useThemeHooks,
8
- useCatalogEntities,
9
- useCatalogEntityDetails,
10
- } from '@redocly/theme/core/hooks';
6
+ import { useThemeHooks, useCatalogEntities } from '@redocly/theme/core/hooks';
7
+ import { getBaseDataAttributes } from '@redocly/theme/core/utils';
11
8
  import { ArrowDownIcon } from '@redocly/theme/icons/ArrowDownIcon/ArrowDownIcon';
12
9
  import { CatalogCardView } from '@redocly/theme/components/Catalog/CatalogCardView/CatalogCardView';
13
10
  import { CatalogTableView } from '@redocly/theme/components/Catalog/CatalogTableView/CatalogTableView';
@@ -45,9 +42,6 @@ export function CatalogEntities(props: CatalogEntitiesProps): JSX.Element {
45
42
  isColumnSorted,
46
43
  } = props;
47
44
 
48
- const { getEntityDetailsLink } = useCatalogEntityDetails({
49
- catalogConfig,
50
- });
51
45
  const { useTelemetry, useFetchCatalogEntities } = useThemeHooks();
52
46
  const telemetry = useTelemetry();
53
47
  const { initialFilter } = useCatalogEntities({
@@ -76,10 +70,12 @@ export function CatalogEntities(props: CatalogEntitiesProps): JSX.Element {
76
70
  if (searchQuery) {
77
71
  telemetry.sendCatalogEntitiesListSearchResultClickedMessage([
78
72
  {
79
- id: entity.id,
80
- object: 'catalog_entity',
81
- uri: getEntityDetailsLink(entity),
73
+ ...getBaseDataAttributes(
74
+ 'catalogEntitiesListSearchResultId',
75
+ 'catalogEntitiesListSearchResult',
76
+ ),
82
77
  query: searchQuery,
78
+ entityId: entity.id,
83
79
  entityKey: entity.key,
84
80
  entityType: entity.type,
85
81
  },
@@ -8,7 +8,8 @@ import type { BffCatalogEntity, BffCatalogRelatedEntity } from '@redocly/theme/c
8
8
  import { CatalogColumn } from '@redocly/theme/components/Catalog/CatalogTableView/CatalogTableView';
9
9
  import { CatalogActionsRow } from '@redocly/theme/components/Catalog/CatalogActionsRow';
10
10
  import { CatalogEntityRelationsTableContent } from '@redocly/theme/components/Catalog/CatalogEntity/CatalogEntityRelations/CatalogEntityRelationsTableContent';
11
- import { useCatalogEntityDetails, useThemeHooks } from '@redocly/theme/core/hooks';
11
+ import { useThemeHooks } from '@redocly/theme/core/hooks';
12
+ import { getBaseDataAttributes } from '@redocly/theme/core/utils';
12
13
  import { reverseRelationMap } from '@redocly/theme/core/constants';
13
14
 
14
15
  export type CatalogEntityRelationsTableProps = {
@@ -51,22 +52,20 @@ export function CatalogEntityRelationsTable({
51
52
  }: CatalogEntityRelationsTableProps): JSX.Element {
52
53
  const { useTelemetry } = useThemeHooks();
53
54
  const telemetry = useTelemetry();
54
- const { getEntityDetailsLink } = useCatalogEntityDetails({
55
- catalogConfig,
56
- entitiesCatalogConfig,
57
- });
58
55
 
59
56
  const onRowClick = (relatedEntity: BffCatalogRelatedEntity) => {
60
57
  if (searchQuery) {
61
- telemetry.sendCatalogEntitiesRelatedEntitiesListSearchResultClickMessage([
58
+ telemetry.sendCatalogEntitiesRelatedEntitiesListSearchResultClickedMessage([
62
59
  {
63
- id: relatedEntity.id,
64
- object: 'catalog_entity',
65
- uri: getEntityDetailsLink(relatedEntity),
60
+ ...getBaseDataAttributes(
61
+ 'catalogEntitiesRelatedEntitiesListSearchResultId',
62
+ 'catalogEntitiesRelatedEntitiesListSearchResult',
63
+ ),
66
64
  query: searchQuery,
67
65
  entityKey: entity.key,
68
66
  entityType: entity.type,
69
67
  relationType: reverseRelationMap[relatedEntity.relationType],
68
+ relatedEntityId: relatedEntity.id,
70
69
  relatedEntityKey: relatedEntity.key,
71
70
  relatedEntityType: relatedEntity.type,
72
71
  },
@@ -5,8 +5,9 @@ import * as Sampler from 'openapi-sampler';
5
5
  import type { CatalogEntityConfig } from '@redocly/config';
6
6
  import type { BffCatalogEntity, BffCatalogRelatedEntity } from '@redocly/theme/core/types';
7
7
 
8
- import { useCatalogEntityDetails, useThemeHooks } from '@redocly/theme/core/hooks';
8
+ import { useThemeHooks } from '@redocly/theme/core/hooks';
9
9
  import { useCatalogEntitySchema } from '@redocly/theme/core/hooks';
10
+ import { getBaseDataAttributes } from '@redocly/theme/core/utils';
10
11
  import { JsonViewer } from '@redocly/theme/components/JsonViewer/JsonViewer';
11
12
  import { CopyButton } from '@redocly/theme/components/Buttons/CopyButton';
12
13
 
@@ -24,7 +25,6 @@ export type CatalogEntitySchemaProps = {
24
25
  export function CatalogEntitySchema({
25
26
  entity,
26
27
  relatedEntity,
27
- catalogConfig,
28
28
  RedocSchema,
29
29
  StoreProvider,
30
30
  GraphqlTypeRenderer,
@@ -35,9 +35,6 @@ export function CatalogEntitySchema({
35
35
  const { definition, parsedSchema, rawSchema } = useCatalogEntitySchema({ entity, relatedEntity });
36
36
  const isGraphql = entity.metadata?.specType === 'graphql';
37
37
  const graphqlSDL = entity?.metadata?.sdl;
38
- const { getEntityDetailsLink } = useCatalogEntityDetails({
39
- catalogConfig,
40
- });
41
38
 
42
39
  return (
43
40
  <MetadataWrapper data-component-name="Catalog/CatalogEntity/CatalogEntitySchema">
@@ -54,9 +51,10 @@ export function CatalogEntitySchema({
54
51
  onCopyClick={() =>
55
52
  telemetry.sendCatalogEntitiesCopyDataSchemaClickedMessage([
56
53
  {
57
- id: entity.id,
58
- object: 'catalog_entity',
59
- uri: getEntityDetailsLink(entity),
54
+ ...getBaseDataAttributes('catalogEntitiesCopyDataSchemaId', 'button'),
55
+ entityId: entity.id,
56
+ entityKey: entity.key,
57
+ entityType: entity.type,
60
58
  },
61
59
  ])
62
60
  }
@@ -12,7 +12,7 @@ import { FilterInput } from '@redocly/theme/components/Filter/FilterInput';
12
12
  import { useThemeHooks } from '@redocly/theme/core/hooks';
13
13
  import { CheckboxIcon } from '@redocly/theme/icons/CheckboxIcon/CheckboxIcon';
14
14
  import { CounterTag } from '@redocly/theme/components/Tags/CounterTag';
15
- import { changeTextCasing } from '@redocly/theme/core/utils';
15
+ import { changeTextCasing, getBaseDataAttributes } from '@redocly/theme/core/utils';
16
16
  import { Accordion } from '@redocly/theme/components/Accordion/Accordion';
17
17
 
18
18
  export type CatalogFilterCheckboxesProps = FilterProps & {
@@ -104,9 +104,7 @@ export function CatalogFilterCheckboxes({
104
104
  filter.toggleOption(value);
105
105
  telemetry.sendCatalogEntitiesFilterCheckboxToggledMessage([
106
106
  {
107
- id: filter.property + '--' + value,
108
- object: 'checkbox',
109
- uri: window.location.href,
107
+ ...getBaseDataAttributes('catalogEntitiesFilterCheckboxId', 'checkbox'),
110
108
  category: filter.property,
111
109
  value: value,
112
110
  },
@@ -11,6 +11,7 @@ import { Comment } from '@redocly/theme/components/Feedback/Comment';
11
11
  import { Mood } from '@redocly/theme/components/Feedback/Mood';
12
12
  import { Scale } from '@redocly/theme/components/Feedback/Scale';
13
13
  import { DEFAULT_FEEDBACK_TYPE } from '@redocly/theme/core/constants';
14
+ import { getBaseDataAttributes } from '@redocly/theme/core/utils';
14
15
 
15
16
  const feedbackComponents = {
16
17
  rating: Rating,
@@ -58,7 +59,16 @@ export function Feedback(props: FeedbackProps & { path?: string }) {
58
59
  settings={settings}
59
60
  onSubmit={(values) => {
60
61
  submitFeedback({ type, values, path });
61
- telemetry.sendFeedbackMessage({ feedbackType: type });
62
+ telemetry.sendFeedbackMessage([
63
+ {
64
+ ...getBaseDataAttributes<
65
+ 'feedbackFormId',
66
+ 'feedbackForm',
67
+ 'urn:redocly:realm:ui:feedbackForm:feedbackFormId'
68
+ >('feedbackFormId', 'feedbackForm'),
69
+ feedbackType: type,
70
+ },
71
+ ]);
62
72
  }}
63
73
  />
64
74
  </FeedbackWrapper>
@@ -16,7 +16,7 @@ export type ReasonsProps = {
16
16
  };
17
17
 
18
18
  export function Reasons({ settings, onChange, className }: ReasonsProps): JSX.Element | null {
19
- const { label, component, items = [] } = settings;
19
+ const { label, component = 'checkbox', items = [] } = settings;
20
20
  const [checkedState, setCheckedState] = React.useState(new Array(items.length).fill(false));
21
21
  const { useTranslate } = useThemeHooks();
22
22
  const { translate } = useTranslate();
@@ -25,8 +25,6 @@ export function Reasons({ settings, onChange, className }: ReasonsProps): JSX.El
25
25
  return null;
26
26
  }
27
27
 
28
- const input_type = component || 'checkbox';
29
-
30
28
  const handleOptionChange = (position: number) => {
31
29
  const updatedCheckedState =
32
30
  component === 'checkbox'
@@ -53,7 +51,7 @@ export function Reasons({ settings, onChange, className }: ReasonsProps): JSX.El
53
51
  {items.map((reason, idx) => (
54
52
  <OptionWrapper key={reason}>
55
53
  <input
56
- type={input_type}
54
+ type={component}
57
55
  value={reason}
58
56
  checked={checkedState[idx]}
59
57
  name="reasons"
@@ -10,7 +10,7 @@ import { Link } from '@redocly/theme/components/Link/Link';
10
10
  import { ChevronDownIcon } from '@redocly/theme/icons/ChevronDownIcon/ChevronDownIcon';
11
11
  import { ChevronRightIcon } from '@redocly/theme/icons/ChevronRightIcon/ChevronRightIcon';
12
12
  import { HttpTag } from '@redocly/theme/components/Tags/HttpTag';
13
- import { MenuItemType } from '@redocly/theme/core/constants';
13
+ import { DEFAULT_LINE_POSITION, MenuItemType } from '@redocly/theme/core/constants';
14
14
  import {
15
15
  getBaseDataAttributes,
16
16
  getMenuItemType,
@@ -178,7 +178,10 @@ export function MenuItem(props: React.PropsWithChildren<MenuItemProps>): JSX.Ele
178
178
  </MenuItemNestedWrapper>
179
179
  ) : null}
180
180
  {item.separatorLine ? (
181
- <MenuItemSeparatorLine $depth={depth} $linePosition={item.linePosition} />
181
+ <MenuItemSeparatorLine
182
+ $depth={depth}
183
+ $linePosition={item.linePosition ?? DEFAULT_LINE_POSITION}
184
+ />
182
185
  ) : null}
183
186
  </MenuItemWrapper>
184
187
  );
@@ -59,7 +59,7 @@ export function SearchAiDialog({
59
59
 
60
60
  const conversationEndRef = React.useRef<HTMLDivElement>(null);
61
61
 
62
- const suggestions = aiAssistant?.suggestions;
62
+ const suggestions = aiAssistant?.suggestions ?? [];
63
63
 
64
64
  const placeholder = isGeneratingResponse
65
65
  ? translate('search.ai.generatingResponse', 'Generating response...')
@@ -42,7 +42,7 @@ export function SearchAiResponse(props: SearchAiResponseProps): JSX.Element {
42
42
 
43
43
  let responseContainer = null;
44
44
 
45
- const suggestions = aiAssistant?.suggestions;
45
+ const suggestions = aiAssistant?.suggestions ?? [];
46
46
 
47
47
  const hasPendingOrReceivedResponse = response || isGeneratingResponse || error;
48
48
  if (hasPendingOrReceivedResponse) {
@@ -7,6 +7,7 @@ import { useThemeConfig, useThemeHooks } from '@redocly/theme/core/hooks';
7
7
  import { Button } from '@redocly/theme/components/Button/Button';
8
8
  import { SearchIcon } from '@redocly/theme/icons/SearchIcon/SearchIcon';
9
9
  import { breakpoints, getUserAgent } from '@redocly/theme/core/utils';
10
+ import { DEFAULT_KEYBOARD_SHORTCUTS } from '@redocly/theme/core/constants';
10
11
 
11
12
  export type SearchTriggerProps = {
12
13
  onClick: () => void;
@@ -18,8 +19,7 @@ export function SearchTrigger({ onClick, className }: SearchTriggerProps): JSX.E
18
19
  const { useTranslate } = useThemeHooks();
19
20
  const { translate } = useTranslate();
20
21
 
21
- const defaultKeyShortcut = '⌘+K,CTRL+K';
22
- const keyShortcuts = themeSettings?.search?.shortcuts ?? [defaultKeyShortcut];
22
+ const keyShortcuts = themeSettings?.search?.shortcuts ?? [DEFAULT_KEYBOARD_SHORTCUTS];
23
23
  let mainShortcutKey: string | null | undefined = null;
24
24
 
25
25
  if (keyShortcuts) {
@@ -52,7 +52,7 @@ export function SearchTrigger({ onClick, className }: SearchTriggerProps): JSX.E
52
52
  >
53
53
  <SearchIcon />
54
54
  {translate('search.navbar.label', 'Search')}
55
- {mainShortcutKey === defaultKeyShortcut ? (
55
+ {mainShortcutKey === DEFAULT_KEYBOARD_SHORTCUTS ? (
56
56
  <span>{isMac ? '⌘K' : 'Ctrl+K'}</span>
57
57
  ) : (
58
58
  <span>{mainShortcutKey}</span>
@@ -35,3 +35,5 @@ export const DEFAULT_COLOR_MODES = {
35
35
  LIGHT: 'light',
36
36
  DARK: 'dark',
37
37
  } as const;
38
+
39
+ export const DEFAULT_LINE_POSITION = 'top';
@@ -46,6 +46,8 @@ export const AI_SEARCH_MAX_MESSAGE_LENGTH = 45000;
46
46
 
47
47
  export const SEARCH_DEBOUNCE_TIME_MS = 300;
48
48
 
49
+ export const DEFAULT_KEYBOARD_SHORTCUTS = '⌘+K,CTRL+K';
50
+
49
51
  export const TOOL_CALL_DISPLAY_TEXT: Partial<
50
52
  Record<string, { inProgressText: string; completedText: string }>
51
53
  > = {
@@ -6,6 +6,7 @@ import { useThemeHooks } from '../use-theme-hooks';
6
6
  import { useThemeConfig } from '../use-theme-config';
7
7
  import { useSearchSession } from '../../contexts';
8
8
  import { getBaseDataAttributes } from '../../utils';
9
+ import { DEFAULT_KEYBOARD_SHORTCUTS } from '../../constants';
9
10
 
10
11
  export function useSearchDialog() {
11
12
  const [isOpen, setIsOpen] = useState(false);
@@ -14,7 +15,7 @@ export function useSearchDialog() {
14
15
  const { useTelemetry } = useThemeHooks();
15
16
  const telemetry = useTelemetry();
16
17
  const { refreshSearchSessionId } = useSearchSession();
17
- const keyShortcuts = themeSettings?.search?.shortcuts ?? ['⌘+K,CTRL+K'];
18
+ const keyShortcuts = themeSettings?.search?.shortcuts ?? [DEFAULT_KEYBOARD_SHORTCUTS];
18
19
  const hotKeys = keyShortcuts?.join(',');
19
20
 
20
21
  useEffect(() => {
@@ -64,8 +64,13 @@ export function useActiveHeading(
64
64
  const headers = Array.from(allHeaders);
65
65
 
66
66
  if (displayedHeadingsRef.current && displayedHeadingsRef.current.length > 0) {
67
- const displayedIds = displayedHeadingsRef.current.map((h) => h?.id).filter(Boolean);
68
- return headers.filter((header) => displayedIds.includes(header.id));
67
+ // Resolve targets by id so anchors that are not headings (e.g. numbered
68
+ // list items with a lifted headline) can be scrolled to and tracked.
69
+ return displayedHeadingsRef.current
70
+ .map((heading) =>
71
+ heading?.id ? allContent.querySelector<HTMLElement>(`[id="${heading.id}"]`) : null,
72
+ )
73
+ .filter((element): element is HTMLElement => element !== null);
69
74
  }
70
75
 
71
76
  return headers;
@@ -8,7 +8,7 @@ export const useTelemetryFallback = () => ({
8
8
  sendCatalogEntitiesCopyDataSchemaClickedMessage: () => {},
9
9
  sendCatalogEntitiesViewModeChangedMessage: () => {},
10
10
  sendCatalogEntitiesRelatedEntitiesListSearchQueryMessage: () => {},
11
- sendCatalogEntitiesRelatedEntitiesListSearchResultClickMessage: () => {},
11
+ sendCatalogEntitiesRelatedEntitiesListSearchResultClickedMessage: () => {},
12
12
  sendCatalogEntitiesListSearchQueryMessage: () => {},
13
13
  sendCatalogEntitiesListSearchResultClickedMessage: () => {},
14
14
  sendRedirectMessage: () => {},
@@ -60,6 +60,17 @@ const badgesDarkMode = css`
60
60
  // @tokens End
61
61
  `
62
62
 
63
+ const numberedListDarkMode = css`
64
+ /**
65
+ * @tokens Markdown Numbered List
66
+ */
67
+
68
+ --md-numbered-list-marker-bg-color: var(--color-warm-grey-3); // @presenter Color
69
+ --md-numbered-list-marker-bg-color-hover: var(--color-warm-grey-4); // @presenter Color
70
+
71
+ // @tokens End
72
+ `
73
+
63
74
 
64
75
  export const darkMode = css`
65
76
  /* === Dark Theme Colors === */
@@ -352,6 +363,7 @@ export const darkMode = css`
352
363
  ${bannerDarkMode}
353
364
  ${admonitionDarkMode}
354
365
  ${badgesDarkMode}
366
+ ${numberedListDarkMode}
355
367
  ${activeBrandPaletteDark}
356
368
  ${codeBlockDarkMode}
357
369
  /**
@@ -29,6 +29,7 @@ import { productPicker } from '@redocly/theme/components/Product/variables';
29
29
  import { markdown } from '@redocly/theme/components/Markdown/variables';
30
30
  import { banner } from '@redocly/theme/components/Banner/variables';
31
31
  import { markdownTabs } from '@redocly/theme/markdoc/components/Tabs/variables';
32
+ import { markdownNumberedList } from '@redocly/theme/markdoc/components/NumberedList/variables';
32
33
  import { diagram } from '@redocly/theme/markdoc/components/Diagram/variables';
33
34
  import { lastUpdated } from '@redocly/theme/components/LastUpdated/variables';
34
35
  import { logo } from '@redocly/theme/components/Logo/variables';
@@ -1344,6 +1345,7 @@ export const styles = css<{ palette?: string }>`
1344
1345
  ${logo}
1345
1346
  ${markdown}
1346
1347
  ${markdownTabs}
1348
+ ${markdownNumberedList}
1347
1349
  ${diagram}
1348
1350
  ${menu}
1349
1351
  ${mobileMenu}