@redocly/theme 0.63.0-next.1 → 0.63.0-next.3

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.
@@ -39,23 +39,27 @@ const styled_components_1 = __importStar(require("styled-components"));
39
39
  const hooks_1 = require("../../core/hooks");
40
40
  const RadioCheckButtonIcon_1 = require("../../icons/RadioCheckButtonIcon/RadioCheckButtonIcon");
41
41
  const Button_1 = require("../../components/Button/Button");
42
+ const constants_1 = require("../../core/constants");
42
43
  function Comment({ settings, onSubmit, onCancel, className, standAlone = true, isDialog = false, }) {
43
44
  const { useTranslate } = (0, hooks_1.useThemeHooks)();
44
45
  const { translate } = useTranslate();
45
46
  const { label, submitText } = settings || {};
46
47
  const [text, setText] = react_1.default.useState('');
47
48
  const [submitValue, setSubmitValue] = react_1.default.useState('');
49
+ const [isMaxLengthReached, setIsMaxLengthReached] = react_1.default.useState(false);
48
50
  const modalRef = (0, react_1.useRef)(null);
49
51
  (0, hooks_1.useOutsideClick)(modalRef, onCancel);
50
52
  const send = () => {
51
- if (!text)
53
+ const trimmedText = text.trim();
54
+ if (!trimmedText)
52
55
  return;
53
- setSubmitValue(text);
54
- onSubmit({ comment: text });
56
+ setSubmitValue(trimmedText);
57
+ onSubmit({ comment: trimmedText });
55
58
  };
56
59
  const handleTextAreaChange = (e) => {
57
60
  const commentValue = e.target.value;
58
61
  setText(commentValue);
62
+ setIsMaxLengthReached(commentValue.length >= constants_1.MAX_CONTEXT_LENGTH);
59
63
  if (!standAlone) {
60
64
  onSubmit({ comment: commentValue });
61
65
  }
@@ -70,7 +74,11 @@ function Comment({ settings, onSubmit, onCancel, className, standAlone = true, i
70
74
  return (react_1.default.createElement(CommentWrapper, { ref: modalRef, "data-component-name": "Feedback/Comment", className: className, style: standAlone ? { width: 'var(--feedback-report-dialog-width)' } : { width: 'auto' } },
71
75
  react_1.default.createElement(Label, { "data-translation-key": "feedback.settings.comment.label", standAlone: standAlone }, label ||
72
76
  translate('feedback.settings.comment.label', 'Please share your feedback with us.')),
73
- react_1.default.createElement(TextArea, { rows: 3, onChange: handleTextAreaChange }),
77
+ react_1.default.createElement(TextArea, { rows: 3, maxLength: constants_1.MAX_CONTEXT_LENGTH, onChange: handleTextAreaChange }),
78
+ isMaxLengthReached && (react_1.default.createElement(MaxLengthMessage, { "data-translation-key": "feedback.settings.comment.maxLength" }, translate('feedback.settings.comment.maxLength', {
79
+ maxLength: constants_1.MAX_CONTEXT_LENGTH,
80
+ defaultValue: `Maximum content length of ${constants_1.MAX_CONTEXT_LENGTH} characters reached`,
81
+ }))),
74
82
  standAlone && (react_1.default.createElement(ButtonsContainer, null,
75
83
  onCancel && (react_1.default.createElement(Button_1.Button, { "data-translation-key": "feedback.settings.comment.cancel", onClick: onCancel, variant: "text", size: "small" }, translate('feedback.settings.comment.cancel', 'Cancel'))),
76
84
  react_1.default.createElement(Button_1.Button, { "data-translation-key": "feedback.settings.comment.send", onClick: send, variant: isDialog ? 'primary' : 'secondary', size: "small" }, translate('feedback.settings.comment.send', 'Send'))))));
@@ -136,4 +144,9 @@ const ButtonsContainer = styled_components_1.default.div `
136
144
  margin-top: var(--spacing-xs);
137
145
  gap: var(--spacing-xs);
138
146
  `;
147
+ const MaxLengthMessage = styled_components_1.default.div `
148
+ font-size: var(--font-size-sm);
149
+ color: var(--text-color-secondary);
150
+ margin-top: var(--spacing-xxs);
151
+ `;
139
152
  //# sourceMappingURL=Comment.js.map
@@ -46,6 +46,7 @@ const Button_1 = require("../../components/Button/Button");
46
46
  const FaceDissatisfiedIcon_1 = require("../../icons/FaceDissatisfiedIcon/FaceDissatisfiedIcon");
47
47
  const FaceSatisfiedIcon_1 = require("../../icons/FaceSatisfiedIcon/FaceSatisfiedIcon");
48
48
  const FaceNeutralIcon_1 = require("../../icons/FaceNeutralIcon/FaceNeutralIcon");
49
+ const constants_1 = require("../../core/constants");
49
50
  var MOOD_STATES;
50
51
  (function (MOOD_STATES) {
51
52
  MOOD_STATES["SATISFIED"] = "satisfied";
@@ -118,11 +119,13 @@ function Mood({ settings, onSubmit, className }) {
118
119
  const displaySubmitBnt = !!(score && (displayReasons || displayComment));
119
120
  const displayFeedbackEmail = !!score && !(optionalEmailSettings === null || optionalEmailSettings === void 0 ? void 0 : optionalEmailSettings.hide) && !userData.isAuthenticated;
120
121
  const onSubmitMoodForm = () => {
122
+ const trimmedComment = (comment === null || comment === void 0 ? void 0 : comment.trim()) || undefined;
123
+ const trimmedEmail = (email === null || email === void 0 ? void 0 : email.trim()) || undefined;
121
124
  onSubmit({
122
125
  score: remapScore(score),
123
- comment,
126
+ comment: trimmedComment,
124
127
  reasons,
125
- email,
128
+ email: trimmedEmail,
126
129
  });
127
130
  setIsSubmitted(true);
128
131
  };
@@ -170,7 +173,7 @@ function Mood({ settings, onSubmit, className }) {
170
173
  React.createElement(InputLabel, { "data-translation-key": "feedback.settings.optionalEmail.label" }, (optionalEmailSettings === null || optionalEmailSettings === void 0 ? void 0 : optionalEmailSettings.label) ||
171
174
  translate('feedback.settings.optionalEmail.label', 'Your email (optional, for follow-up)')),
172
175
  React.createElement(EmailInput, { onChange: onEmailChange, placeholder: (optionalEmailSettings === null || optionalEmailSettings === void 0 ? void 0 : optionalEmailSettings.placeholder) ||
173
- translate('feedback.settings.optionalEmail.placeholder', 'yourname@example.com'), type: "email", required: !!email, onKeyDown: (e) => {
176
+ translate('feedback.settings.optionalEmail.placeholder', 'yourname@example.com'), type: "email", maxLength: constants_1.MAX_EMAIL_LENGTH, required: !!email, onKeyDown: (e) => {
174
177
  if (e.key === 'Enter') {
175
178
  e.preventDefault();
176
179
  onSubmitMoodForm();
@@ -47,6 +47,7 @@ const RadioCheckButtonIcon_1 = require("../../icons/RadioCheckButtonIcon/RadioCh
47
47
  const Comment_1 = require("../../components/Feedback/Comment");
48
48
  const Stars_1 = require("../../components/Feedback/Stars");
49
49
  const Button_1 = require("../../components/Button/Button");
50
+ const constants_1 = require("../../core/constants");
50
51
  exports.FEEDBACK_MAX_RATING = 5;
51
52
  function Rating({ settings, onSubmit, className }) {
52
53
  const { label, submitText, comment: commentSettings, reasons: reasonsSettings, optionalEmail: optionalEmailSettings, } = settings || {};
@@ -63,12 +64,14 @@ function Rating({ settings, onSubmit, className }) {
63
64
  setEmail(value || undefined);
64
65
  };
65
66
  const onSubmitRatingForm = () => {
67
+ const trimmedComment = (comment === null || comment === void 0 ? void 0 : comment.trim()) || undefined;
68
+ const trimmedEmail = (email === null || email === void 0 ? void 0 : email.trim()) || undefined;
66
69
  onSubmit({
67
70
  score,
68
- comment,
71
+ comment: trimmedComment,
69
72
  reasons,
70
73
  max: exports.FEEDBACK_MAX_RATING,
71
- email,
74
+ email: trimmedEmail,
72
75
  });
73
76
  setIsSubmitted(true);
74
77
  };
@@ -115,7 +118,7 @@ function Rating({ settings, onSubmit, className }) {
115
118
  React.createElement(InputLabel, { "data-translation-key": "feedback.settings.optionalEmail.label" }, (optionalEmailSettings === null || optionalEmailSettings === void 0 ? void 0 : optionalEmailSettings.label) ||
116
119
  translate('feedback.settings.optionalEmail.label', 'Your email (optional, for follow-up)')),
117
120
  React.createElement(EmailInput, { onChange: onEmailChange, placeholder: (optionalEmailSettings === null || optionalEmailSettings === void 0 ? void 0 : optionalEmailSettings.placeholder) ||
118
- translate('feedback.settings.optionalEmail.placeholder', 'yourname@example.com'), type: "email", required: !!email, onKeyDown: (e) => {
121
+ translate('feedback.settings.optionalEmail.placeholder', 'yourname@example.com'), type: "email", maxLength: constants_1.MAX_EMAIL_LENGTH, required: !!email, onKeyDown: (e) => {
119
122
  if (e.key === 'Enter') {
120
123
  e.preventDefault();
121
124
  onSubmitRatingForm();
@@ -47,6 +47,7 @@ const RadioCheckButtonIcon_1 = require("../../icons/RadioCheckButtonIcon/RadioCh
47
47
  const Comment_1 = require("../../components/Feedback/Comment");
48
48
  const Reasons_1 = require("../../components/Feedback/Reasons");
49
49
  const Button_1 = require("../../components/Button/Button");
50
+ const constants_1 = require("../../core/constants");
50
51
  exports.MAX_SCALE = 10;
51
52
  function Scale({ settings, onSubmit, className }) {
52
53
  const { label, submitText, leftScaleLabel, rightScaleLabel, comment: commentSettings, reasons: reasonsSettings, optionalEmail: optionalEmailSettings, } = settings || {};
@@ -77,12 +78,14 @@ function Scale({ settings, onSubmit, className }) {
77
78
  setEmail(undefined);
78
79
  };
79
80
  const onSubmitScaleForm = () => {
81
+ const trimmedComment = (comment === null || comment === void 0 ? void 0 : comment.trim()) || undefined;
82
+ const trimmedEmail = (email === null || email === void 0 ? void 0 : email.trim()) || undefined;
80
83
  onSubmit({
81
84
  score,
82
- comment,
85
+ comment: trimmedComment,
83
86
  reasons,
84
87
  max: exports.MAX_SCALE,
85
- email,
88
+ email: trimmedEmail,
86
89
  });
87
90
  setIsSubmitted(true);
88
91
  };
@@ -122,7 +125,7 @@ function Scale({ settings, onSubmit, className }) {
122
125
  React.createElement(InputLabel, { "data-translation-key": "feedback.settings.optionalEmail.label" }, (optionalEmailSettings === null || optionalEmailSettings === void 0 ? void 0 : optionalEmailSettings.label) ||
123
126
  translate('feedback.settings.optionalEmail.label', 'Your email (optional, for follow-up)')),
124
127
  React.createElement(EmailInput, { onChange: onEmailChange, placeholder: (optionalEmailSettings === null || optionalEmailSettings === void 0 ? void 0 : optionalEmailSettings.placeholder) ||
125
- translate('feedback.settings.optionalEmail.placeholder', 'yourname@example.com'), type: "email", required: !!email, onKeyDown: (e) => {
128
+ translate('feedback.settings.optionalEmail.placeholder', 'yourname@example.com'), type: "email", maxLength: constants_1.MAX_EMAIL_LENGTH, required: !!email, onKeyDown: (e) => {
126
129
  if (e.key === 'Enter') {
127
130
  e.preventDefault();
128
131
  onSubmitScaleForm();
@@ -47,6 +47,7 @@ const Comment_1 = require("../../components/Feedback/Comment");
47
47
  const Button_1 = require("../../components/Button/Button");
48
48
  const ThumbDownIcon_1 = require("../../icons/ThumbDownIcon/ThumbDownIcon");
49
49
  const ThumbUpIcon_1 = require("../../icons/ThumbUpIcon/ThumbUpIcon");
50
+ const constants_1 = require("../../core/constants");
50
51
  function Sentiment({ settings, onSubmit, className }) {
51
52
  const { label, submitText, comment: commentSettings, reasons: reasonsSettings, optionalEmail: optionalEmailSettings, } = settings || {};
52
53
  const [isSubmitted, setIsSubmitted] = React.useState(false);
@@ -105,11 +106,13 @@ function Sentiment({ settings, onSubmit, className }) {
105
106
  return scoreSpecificReasonSettings === null || scoreSpecificReasonSettings === void 0 ? void 0 : scoreSpecificReasonSettings.component;
106
107
  };
107
108
  const onSubmitSentimentForm = () => {
109
+ const trimmedComment = (comment === null || comment === void 0 ? void 0 : comment.trim()) || undefined;
110
+ const trimmedEmail = (email === null || email === void 0 ? void 0 : email.trim()) || undefined;
108
111
  onSubmit({
109
112
  score,
110
- comment,
113
+ comment: trimmedComment,
111
114
  reasons,
112
- email,
115
+ email: trimmedEmail,
113
116
  });
114
117
  setIsSubmitted(true);
115
118
  };
@@ -154,7 +157,7 @@ function Sentiment({ settings, onSubmit, className }) {
154
157
  React.createElement(InputLabel, { "data-translation-key": "feedback.settings.optionalEmail.label" }, (optionalEmailSettings === null || optionalEmailSettings === void 0 ? void 0 : optionalEmailSettings.label) ||
155
158
  translate('feedback.settings.optionalEmail.label', 'Your email (optional, for follow-up)')),
156
159
  React.createElement(EmailInput, { onChange: onEmailChange, placeholder: (optionalEmailSettings === null || optionalEmailSettings === void 0 ? void 0 : optionalEmailSettings.placeholder) ||
157
- translate('feedback.settings.optionalEmail.placeholder', 'yourname@example.com'), type: "email", required: !!email, onKeyDown: (e) => {
160
+ translate('feedback.settings.optionalEmail.placeholder', 'yourname@example.com'), type: "email", maxLength: constants_1.MAX_EMAIL_LENGTH, required: !!email, onKeyDown: (e) => {
158
161
  if (e.key === 'Enter') {
159
162
  e.preventDefault();
160
163
  onSubmitSentimentForm();
@@ -0,0 +1,2 @@
1
+ export declare const MAX_EMAIL_LENGTH = 254;
2
+ export declare const MAX_CONTEXT_LENGTH = 5000;
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MAX_CONTEXT_LENGTH = exports.MAX_EMAIL_LENGTH = void 0;
4
+ exports.MAX_EMAIL_LENGTH = 254;
5
+ exports.MAX_CONTEXT_LENGTH = 5000;
6
+ //# sourceMappingURL=feedback.js.map
@@ -6,3 +6,4 @@ export * from './catalog';
6
6
  export * from './breadcrumb';
7
7
  export * from './request-methods';
8
8
  export * from './mcp';
9
+ export * from './feedback';
@@ -22,4 +22,5 @@ __exportStar(require("./catalog"), exports);
22
22
  __exportStar(require("./breadcrumb"), exports);
23
23
  __exportStar(require("./request-methods"), exports);
24
24
  __exportStar(require("./mcp"), exports);
25
+ __exportStar(require("./feedback"), exports);
25
26
  //# sourceMappingURL=index.js.map
@@ -23,6 +23,7 @@ const use_mcp_config_1 = require("./use-mcp-config");
23
23
  const clipboard_service_1 = require("../utils/clipboard-service");
24
24
  const dom_1 = require("../utils/dom");
25
25
  const mcp_1 = require("../utils/mcp");
26
+ const urls_1 = require("../utils/urls");
26
27
  function createPageActionResource(pageSlug, pageUrl) {
27
28
  return {
28
29
  id: pageSlug,
@@ -79,9 +80,12 @@ function usePageActions(pageSlug, mcpUrl, actions) {
79
80
  const origin = dom_1.IS_BROWSER
80
81
  ? window.location.origin
81
82
  : ((_a = globalThis['SSR_HOSTNAME']) !== null && _a !== void 0 ? _a : '');
82
- const normalizedSlug = pageSlug.startsWith('/') ? pageSlug : '/' + pageSlug;
83
- const pageUrl = `${origin}${normalizedSlug}`;
84
- const mdPageUrl = new URL(origin + normalizedSlug + (normalizedSlug === '/' ? 'index.html.md' : '.md')).toString();
83
+ const pathname = (0, urls_1.addTrailingSlash)(pageSlug);
84
+ const pageUrl = (0, urls_1.combineUrls)(origin, pathname);
85
+ const isRoot = (0, urls_1.withoutPathPrefix)(pathname) === '/';
86
+ const mdPageUrl = isRoot
87
+ ? (0, urls_1.combineUrls)(origin, pathname, 'index.html.md')
88
+ : (0, urls_1.combineUrls)(origin, (0, urls_1.removeTrailingSlash)(pathname) + '.md');
85
89
  const actionHandlers = {
86
90
  'docs-mcp-cursor': createMCPHandler('cursor', false),
87
91
  'docs-mcp-vscode': createMCPHandler('vscode', false),
@@ -119,7 +123,6 @@ function usePageActions(pageSlug, mcpUrl, actions) {
119
123
  telemetry.sendPageActionsButtonClickedMessage([
120
124
  Object.assign(Object.assign({}, createPageActionResource(pageSlug, pageUrl)), { action_type: 'view' }),
121
125
  ]);
122
- window.location.href = mdPageUrl;
123
126
  },
124
127
  }),
125
128
  chatgpt: () => {
@@ -26,3 +26,4 @@ export { isUndefined, isString, isNotNull, isObject } from '../utils/type-guards
26
26
  export { ThemeDataContext, type ThemeDataTransferObject } from '../contexts/ThemeDataContext';
27
27
  export { SearchSessionProvider, SearchSessionContext } from '../contexts/SearchContext';
28
28
  export { useUniqueSvgIds } from '../hooks/use-unique-svg-ids';
29
+ export { trimText } from '../utils/text-trimmer';
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.useUniqueSvgIds = exports.SearchSessionContext = exports.SearchSessionProvider = exports.ThemeDataContext = exports.isObject = exports.isNotNull = exports.isString = exports.isUndefined = exports.SecurityVariablesEnvSuffix = exports.useDialogHotKeys = exports.useSearchDialog = exports.useModalScrollLock = exports.useActiveSectionId = exports.useOutsideClick = exports.useThemeHooks = exports.useFocusTrap = exports.getUserAgent = exports.ClipboardService = exports.getOperationColor = exports.isPrimitive = exports.breakpoints = exports.GlobalStyle = exports.useMount = exports.typedMemo = exports.capitalize = exports.withPathPrefix = exports.addTrailingSlash = exports.combineUrls = exports.getPathPrefix = exports.removeLeadingSlash = exports.addLeadingSlash = exports.IS_BROWSER = void 0;
3
+ exports.trimText = exports.useUniqueSvgIds = exports.SearchSessionContext = exports.SearchSessionProvider = exports.ThemeDataContext = exports.isObject = exports.isNotNull = exports.isString = exports.isUndefined = exports.SecurityVariablesEnvSuffix = exports.useDialogHotKeys = exports.useSearchDialog = exports.useModalScrollLock = exports.useActiveSectionId = exports.useOutsideClick = exports.useThemeHooks = exports.useFocusTrap = exports.getUserAgent = exports.ClipboardService = exports.getOperationColor = exports.isPrimitive = exports.breakpoints = exports.GlobalStyle = exports.useMount = exports.typedMemo = exports.capitalize = exports.withPathPrefix = exports.addTrailingSlash = exports.combineUrls = exports.getPathPrefix = exports.removeLeadingSlash = exports.addLeadingSlash = exports.IS_BROWSER = void 0;
4
4
  var dom_1 = require("../utils/dom");
5
5
  Object.defineProperty(exports, "IS_BROWSER", { enumerable: true, get: function () { return dom_1.IS_BROWSER; } });
6
6
  var urls_1 = require("../utils/urls");
@@ -56,4 +56,6 @@ Object.defineProperty(exports, "SearchSessionProvider", { enumerable: true, get:
56
56
  Object.defineProperty(exports, "SearchSessionContext", { enumerable: true, get: function () { return SearchContext_1.SearchSessionContext; } });
57
57
  var use_unique_svg_ids_1 = require("../hooks/use-unique-svg-ids");
58
58
  Object.defineProperty(exports, "useUniqueSvgIds", { enumerable: true, get: function () { return use_unique_svg_ids_1.useUniqueSvgIds; } });
59
+ var text_trimmer_1 = require("../utils/text-trimmer");
60
+ Object.defineProperty(exports, "trimText", { enumerable: true, get: function () { return text_trimmer_1.trimText; } });
59
61
  //# sourceMappingURL=index.js.map
@@ -46,6 +46,16 @@ const replayDarkMode = (0, styled_components_1.css) `
46
46
 
47
47
  // @tokens End
48
48
  `;
49
+ const badgesDarkMode = (0, styled_components_1.css) `
50
+ /**
51
+ * @tokens Audience Badge
52
+ */
53
+
54
+ --badge-audience-text-color: var(--text-color-secondary); // @presenter Color
55
+ --badge-audience-bg-color: var(--color-warm-grey-4); // @presenter Color
56
+
57
+ // @tokens End
58
+ `;
49
59
  exports.darkMode = (0, styled_components_1.css) `
50
60
  /* === Dark Theme Colors === */
51
61
 
@@ -333,6 +343,7 @@ exports.darkMode = (0, styled_components_1.css) `
333
343
  ${variables_dark_15.bannerDarkMode}
334
344
  ${variables_dark_16.admonitionDarkMode}
335
345
  ${variables_dark_17.svgViewerDarkMode}
346
+ ${badgesDarkMode}
336
347
 
337
348
  /**
338
349
  * @tokens Dark Theme Scrollbar Config
@@ -815,6 +815,13 @@ const badges = (0, styled_components_1.css) `
815
815
  --badge-deprecated-bg-color: var(--color-warning-base); // @presenter Color
816
816
  --badge-deprecated-border-radius: var(--border-radius); // @presenter BorderRadius
817
817
 
818
+ /**
819
+ * @tokens Audience Badge
820
+ */
821
+
822
+ --badge-audience-text-color: var(--text-color-secondary); // @presenter Color
823
+ --badge-audience-bg-color: var(--color-warm-grey-2); // @presenter Color
824
+
818
825
  // @tokens End
819
826
  `;
820
827
  const loadProgressBar = (0, styled_components_1.css) `
@@ -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.welcomeText' | 'search.ai.newConversation' | 'search.ai.backToSearch' | 'search.ai.back' | 'search.ai.assistant' | 'search.ai.placeholder' | 'search.ai.generatingResponse' | 'search.ai.followUpQuestion' | 'search.ai.suggestionsTitle' | 'search.ai.thinkingText' | 'search.ai.resourcesFound' | 'search.ai.resourcesFound.basedOn' | 'search.ai.resourcesFound.resources' | 'search.ai.feedback.title' | 'search.ai.feedback.detailsPlaceholder' | 'search.ai.feedback.thanks' | 'search.ai.toolResult.found' | 'search.ai.toolResult.found.documents' | 'search.ai.toolCall.searching' | 'search.ai.button' | 'search.ai.label' | 'search.ai.disclaimer' | 'search.ai.error.description' | 'search.ai.error.description.forbidden' | 'search.ai.error.description.unauthorized' | 'search.ai.error.header' | 'search.ai.error.header.forbidden' | 'search.ai.error.header.unauthorized' | 'search.ai.feedback.more' | 'search.searchItem.deprecated' | 'search.groups.all' | 'search.filter.field.footer' | 'aiAssistant.trigger' | 'toc.header' | 'footer.copyrightText' | 'page.homeButton' | 'page.forbidden.title' | 'page.forbidden.description' | 'page.notFound.title' | 'page.notFound.description' | 'page.lastUpdated.timeago' | 'page.lastUpdated.on' | 'catalog.filters.placeholder' | 'catalog.filters.title' | 'catalog.filters.add' | 'catalog.filters.clearAll' | 'catalog.filters.select.addFilter' | 'catalog.filters.select.all' | 'catalog.filters.done' | 'catalog.catalogs.all.title' | 'catalog.catalogs.all.description' | 'catalog.catalogs.all.switcherLabel' | 'catalog.catalogs.service.title' | 'catalog.catalogs.service.description' | 'catalog.catalogs.service.switcherLabel' | 'catalog.catalogs.user.title' | 'catalog.catalogs.user.description' | 'catalog.catalogs.user.switcherLabel' | 'catalog.catalogs.team.title' | 'catalog.catalogs.team.description' | 'catalog.catalogs.team.switcherLabel' | 'catalog.catalogs.domain.title' | 'catalog.catalogs.domain.description' | 'catalog.catalogs.domain.switcherLabel' | 'catalog.catalogs.apiDescription.title' | 'catalog.catalogs.apiDescription.description' | 'catalog.catalogs.apiDescription.switcherLabel' | 'catalog.catalogs.dataSchema.title' | 'catalog.catalogs.dataSchema.description' | 'catalog.catalogs.dataSchema.switcherLabel' | 'catalog.catalogs.apiOperation.title' | 'catalog.catalogs.apiOperation.description' | 'catalog.catalogs.apiOperation.switcherLabel' | 'catalog.entity.metadata.title' | 'catalog.entity.schema.title' | 'catalog.entity.properties.apiDescription.title' | 'catalog.backToAllLabel' | 'catalog.tags.more' | 'catalog.tags.label' | 'catalog.sort' | 'catalog.catalogs.label' | 'catalog.owners.label' | 'catalog.repositories.label' | 'catalog.email.label' | 'catalog.format.label' | 'catalog.entityType.label' | 'catalog.domains.label' | 'catalog.contact.label' | 'catalog.methodAndPath.label' | 'catalog.links.label' | 'catalog.metadata.domains' | 'catalog.metadata.owners' | 'catalog.history.button.label' | 'catalog.history.sidebar.title' | 'catalog.history.sidebar.close' | 'catalog.history.version.label' | 'catalog.history.version.notSpecified' | 'catalog.history.version.default' | 'catalog.history.revisions.limitMessage' | 'catalog.history.revision.current' | 'catalog.history.revisions.showLess' | 'catalog.history.revisions.showMore' | 'sidebar.menu.backLabel' | 'sidebar.menu.backToLabel' | 'sidebar.actions.show' | 'sidebar.actions.hide' | 'sidebar.actions.changeToSingleColumn' | 'sidebar.actions.changeToTwoColumns' | 'sidebar.actions.singleColumn' | 'sidebar.actions.twoColumns' | '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.submit' | 'feedback.cancel' | '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' | 'codeSnippet.expand.tooltipText' | 'codeSnippet.collapse.tooltipText' | 'userMenu.login' | 'userMenu.logout' | 'userMenu.devOnboardingLabel' | 'mobileMenu.mainMenu' | 'mobileMenu.previous' | 'mobileMenu.products' | 'mobileMenu.version' | 'navbar.products' | 'page.nextButton' | 'page.previousButton' | 'page.actions.copyButtonText' | 'page.actions.copyTitle' | 'page.actions.copyDescription' | 'page.actions.viewAsMdTitle' | 'page.actions.viewAsMdButtonText' | 'page.actions.viewAsMdDescription' | 'page.actions.chatGptTitle' | 'page.actions.chatGptButtonText' | 'page.actions.chatGptDescription' | 'page.actions.claudeTitle' | 'page.actions.claudeButtonText' | 'page.actions.claudeDescription' | 'page.actions.cursorMcpButtonText' | 'page.actions.cursorMcpTitle' | 'page.actions.cursorMcpDescription' | 'page.actions.connectMcp' | 'page.actions.connectMcp.cursor' | 'page.actions.connectMcp.cursorDescription' | 'page.actions.connectMcp.vscode' | 'page.actions.connectMcp.vscodeDescription' | 'page.actions.connectMcp.copyConfig' | 'page.actions.connectMcp.copyConfigDescription' | '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.hideExample' | 'openapi.showExample' | 'openapi.expandAll' | 'openapi.collapseAll' | 'openapi.viewSecurityDetails' | 'openapi.noResponseExample' | 'openapi.discriminator.searchPlaceholder' | 'openapi.discriminator.searchNoResults' | 'openapi.discriminator.defaultMapping' | 'openapi.discriminator.defaultMappingTooltip' | 'openapi.noResponseContent' | 'openapi.noRequestPayload' | 'openapi.hidePattern' | 'openapi.showPattern' | 'openapi.authorizationUrl' | 'openapi.tokenUrl' | 'openapi.refreshUrl' | 'openapi.showOptionalScopes' | 'openapi.hideOptionalScopes' | 'openapi.security' | 'openapi.httpAuthorizationScheme' | 'openapi.bearerFormat' | 'openapi.parameterName' | 'openapi.flowType' | 'openapi.connectUrl' | 'openapi.requiredScopes' | 'openapi.unsupportedLanguage' | 'openapi.failedToGenerateCodeSample' | 'openapi.schemaCatalogLink.title' | 'openapi.schemaCatalogLink.copyButtonTooltip' | 'openapi.schemaCatalogLink.copiedTooltip' | 'openapi.mcp.title' | 'openapi.mcp.endpoint' | 'openapi.mcp.tools' | 'openapi.mcp.protocolVersion' | 'openapi.mcp.capabilities' | 'openapi.mcp.experimentalCapabilities' | 'openapi.mcp.inputSchema' | 'openapi.mcp.inputExample' | 'openapi.mcp.outputSchema' | 'openapi.mcp.outputExample' | 'asyncapi.download.description.title' | 'asyncapi.info.title' | 'graphql.download.description.title' | 'graphql.info.title' | 'graphql.info.contact.url' | 'graphql.info.contact.name' | 'graphql.info.license' | 'graphql.info.termsOfService' | 'graphql.overview' | 'graphql.metadata' | 'graphql.key' | 'graphql.value' | '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.requiredScopes' | 'graphql.viewSecurityDetails' | 'graphql.objectScopes' | 'graphql.fieldScopes' | 'graphql.implementedInterfaces' | 'graphql.nonNull' | 'graphql.required' | 'graphql.deprecated' | 'graphql.variables' | 'graphql.querySample' | 'graphql.mutationSample' | 'graphql.subscriptionSample' | 'graphql.responseSample' | 'graphql.locations' | 'graphql.sample' | 'graphql.referenced' | 'graphql.content.fragment' | 'codeWalkthrough.download' | 'codeWalkthrough.preview' | 'time.justNow' | 'time.past.second' | 'time.past.seconds' | 'time.past.minute' | 'time.past.minutes' | 'time.past.hour' | 'time.past.hours' | 'time.past.day' | 'time.past.days' | 'time.past.week' | 'time.past.weeks' | 'time.past.month' | 'time.past.months' | 'time.past.year' | 'time.past.years' | 'page.internalServerError.title' | 'page.internalServerError.description' | 'page.skipToContent.label' | 'select.noResults' | 'loaders.loading' | 'filter.dateRange.from' | 'filter.dateRange.to' | 'mermaid.openFullscreen' | 'mermaid.zoomIn' | 'mermaid.zoomOut' | 'mermaid.reset' | 'mermaid.close' | 'mermaid.viewer';
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.welcomeText' | 'search.ai.newConversation' | 'search.ai.backToSearch' | 'search.ai.back' | 'search.ai.assistant' | 'search.ai.placeholder' | 'search.ai.generatingResponse' | 'search.ai.followUpQuestion' | 'search.ai.suggestionsTitle' | 'search.ai.thinkingText' | 'search.ai.resourcesFound' | 'search.ai.resourcesFound.basedOn' | 'search.ai.resourcesFound.resources' | 'search.ai.feedback.title' | 'search.ai.feedback.detailsPlaceholder' | 'search.ai.feedback.thanks' | 'search.ai.toolResult.found' | 'search.ai.toolResult.found.documents' | 'search.ai.toolCall.searching' | 'search.ai.button' | 'search.ai.label' | 'search.ai.disclaimer' | 'search.ai.error.description' | 'search.ai.error.description.forbidden' | 'search.ai.error.description.unauthorized' | 'search.ai.error.header' | 'search.ai.error.header.forbidden' | 'search.ai.error.header.unauthorized' | 'search.ai.feedback.more' | 'search.searchItem.deprecated' | 'search.groups.all' | 'search.filter.field.footer' | 'aiAssistant.trigger' | 'toc.header' | 'footer.copyrightText' | 'page.homeButton' | 'page.forbidden.title' | 'page.forbidden.description' | 'page.notFound.title' | 'page.notFound.description' | 'page.lastUpdated.timeago' | 'page.lastUpdated.on' | 'catalog.filters.placeholder' | 'catalog.filters.title' | 'catalog.filters.add' | 'catalog.filters.clearAll' | 'catalog.filters.select.addFilter' | 'catalog.filters.select.all' | 'catalog.filters.done' | 'catalog.catalogs.all.title' | 'catalog.catalogs.all.description' | 'catalog.catalogs.all.switcherLabel' | 'catalog.catalogs.service.title' | 'catalog.catalogs.service.description' | 'catalog.catalogs.service.switcherLabel' | 'catalog.catalogs.user.title' | 'catalog.catalogs.user.description' | 'catalog.catalogs.user.switcherLabel' | 'catalog.catalogs.team.title' | 'catalog.catalogs.team.description' | 'catalog.catalogs.team.switcherLabel' | 'catalog.catalogs.domain.title' | 'catalog.catalogs.domain.description' | 'catalog.catalogs.domain.switcherLabel' | 'catalog.catalogs.apiDescription.title' | 'catalog.catalogs.apiDescription.description' | 'catalog.catalogs.apiDescription.switcherLabel' | 'catalog.catalogs.dataSchema.title' | 'catalog.catalogs.dataSchema.description' | 'catalog.catalogs.dataSchema.switcherLabel' | 'catalog.catalogs.apiOperation.title' | 'catalog.catalogs.apiOperation.description' | 'catalog.catalogs.apiOperation.switcherLabel' | 'catalog.entity.metadata.title' | 'catalog.entity.schema.title' | 'catalog.entity.properties.apiDescription.title' | 'catalog.backToAllLabel' | 'catalog.tags.more' | 'catalog.tags.label' | 'catalog.sort' | 'catalog.catalogs.label' | 'catalog.owners.label' | 'catalog.repositories.label' | 'catalog.email.label' | 'catalog.format.label' | 'catalog.entityType.label' | 'catalog.domains.label' | 'catalog.contact.label' | 'catalog.methodAndPath.label' | 'catalog.links.label' | 'catalog.metadata.domains' | 'catalog.metadata.owners' | 'catalog.history.button.label' | 'catalog.history.sidebar.title' | 'catalog.history.sidebar.close' | 'catalog.history.version.label' | 'catalog.history.version.notSpecified' | 'catalog.history.version.default' | 'catalog.history.revisions.limitMessage' | 'catalog.history.revision.current' | 'catalog.history.revisions.showLess' | 'catalog.history.revisions.showMore' | 'sidebar.menu.backLabel' | 'sidebar.menu.backToLabel' | 'sidebar.actions.show' | 'sidebar.actions.hide' | 'sidebar.actions.changeToSingleColumn' | 'sidebar.actions.changeToTwoColumns' | 'sidebar.actions.singleColumn' | 'sidebar.actions.twoColumns' | '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.maxLength' | 'feedback.settings.comment.satisfiedLabel' | 'feedback.settings.comment.neutralLabel' | 'feedback.settings.comment.dissatisfiedLabel' | 'feedback.settings.submitText' | 'feedback.settings.label' | 'feedback.settings.reasons.label' | 'feedback.submit' | 'feedback.cancel' | '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' | 'codeSnippet.expand.tooltipText' | 'codeSnippet.collapse.tooltipText' | 'userMenu.login' | 'userMenu.logout' | 'userMenu.devOnboardingLabel' | 'mobileMenu.mainMenu' | 'mobileMenu.previous' | 'mobileMenu.products' | 'mobileMenu.version' | 'navbar.products' | 'page.nextButton' | 'page.previousButton' | 'page.actions.copyButtonText' | 'page.actions.copyTitle' | 'page.actions.copyDescription' | 'page.actions.viewAsMdTitle' | 'page.actions.viewAsMdButtonText' | 'page.actions.viewAsMdDescription' | 'page.actions.chatGptTitle' | 'page.actions.chatGptButtonText' | 'page.actions.chatGptDescription' | 'page.actions.claudeTitle' | 'page.actions.claudeButtonText' | 'page.actions.claudeDescription' | 'page.actions.cursorMcpButtonText' | 'page.actions.cursorMcpTitle' | 'page.actions.cursorMcpDescription' | 'page.actions.connectMcp' | 'page.actions.connectMcp.cursor' | 'page.actions.connectMcp.cursorDescription' | 'page.actions.connectMcp.vscode' | 'page.actions.connectMcp.vscodeDescription' | 'page.actions.connectMcp.copyConfig' | 'page.actions.connectMcp.copyConfigDescription' | '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.hideExample' | 'openapi.showExample' | 'openapi.expandAll' | 'openapi.collapseAll' | 'openapi.viewSecurityDetails' | 'openapi.noResponseExample' | 'openapi.discriminator.searchPlaceholder' | 'openapi.discriminator.searchNoResults' | 'openapi.discriminator.defaultMapping' | 'openapi.discriminator.defaultMappingTooltip' | 'openapi.noResponseContent' | 'openapi.noRequestPayload' | 'openapi.hidePattern' | 'openapi.showPattern' | 'openapi.authorizationUrl' | 'openapi.tokenUrl' | 'openapi.refreshUrl' | 'openapi.showOptionalScopes' | 'openapi.hideOptionalScopes' | 'openapi.security' | 'openapi.httpAuthorizationScheme' | 'openapi.bearerFormat' | 'openapi.parameterName' | 'openapi.flowType' | 'openapi.connectUrl' | 'openapi.requiredScopes' | 'openapi.unsupportedLanguage' | 'openapi.failedToGenerateCodeSample' | 'openapi.schemaCatalogLink.title' | 'openapi.schemaCatalogLink.copyButtonTooltip' | 'openapi.schemaCatalogLink.copiedTooltip' | 'openapi.mcp.title' | 'openapi.mcp.endpoint' | 'openapi.mcp.tools' | 'openapi.mcp.protocolVersion' | 'openapi.mcp.capabilities' | 'openapi.mcp.experimentalCapabilities' | 'openapi.mcp.inputSchema' | 'openapi.mcp.inputExample' | 'openapi.mcp.outputSchema' | 'openapi.mcp.outputExample' | 'asyncapi.download.description.title' | 'asyncapi.info.title' | 'graphql.download.description.title' | 'graphql.info.title' | 'graphql.info.contact.url' | 'graphql.info.contact.name' | 'graphql.info.license' | 'graphql.info.termsOfService' | 'graphql.overview' | 'graphql.metadata' | 'graphql.key' | 'graphql.value' | '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.requiredScopes' | 'graphql.viewSecurityDetails' | 'graphql.objectScopes' | 'graphql.fieldScopes' | 'graphql.implementedInterfaces' | 'graphql.nonNull' | 'graphql.required' | 'graphql.deprecated' | 'graphql.variables' | 'graphql.querySample' | 'graphql.mutationSample' | 'graphql.subscriptionSample' | 'graphql.responseSample' | 'graphql.locations' | 'graphql.sample' | 'graphql.referenced' | 'graphql.content.fragment' | 'codeWalkthrough.download' | 'codeWalkthrough.preview' | 'time.justNow' | 'time.past.second' | 'time.past.seconds' | 'time.past.minute' | 'time.past.minutes' | 'time.past.hour' | 'time.past.hours' | 'time.past.day' | 'time.past.days' | 'time.past.week' | 'time.past.weeks' | 'time.past.month' | 'time.past.months' | 'time.past.year' | 'time.past.years' | 'page.internalServerError.title' | 'page.internalServerError.description' | 'page.skipToContent.label' | 'select.noResults' | 'loaders.loading' | 'filter.dateRange.from' | 'filter.dateRange.to' | 'mermaid.openFullscreen' | 'mermaid.zoomIn' | 'mermaid.zoomOut' | 'mermaid.reset' | 'mermaid.close' | 'mermaid.viewer';
3
3
  export type Locale = {
4
4
  code: string;
5
5
  name: string;
@@ -0,0 +1,5 @@
1
+ import React from 'react';
2
+ import type { IconProps } from '../../icons/types';
3
+ export declare const DirectionRightIcon: import("styled-components").StyledComponent<(props: IconProps) => React.JSX.Element, any, {
4
+ 'data-component-name': string;
5
+ }, "data-component-name">;
@@ -0,0 +1,24 @@
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.DirectionRightIcon = 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({ width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", xmlns: "http://www.w3.org/2000/svg" }, props),
11
+ react_1.default.createElement("path", { d: "M9.5 4L8.79295 4.70705L11.0859 7H5C4.73478 7 4.48043 7.10536 4.29289 7.29289C4.10536 7.48043 4 7.73478 4 8V14H5V8H11.0859L8.79295 10.2929L9.5 11L13 7.5L9.5 4Z", fill: "#1A1C21" }),
12
+ react_1.default.createElement("path", { d: "M5 2H4V6H5V2Z", fill: "#1A1C21" })));
13
+ exports.DirectionRightIcon = (0, styled_components_1.default)(Icon).attrs(() => ({
14
+ 'data-component-name': 'icons/DirectionRightIcon/DirectionRightIcon',
15
+ })) `
16
+ path {
17
+ fill: ${({ color }) => (0, utils_1.getCssColorVariable)(color)};
18
+ }
19
+
20
+ height: ${({ size }) => size || '16px'};
21
+ width: ${({ size }) => size || '16px'};
22
+ vertical-align: middle;
23
+ `;
24
+ //# sourceMappingURL=DirectionRightIcon.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@redocly/theme",
3
- "version": "0.63.0-next.1",
3
+ "version": "0.63.0-next.3",
4
4
  "description": "Shared UI components lib",
5
5
  "keywords": [
6
6
  "theme",
@@ -62,7 +62,7 @@
62
62
  "typescript": "5.9.3",
63
63
  "vitest": "4.0.10",
64
64
  "vitest-when": "0.6.2",
65
- "webpack": "5.94.0",
65
+ "webpack": "5.105.2",
66
66
  "@redocly/realm-asyncapi-sdk": "0.9.0-next.1"
67
67
  },
68
68
  "dependencies": {
@@ -78,7 +78,7 @@
78
78
  "lodash.debounce": "^4.0.8",
79
79
  "lodash.throttle": "4.1.1",
80
80
  "nprogress": "0.2.0",
81
- "openapi-sampler": "1.6.2",
81
+ "openapi-sampler": "1.7.0",
82
82
  "react-calendar": "5.1.0",
83
83
  "react-date-picker": "11.0.0",
84
84
  "@redocly/config": "0.43.0"
@@ -6,6 +6,7 @@ import type { JSX } from 'react';
6
6
  import { useOutsideClick, useThemeHooks } from '@redocly/theme/core/hooks';
7
7
  import { RadioCheckButtonIcon } from '@redocly/theme/icons/RadioCheckButtonIcon/RadioCheckButtonIcon';
8
8
  import { Button } from '@redocly/theme/components/Button/Button';
9
+ import { MAX_CONTEXT_LENGTH } from '@redocly/theme/core/constants';
9
10
 
10
11
  export type CommentProps = {
11
12
  onSubmit: (value: { comment: string }) => void;
@@ -32,18 +33,21 @@ export function Comment({
32
33
  const { label, submitText } = settings || {};
33
34
  const [text, setText] = React.useState('');
34
35
  const [submitValue, setSubmitValue] = React.useState('');
36
+ const [isMaxLengthReached, setIsMaxLengthReached] = React.useState(false);
35
37
  const modalRef = useRef<HTMLDivElement>(null);
36
38
 
37
39
  useOutsideClick(modalRef, onCancel);
38
40
 
39
41
  const send = () => {
40
- if (!text) return;
41
- setSubmitValue(text);
42
- onSubmit({ comment: text });
42
+ const trimmedText = text.trim();
43
+ if (!trimmedText) return;
44
+ setSubmitValue(trimmedText);
45
+ onSubmit({ comment: trimmedText });
43
46
  };
44
47
  const handleTextAreaChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
45
48
  const commentValue = e.target.value;
46
49
  setText(commentValue);
50
+ setIsMaxLengthReached(commentValue.length >= MAX_CONTEXT_LENGTH);
47
51
 
48
52
  if (!standAlone) {
49
53
  onSubmit({ comment: commentValue });
@@ -81,7 +85,15 @@ export function Comment({
81
85
  {label ||
82
86
  translate('feedback.settings.comment.label', 'Please share your feedback with us.')}
83
87
  </Label>
84
- <TextArea rows={3} onChange={handleTextAreaChange} />
88
+ <TextArea rows={3} maxLength={MAX_CONTEXT_LENGTH} onChange={handleTextAreaChange} />
89
+ {isMaxLengthReached && (
90
+ <MaxLengthMessage data-translation-key="feedback.settings.comment.maxLength">
91
+ {translate('feedback.settings.comment.maxLength', {
92
+ maxLength: MAX_CONTEXT_LENGTH,
93
+ defaultValue: `Maximum content length of ${MAX_CONTEXT_LENGTH} characters reached`,
94
+ })}
95
+ </MaxLengthMessage>
96
+ )}
85
97
  {standAlone && (
86
98
  <ButtonsContainer>
87
99
  {onCancel && (
@@ -175,3 +187,9 @@ const ButtonsContainer = styled.div`
175
187
  margin-top: var(--spacing-xs);
176
188
  gap: var(--spacing-xs);
177
189
  `;
190
+
191
+ const MaxLengthMessage = styled.div`
192
+ font-size: var(--font-size-sm);
193
+ color: var(--text-color-secondary);
194
+ margin-top: var(--spacing-xxs);
195
+ `;
@@ -14,6 +14,7 @@ import { Button } from '@redocly/theme/components/Button/Button';
14
14
  import { FaceDissatisfiedIcon } from '@redocly/theme/icons/FaceDissatisfiedIcon/FaceDissatisfiedIcon';
15
15
  import { FaceSatisfiedIcon } from '@redocly/theme/icons/FaceSatisfiedIcon/FaceSatisfiedIcon';
16
16
  import { FaceNeutralIcon } from '@redocly/theme/icons/FaceNeutralIcon/FaceNeutralIcon';
17
+ import { MAX_EMAIL_LENGTH } from '@redocly/theme/core/constants';
17
18
 
18
19
  export enum MOOD_STATES {
19
20
  SATISFIED = 'satisfied',
@@ -135,11 +136,13 @@ export function Mood({ settings, onSubmit, className }: MoodProps): JSX.Element
135
136
  const displayFeedbackEmail = !!score && !optionalEmailSettings?.hide && !userData.isAuthenticated;
136
137
 
137
138
  const onSubmitMoodForm = () => {
139
+ const trimmedComment = comment?.trim() || undefined;
140
+ const trimmedEmail = email?.trim() || undefined;
138
141
  onSubmit({
139
142
  score: remapScore(score),
140
- comment,
143
+ comment: trimmedComment,
141
144
  reasons,
142
- email,
145
+ email: trimmedEmail,
143
146
  });
144
147
  setIsSubmitted(true);
145
148
  };
@@ -255,6 +258,7 @@ export function Mood({ settings, onSubmit, className }: MoodProps): JSX.Element
255
258
  translate('feedback.settings.optionalEmail.placeholder', 'yourname@example.com')
256
259
  }
257
260
  type="email"
261
+ maxLength={MAX_EMAIL_LENGTH}
258
262
  required={!!email}
259
263
  onKeyDown={(e) => {
260
264
  if (e.key === 'Enter') {
@@ -12,6 +12,7 @@ import { RadioCheckButtonIcon } from '@redocly/theme/icons/RadioCheckButtonIcon/
12
12
  import { Comment } from '@redocly/theme/components/Feedback/Comment';
13
13
  import { Stars } from '@redocly/theme/components/Feedback/Stars';
14
14
  import { Button } from '@redocly/theme/components/Button/Button';
15
+ import { MAX_EMAIL_LENGTH } from '@redocly/theme/core/constants';
15
16
 
16
17
  export const FEEDBACK_MAX_RATING = 5;
17
18
 
@@ -64,12 +65,14 @@ export function Rating({ settings, onSubmit, className }: RatingProps): JSX.Elem
64
65
  };
65
66
 
66
67
  const onSubmitRatingForm = () => {
68
+ const trimmedComment = comment?.trim() || undefined;
69
+ const trimmedEmail = email?.trim() || undefined;
67
70
  onSubmit({
68
71
  score,
69
- comment,
72
+ comment: trimmedComment,
70
73
  reasons,
71
74
  max: FEEDBACK_MAX_RATING,
72
- email,
75
+ email: trimmedEmail,
73
76
  });
74
77
  setIsSubmitted(true);
75
78
  };
@@ -168,6 +171,7 @@ export function Rating({ settings, onSubmit, className }: RatingProps): JSX.Elem
168
171
  translate('feedback.settings.optionalEmail.placeholder', 'yourname@example.com')
169
172
  }
170
173
  type="email"
174
+ maxLength={MAX_EMAIL_LENGTH}
171
175
  required={!!email}
172
176
  onKeyDown={(e) => {
173
177
  if (e.key === 'Enter') {
@@ -12,6 +12,7 @@ import { RadioCheckButtonIcon } from '@redocly/theme/icons/RadioCheckButtonIcon/
12
12
  import { Comment } from '@redocly/theme/components/Feedback/Comment';
13
13
  import { Reasons } from '@redocly/theme/components/Feedback/Reasons';
14
14
  import { Button } from '@redocly/theme/components/Button/Button';
15
+ import { MAX_EMAIL_LENGTH } from '@redocly/theme/core/constants';
15
16
 
16
17
  export const MAX_SCALE = 10;
17
18
 
@@ -96,12 +97,14 @@ export function Scale({ settings, onSubmit, className }: ScaleProps): JSX.Elemen
96
97
  };
97
98
 
98
99
  const onSubmitScaleForm = () => {
100
+ const trimmedComment = comment?.trim() || undefined;
101
+ const trimmedEmail = email?.trim() || undefined;
99
102
  onSubmit({
100
103
  score,
101
- comment,
104
+ comment: trimmedComment,
102
105
  reasons,
103
106
  max: MAX_SCALE,
104
- email,
107
+ email: trimmedEmail,
105
108
  });
106
109
  setIsSubmitted(true);
107
110
  };
@@ -193,6 +196,7 @@ export function Scale({ settings, onSubmit, className }: ScaleProps): JSX.Elemen
193
196
  translate('feedback.settings.optionalEmail.placeholder', 'yourname@example.com')
194
197
  }
195
198
  type="email"
199
+ maxLength={MAX_EMAIL_LENGTH}
196
200
  required={!!email}
197
201
  onKeyDown={(e) => {
198
202
  if (e.key === 'Enter') {
@@ -13,6 +13,7 @@ import { Comment } from '@redocly/theme/components/Feedback/Comment';
13
13
  import { Button } from '@redocly/theme/components/Button/Button';
14
14
  import { ThumbDownIcon } from '@redocly/theme/icons/ThumbDownIcon/ThumbDownIcon';
15
15
  import { ThumbUpIcon } from '@redocly/theme/icons/ThumbUpIcon/ThumbUpIcon';
16
+ import { MAX_EMAIL_LENGTH } from '@redocly/theme/core/constants';
16
17
 
17
18
  export type SentimentProps = {
18
19
  onSubmit: (value: {
@@ -117,11 +118,13 @@ export function Sentiment({ settings, onSubmit, className }: SentimentProps): JS
117
118
  };
118
119
 
119
120
  const onSubmitSentimentForm = () => {
121
+ const trimmedComment = comment?.trim() || undefined;
122
+ const trimmedEmail = email?.trim() || undefined;
120
123
  onSubmit({
121
124
  score,
122
- comment,
125
+ comment: trimmedComment,
123
126
  reasons,
124
- email,
127
+ email: trimmedEmail,
125
128
  });
126
129
  setIsSubmitted(true);
127
130
  };
@@ -227,6 +230,7 @@ export function Sentiment({ settings, onSubmit, className }: SentimentProps): JS
227
230
  translate('feedback.settings.optionalEmail.placeholder', 'yourname@example.com')
228
231
  }
229
232
  type="email"
233
+ maxLength={MAX_EMAIL_LENGTH}
230
234
  required={!!email}
231
235
  onKeyDown={(e) => {
232
236
  if (e.key === 'Enter') {
@@ -0,0 +1,2 @@
1
+ export const MAX_EMAIL_LENGTH = 254;
2
+ export const MAX_CONTEXT_LENGTH = 5000;
@@ -6,3 +6,4 @@ export * from './catalog';
6
6
  export * from './breadcrumb';
7
7
  export * from './request-methods';
8
8
  export * from './mcp';
9
+ export * from './feedback';
@@ -16,6 +16,12 @@ import { useMCPConfig } from './use-mcp-config';
16
16
  import { ClipboardService } from '../utils/clipboard-service';
17
17
  import { IS_BROWSER } from '../utils/dom';
18
18
  import { generateMCPDeepLink } from '../utils/mcp';
19
+ import {
20
+ addTrailingSlash,
21
+ combineUrls,
22
+ removeTrailingSlash,
23
+ withoutPathPrefix,
24
+ } from '../utils/urls';
19
25
 
20
26
  function createPageActionResource(pageSlug: string, pageUrl: string) {
21
27
  return {
@@ -108,11 +114,12 @@ export function usePageActions(
108
114
  const origin = IS_BROWSER
109
115
  ? window.location.origin
110
116
  : ((globalThis as { SSR_HOSTNAME?: string })['SSR_HOSTNAME'] ?? '');
111
- const normalizedSlug = pageSlug.startsWith('/') ? pageSlug : '/' + pageSlug;
112
- const pageUrl = `${origin}${normalizedSlug}`;
113
- const mdPageUrl = new URL(
114
- origin + normalizedSlug + (normalizedSlug === '/' ? 'index.html.md' : '.md'),
115
- ).toString();
117
+ const pathname = addTrailingSlash(pageSlug);
118
+ const pageUrl = combineUrls(origin, pathname);
119
+ const isRoot = withoutPathPrefix(pathname) === '/';
120
+ const mdPageUrl = isRoot
121
+ ? combineUrls(origin, pathname, 'index.html.md')
122
+ : combineUrls(origin, removeTrailingSlash(pathname) + '.md');
116
123
 
117
124
  const actionHandlers: Record<PageActionType, () => PageAction | null> = {
118
125
  'docs-mcp-cursor': createMCPHandler('cursor', false),
@@ -158,7 +165,6 @@ export function usePageActions(
158
165
  action_type: 'view',
159
166
  },
160
167
  ]);
161
- window.location.href = mdPageUrl;
162
168
  },
163
169
  }),
164
170
 
@@ -39,3 +39,4 @@ 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
41
  export { useUniqueSvgIds } from '../hooks/use-unique-svg-ids';
42
+ export { trimText } from '../utils/text-trimmer';
@@ -46,6 +46,17 @@ const replayDarkMode = css`
46
46
  // @tokens End
47
47
  `;
48
48
 
49
+ const badgesDarkMode = css`
50
+ /**
51
+ * @tokens Audience Badge
52
+ */
53
+
54
+ --badge-audience-text-color: var(--text-color-secondary); // @presenter Color
55
+ --badge-audience-bg-color: var(--color-warm-grey-4); // @presenter Color
56
+
57
+ // @tokens End
58
+ `
59
+
49
60
 
50
61
  export const darkMode = css`
51
62
  /* === Dark Theme Colors === */
@@ -334,6 +345,7 @@ export const darkMode = css`
334
345
  ${bannerDarkMode}
335
346
  ${admonitionDarkMode}
336
347
  ${svgViewerDarkMode}
348
+ ${badgesDarkMode}
337
349
 
338
350
  /**
339
351
  * @tokens Dark Theme Scrollbar Config
@@ -823,6 +823,13 @@ const badges = css`
823
823
  --badge-deprecated-bg-color: var(--color-warning-base); // @presenter Color
824
824
  --badge-deprecated-border-radius: var(--border-radius); // @presenter BorderRadius
825
825
 
826
+ /**
827
+ * @tokens Audience Badge
828
+ */
829
+
830
+ --badge-audience-text-color: var(--text-color-secondary); // @presenter Color
831
+ --badge-audience-bg-color: var(--color-warm-grey-2); // @presenter Color
832
+
826
833
  // @tokens End
827
834
  `;
828
835
 
@@ -203,6 +203,7 @@ export type TranslationKey =
203
203
  | 'feedback.settings.comment.label'
204
204
  | 'feedback.settings.comment.send'
205
205
  | 'feedback.settings.comment.cancel'
206
+ | 'feedback.settings.comment.maxLength'
206
207
  | 'feedback.settings.comment.satisfiedLabel'
207
208
  | 'feedback.settings.comment.neutralLabel'
208
209
  | 'feedback.settings.comment.dissatisfiedLabel'
@@ -0,0 +1,35 @@
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
+ width="16"
11
+ height="16"
12
+ viewBox="0 0 16 16"
13
+ fill="none"
14
+ xmlns="http://www.w3.org/2000/svg"
15
+ {...props}
16
+ >
17
+ <path
18
+ d="M9.5 4L8.79295 4.70705L11.0859 7H5C4.73478 7 4.48043 7.10536 4.29289 7.29289C4.10536 7.48043 4 7.73478 4 8V14H5V8H11.0859L8.79295 10.2929L9.5 11L13 7.5L9.5 4Z"
19
+ fill="#1A1C21"
20
+ />
21
+ <path d="M5 2H4V6H5V2Z" fill="#1A1C21" />
22
+ </svg>
23
+ );
24
+
25
+ export const DirectionRightIcon = styled(Icon).attrs(() => ({
26
+ 'data-component-name': 'icons/DirectionRightIcon/DirectionRightIcon',
27
+ }))`
28
+ path {
29
+ fill: ${({ color }) => getCssColorVariable(color)};
30
+ }
31
+
32
+ height: ${({ size }) => size || '16px'};
33
+ width: ${({ size }) => size || '16px'};
34
+ vertical-align: middle;
35
+ `;