@redocly/theme 0.67.0-next.3 → 0.67.0-next.4

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.
@@ -9,6 +9,7 @@ type BreadcrumbDropdownProps = {
9
9
  })[];
10
10
  onItemClick?: (item: BreadcrumbItem, index: number) => void;
11
11
  className?: string;
12
+ withChevron?: boolean;
12
13
  };
13
- export declare function BreadcrumbDropdown({ children, label, items, onItemClick, className }: BreadcrumbDropdownProps): JSX.Element | null;
14
+ export declare function BreadcrumbDropdown({ children, label, items, onItemClick, className, withChevron }: BreadcrumbDropdownProps): JSX.Element | null;
14
15
  export {};
@@ -47,19 +47,22 @@ const Tooltip_1 = require("../../components/Tooltip/Tooltip");
47
47
  const GenericIcon_1 = require("../../icons/GenericIcon/GenericIcon");
48
48
  const constants_1 = require("../../core/constants");
49
49
  const BreadcrumbIcon_1 = require("../../components/Breadcrumbs/BreadcrumbIcon");
50
- function BreadcrumbDropdown({ children, label, items, onItemClick, className, }) {
50
+ function BreadcrumbDropdown({ children, label, items, onItemClick, className, withChevron, }) {
51
51
  const { useTelemetry, useTranslate } = (0, hooks_1.useThemeHooks)();
52
52
  const telemetry = useTelemetry();
53
53
  const { translate } = useTranslate();
54
+ const [isOpen, setIsOpen] = react_1.default.useState(false);
54
55
  if (!items || items.length === 0) {
55
56
  return null;
56
57
  }
57
58
  const isTruncated = label.length > constants_1.BREADCRUMB_MAX_LENGTH;
58
59
  const triggerContent = isTruncated ? (react_1.default.createElement(Tooltip_1.Tooltip, { tip: label, placement: "bottom" },
59
60
  react_1.default.createElement(TriggerContentWrapper, null, children))) : (children);
60
- const trigger = react_1.default.createElement(StyledDropdownTrigger, null, triggerContent);
61
+ const trigger = (react_1.default.createElement(StyledDropdownTrigger, null,
62
+ triggerContent,
63
+ withChevron && react_1.default.createElement(GenericIcon_1.GenericIcon, { icon: isOpen ? 'chevron-up' : 'chevron-down' })));
61
64
  return (react_1.default.createElement(BreadcrumbDropdownWrapper, { "data-component-name": "Breadcrumbs/BreadcrumbDropdown", className: className, "data-testid": "breadcrumb-dropdown" },
62
- react_1.default.createElement(Dropdown_1.Dropdown, { trigger: trigger, closeOnClick: true },
65
+ react_1.default.createElement(Dropdown_1.Dropdown, { trigger: trigger, closeOnClick: true, onOpen: () => setIsOpen(true), onClose: () => setIsOpen(false) },
63
66
  react_1.default.createElement(DropdownMenu_1.DropdownMenu, null, items.map((item, index) => {
64
67
  const isActive = Boolean(item === null || item === void 0 ? void 0 : item.isActive);
65
68
  const hasLink = Boolean(item.link);
@@ -101,7 +104,7 @@ const StyledDropdownTrigger = styled_components_1.default.button `
101
104
  background-color: var(--breadcrumbs-background-color-hover);
102
105
  }
103
106
 
104
- &:focus {
107
+ &:focus-visible {
105
108
  box-shadow: var(--breadcrumbs-box-shadow-focus);
106
109
  outline: none;
107
110
  }
@@ -12,7 +12,6 @@ const BreadcrumbDropdown_1 = require("../../components/Breadcrumbs/BreadcrumbDro
12
12
  const BreadcrumbIcon_1 = require("../../components/Breadcrumbs/BreadcrumbIcon");
13
13
  const utils_1 = require("../../core/utils");
14
14
  const constants_1 = require("../../core/constants");
15
- const GenericIcon_1 = require("../../icons/GenericIcon/GenericIcon");
16
15
  function Breadcrumbs(props) {
17
16
  const { useBreadcrumbs, useTelemetry, useTranslate } = (0, hooks_1.useThemeHooks)();
18
17
  const { breadcrumbs: fileBreadcrumbs, currentItemSiblings } = useBreadcrumbs();
@@ -46,7 +45,7 @@ function Breadcrumbs(props) {
46
45
  ...currentItemSiblings,
47
46
  ];
48
47
  const translatedLabel = translate(breadcrumb.labelTranslationKey, breadcrumb.label);
49
- return (react_1.default.createElement(BreadcrumbDropdown_1.BreadcrumbDropdown, { label: translatedLabel, items: siblingsWithActive, onItemClick: (item, itemIdx) => telemetry.sendBreadcrumbClickedMessage([
48
+ return (react_1.default.createElement(BreadcrumbDropdown_1.BreadcrumbDropdown, { label: translatedLabel, items: siblingsWithActive, withChevron: true, onItemClick: (item, itemIdx) => telemetry.sendBreadcrumbClickedMessage([
50
49
  {
51
50
  object: 'breadcrumb',
52
51
  link: item.link,
@@ -55,8 +54,7 @@ function Breadcrumbs(props) {
55
54
  },
56
55
  ]) },
57
56
  react_1.default.createElement(BreadcrumbIcon_1.BreadcrumbIcon, { icon: breadcrumb.icon }),
58
- (0, utils_1.trimText)(translatedLabel, constants_1.BREADCRUMB_MAX_LENGTH),
59
- react_1.default.createElement(GenericIcon_1.GenericIcon, { icon: "chevron-down" })));
57
+ (0, utils_1.trimText)(translatedLabel, constants_1.BREADCRUMB_MAX_LENGTH)));
60
58
  }
61
59
  return (react_1.default.createElement(Breadcrumb_1.Breadcrumb, { link: breadcrumb.link, label: translate(breadcrumb.labelTranslationKey, breadcrumb.label), isActive: isActive, icon: breadcrumb.icon, onClick: () => telemetry.sendBreadcrumbClickedMessage([
62
60
  {
@@ -12,6 +12,7 @@ export type DropdownProps = PropsWithChildren<{
12
12
  withArrow?: boolean;
13
13
  onClick?: (event: React.UIEvent) => void;
14
14
  onClose?: () => void;
15
+ onOpen?: () => void;
15
16
  }>;
16
17
  export declare const Dropdown: React.ForwardRefExoticComponent<{
17
18
  trigger: React.ReactNode;
@@ -25,6 +26,7 @@ export declare const Dropdown: React.ForwardRefExoticComponent<{
25
26
  withArrow?: boolean;
26
27
  onClick?: (event: React.UIEvent) => void;
27
28
  onClose?: () => void;
29
+ onOpen?: () => void;
28
30
  } & {
29
31
  children?: React.ReactNode | undefined;
30
32
  } & React.RefAttributes<HTMLDivElement>>;
@@ -42,11 +42,12 @@ const styled_components_1 = __importDefault(require("styled-components"));
42
42
  const hooks_1 = require("../../core/hooks");
43
43
  const ChevronDownIcon_1 = require("../../icons/ChevronDownIcon/ChevronDownIcon");
44
44
  const ChevronUpIcon_1 = require("../../icons/ChevronUpIcon/ChevronUpIcon");
45
- exports.Dropdown = (0, react_1.forwardRef)(({ children, className, active, trigger, triggerEvent = 'click', closeOnClick = true, withArrow, dataAttributes, placement, alignment, onClick, onClose, }, ref) => {
45
+ exports.Dropdown = (0, react_1.forwardRef)(({ children, className, active, trigger, triggerEvent = 'click', closeOnClick = true, withArrow, dataAttributes, placement, alignment, onClick, onClose, onOpen, }, ref) => {
46
46
  const dropdownRef = (0, react_1.useRef)(null);
47
47
  const [isOpen, setIsOpen] = (0, hooks_1.useControlledState)(false, active);
48
48
  const handleOpen = () => {
49
49
  setIsOpen(true);
50
+ onOpen === null || onOpen === void 0 ? void 0 : onOpen();
50
51
  };
51
52
  const handleClose = () => {
52
53
  setIsOpen(false);
@@ -58,7 +59,12 @@ exports.Dropdown = (0, react_1.forwardRef)(({ children, className, active, trigg
58
59
  const handleToggle = (event) => {
59
60
  event.stopPropagation();
60
61
  event.preventDefault();
61
- setIsOpen(!isOpen);
62
+ if (isOpen) {
63
+ handleClose();
64
+ }
65
+ else {
66
+ handleOpen();
67
+ }
62
68
  };
63
69
  const handleKeyDown = (event) => {
64
70
  if (event.key === 'Enter' || event.key === ' ') {
@@ -41,6 +41,7 @@ const react_1 = __importStar(require("react"));
41
41
  const styled_components_1 = __importDefault(require("styled-components"));
42
42
  const types_1 = require("../../core/types");
43
43
  const constants_1 = require("../../core/constants");
44
+ const types_2 = require("../../core/types");
44
45
  const Link_1 = require("../../components/Link/Link");
45
46
  const Tag_1 = require("../../components/Tag/Tag");
46
47
  const constants_2 = require("../../core/constants");
@@ -56,11 +57,28 @@ function MarkdownSegment({ text }) {
56
57
  const markdown = useMarkdownText(text);
57
58
  return react_1.default.createElement(ResponseText, { as: "div", children: markdown, "data-testid": "response-text" });
58
59
  }
59
- function getToolCallDisplayText(toolName) {
60
+ // The codemode `execute` tool passes a human-readable `description` of what its code
61
+ // does; show that instead of a generic "Executing execute..." label.
62
+ function getExecuteDescription(args) {
63
+ if (args && typeof args === 'object' && 'description' in args) {
64
+ const { description } = args;
65
+ if (typeof description === 'string' && description.trim().length > 0) {
66
+ return description.trim();
67
+ }
68
+ }
69
+ return undefined;
70
+ }
71
+ function getToolCallDisplayText(toolCall) {
60
72
  var _a;
61
- return ((_a = constants_1.TOOL_CALL_DISPLAY_TEXT[toolName]) !== null && _a !== void 0 ? _a : {
62
- inProgressText: `Executing ${toolName}...`,
63
- completedText: `${toolName} executed`,
73
+ if (toolCall.name === types_2.ToolCallName.Execute) {
74
+ const description = getExecuteDescription(toolCall.args);
75
+ if (description) {
76
+ return { inProgressText: description, completedText: description };
77
+ }
78
+ }
79
+ return ((_a = constants_1.TOOL_CALL_DISPLAY_TEXT[toolCall.name]) !== null && _a !== void 0 ? _a : {
80
+ inProgressText: `Executing ${toolCall.name}...`,
81
+ completedText: `${toolCall.name} executed`,
64
82
  });
65
83
  }
66
84
  function SearchAiMessageComponent({ role, content, isThinking, resources, className, messageId, feedback, onFeedbackChange, toolCalls = [], contentSegments = [{ type: 'text', text: content }], }) {
@@ -111,7 +129,7 @@ function SearchAiMessageComponent({ role, content, isThinking, resources, classN
111
129
  contentSegments.map((segment, index) => {
112
130
  if (segment.type === 'tool') {
113
131
  const toolCallCompleted = Boolean(segment.toolCall.result);
114
- const { inProgressText, completedText } = getToolCallDisplayText(segment.toolCall.name);
132
+ const { inProgressText, completedText } = getToolCallDisplayText(segment.toolCall);
115
133
  const toolCallDisplayText = toolCallCompleted ? completedText : inProgressText;
116
134
  return (react_1.default.createElement(ToolCallsInfoWrapper, { key: `tool-${index}`, "data-testid": "tool-calls-info" },
117
135
  react_1.default.createElement(ToolCallInfoItem, null,
@@ -124,5 +124,6 @@ export declare enum ToolCallName {
124
124
  GetEndpoints = "get-endpoints",
125
125
  GetEndpointInfo = "get-endpoint-info",
126
126
  GetSecuritySchemes = "get-security-schemes",
127
- GetFullApiDescription = "get-full-api-description"
127
+ GetFullApiDescription = "get-full-api-description",
128
+ Execute = "execute"
128
129
  }
@@ -14,5 +14,6 @@ var ToolCallName;
14
14
  ToolCallName["GetEndpointInfo"] = "get-endpoint-info";
15
15
  ToolCallName["GetSecuritySchemes"] = "get-security-schemes";
16
16
  ToolCallName["GetFullApiDescription"] = "get-full-api-description";
17
+ ToolCallName["Execute"] = "execute";
17
18
  })(ToolCallName || (exports.ToolCallName = ToolCallName = {}));
18
19
  //# sourceMappingURL=search.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@redocly/theme",
3
- "version": "0.67.0-next.3",
3
+ "version": "0.67.0-next.4",
4
4
  "description": "Shared UI components lib",
5
5
  "keywords": [
6
6
  "theme",
@@ -63,7 +63,7 @@
63
63
  "vitest": "4.1.8",
64
64
  "vitest-when": "0.6.2",
65
65
  "webpack": "5.105.2",
66
- "@redocly/realm-asyncapi-sdk": "0.13.0-next.2"
66
+ "@redocly/realm-asyncapi-sdk": "0.13.0-next.3"
67
67
  },
68
68
  "dependencies": {
69
69
  "@tanstack/react-query": "5.62.3",
@@ -19,6 +19,7 @@ type BreadcrumbDropdownProps = {
19
19
  items: (BreadcrumbItem & { isActive?: boolean })[];
20
20
  onItemClick?: (item: BreadcrumbItem, index: number) => void;
21
21
  className?: string;
22
+ withChevron?: boolean;
22
23
  };
23
24
 
24
25
  export function BreadcrumbDropdown({
@@ -27,10 +28,12 @@ export function BreadcrumbDropdown({
27
28
  items,
28
29
  onItemClick,
29
30
  className,
31
+ withChevron,
30
32
  }: BreadcrumbDropdownProps): JSX.Element | null {
31
33
  const { useTelemetry, useTranslate } = useThemeHooks();
32
34
  const telemetry = useTelemetry();
33
35
  const { translate } = useTranslate();
36
+ const [isOpen, setIsOpen] = React.useState(false);
34
37
 
35
38
  if (!items || items.length === 0) {
36
39
  return null;
@@ -46,7 +49,12 @@ export function BreadcrumbDropdown({
46
49
  children
47
50
  );
48
51
 
49
- const trigger = <StyledDropdownTrigger>{triggerContent}</StyledDropdownTrigger>;
52
+ const trigger = (
53
+ <StyledDropdownTrigger>
54
+ {triggerContent}
55
+ {withChevron && <GenericIcon icon={isOpen ? 'chevron-up' : 'chevron-down'} />}
56
+ </StyledDropdownTrigger>
57
+ );
50
58
 
51
59
  return (
52
60
  <BreadcrumbDropdownWrapper
@@ -54,7 +62,12 @@ export function BreadcrumbDropdown({
54
62
  className={className}
55
63
  data-testid="breadcrumb-dropdown"
56
64
  >
57
- <Dropdown trigger={trigger} closeOnClick={true}>
65
+ <Dropdown
66
+ trigger={trigger}
67
+ closeOnClick={true}
68
+ onOpen={() => setIsOpen(true)}
69
+ onClose={() => setIsOpen(false)}
70
+ >
58
71
  <DropdownMenu>
59
72
  {items.map((item, index) => {
60
73
  const isActive = Boolean(item?.isActive);
@@ -117,7 +130,7 @@ const StyledDropdownTrigger = styled.button`
117
130
  background-color: var(--breadcrumbs-background-color-hover);
118
131
  }
119
132
 
120
- &:focus {
133
+ &:focus-visible {
121
134
  box-shadow: var(--breadcrumbs-box-shadow-focus);
122
135
  outline: none;
123
136
  }
@@ -10,7 +10,6 @@ import { BreadcrumbDropdown } from '@redocly/theme/components/Breadcrumbs/Breadc
10
10
  import { BreadcrumbIcon } from '@redocly/theme/components/Breadcrumbs/BreadcrumbIcon';
11
11
  import { trimText } from '@redocly/theme/core/utils';
12
12
  import { BREADCRUMB_MAX_LENGTH } from '@redocly/theme/core/constants';
13
- import { GenericIcon } from '@redocly/theme/icons/GenericIcon/GenericIcon';
14
13
 
15
14
  export function Breadcrumbs(props: {
16
15
  className?: string;
@@ -65,6 +64,7 @@ export function Breadcrumbs(props: {
65
64
  <BreadcrumbDropdown
66
65
  label={translatedLabel}
67
66
  items={siblingsWithActive}
67
+ withChevron
68
68
  onItemClick={(item, itemIdx) =>
69
69
  telemetry.sendBreadcrumbClickedMessage([
70
70
  {
@@ -78,7 +78,6 @@ export function Breadcrumbs(props: {
78
78
  >
79
79
  <BreadcrumbIcon icon={breadcrumb.icon} />
80
80
  {trimText(translatedLabel, BREADCRUMB_MAX_LENGTH)}
81
- <GenericIcon icon="chevron-down" />
82
81
  </BreadcrumbDropdown>
83
82
  );
84
83
  }
@@ -30,6 +30,7 @@ export type DropdownProps = PropsWithChildren<{
30
30
 
31
31
  onClick?: (event: React.UIEvent) => void;
32
32
  onClose?: () => void;
33
+ onOpen?: () => void;
33
34
  }>;
34
35
 
35
36
  export const Dropdown = forwardRef<HTMLDivElement, DropdownProps>(
@@ -47,6 +48,7 @@ export const Dropdown = forwardRef<HTMLDivElement, DropdownProps>(
47
48
  alignment,
48
49
  onClick,
49
50
  onClose,
51
+ onOpen,
50
52
  },
51
53
  ref,
52
54
  ) => {
@@ -55,6 +57,7 @@ export const Dropdown = forwardRef<HTMLDivElement, DropdownProps>(
55
57
 
56
58
  const handleOpen = () => {
57
59
  setIsOpen(true);
60
+ onOpen?.();
58
61
  };
59
62
 
60
63
  const handleClose = () => {
@@ -69,7 +72,11 @@ export const Dropdown = forwardRef<HTMLDivElement, DropdownProps>(
69
72
  const handleToggle = (event: React.UIEvent) => {
70
73
  event.stopPropagation();
71
74
  event.preventDefault();
72
- setIsOpen(!isOpen);
75
+ if (isOpen) {
76
+ handleClose();
77
+ } else {
78
+ handleOpen();
79
+ }
73
80
  };
74
81
 
75
82
  const handleKeyDown = (event: React.KeyboardEvent) => {
@@ -10,6 +10,7 @@ import {
10
10
  type ContentSegment,
11
11
  } from '@redocly/theme/core/types';
12
12
  import { TOOL_CALL_DISPLAY_TEXT } from '@redocly/theme/core/constants';
13
+ import { ToolCallName } from '@redocly/theme/core/types';
13
14
  import { Link } from '@redocly/theme/components/Link/Link';
14
15
  import { Tag } from '@redocly/theme/components/Tag/Tag';
15
16
  import { AiSearchConversationRole } from '@redocly/theme/core/constants';
@@ -26,14 +27,33 @@ function MarkdownSegment({ text }: { text: string }): JSX.Element {
26
27
  const markdown = useMarkdownText(text);
27
28
  return <ResponseText as="div" children={markdown} data-testid="response-text" />;
28
29
  }
29
- function getToolCallDisplayText(toolName: string): {
30
+ // The codemode `execute` tool passes a human-readable `description` of what its code
31
+ // does; show that instead of a generic "Executing execute..." label.
32
+ function getExecuteDescription(args: unknown): string | undefined {
33
+ if (args && typeof args === 'object' && 'description' in args) {
34
+ const { description } = args as { description?: unknown };
35
+ if (typeof description === 'string' && description.trim().length > 0) {
36
+ return description.trim();
37
+ }
38
+ }
39
+ return undefined;
40
+ }
41
+
42
+ function getToolCallDisplayText(toolCall: ToolCall): {
30
43
  inProgressText: string;
31
44
  completedText: string;
32
45
  } {
46
+ if (toolCall.name === ToolCallName.Execute) {
47
+ const description = getExecuteDescription(toolCall.args);
48
+ if (description) {
49
+ return { inProgressText: description, completedText: description };
50
+ }
51
+ }
52
+
33
53
  return (
34
- TOOL_CALL_DISPLAY_TEXT[toolName] ?? {
35
- inProgressText: `Executing ${toolName}...`,
36
- completedText: `${toolName} executed`,
54
+ TOOL_CALL_DISPLAY_TEXT[toolCall.name] ?? {
55
+ inProgressText: `Executing ${toolCall.name}...`,
56
+ completedText: `${toolCall.name} executed`,
37
57
  }
38
58
  );
39
59
  }
@@ -135,7 +155,7 @@ function SearchAiMessageComponent({
135
155
  const toolCallCompleted = Boolean(segment.toolCall.result);
136
156
 
137
157
  const { inProgressText, completedText } = getToolCallDisplayText(
138
- segment.toolCall.name,
158
+ segment.toolCall,
139
159
  );
140
160
 
141
161
  const toolCallDisplayText = toolCallCompleted ? completedText : inProgressText;
@@ -132,4 +132,5 @@ export enum ToolCallName {
132
132
  GetEndpointInfo = 'get-endpoint-info',
133
133
  GetSecuritySchemes = 'get-security-schemes',
134
134
  GetFullApiDescription = 'get-full-api-description',
135
+ Execute = 'execute',
135
136
  }