@redocly/theme 0.61.1 → 0.62.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 (39) hide show
  1. package/lib/components/Catalog/CatalogEntity/CatalogEntity.js +2 -2
  2. package/lib/components/Catalog/CatalogEntity/CatalogEntityHistory/CatalogEntityHistorySidebar.js +1 -1
  3. package/lib/components/Catalog/CatalogEntity/CatalogEntityProperties/TagsProperty.js +2 -2
  4. package/lib/components/Catalog/CatalogTableView/CatalogTableViewRow.js +1 -1
  5. package/lib/components/JsonViewer/JsonViewer.js +1 -1
  6. package/lib/components/Search/SearchAiMessage.js +2 -2
  7. package/lib/core/hooks/use-page-actions.js +3 -3
  8. package/lib/core/hooks/use-unique-svg-ids.d.ts +6 -0
  9. package/lib/core/hooks/use-unique-svg-ids.js +15 -0
  10. package/lib/core/openapi/index.d.ts +1 -0
  11. package/lib/core/openapi/index.js +3 -1
  12. package/lib/core/styles/global.js +29 -11
  13. package/lib/core/types/l10n.d.ts +1 -1
  14. package/lib/index.d.ts +3 -0
  15. package/lib/index.js +3 -0
  16. package/lib/layouts/DocumentationLayout.js +4 -25
  17. package/lib/layouts/DocumentationLayoutBottom.d.ts +11 -0
  18. package/lib/layouts/DocumentationLayoutBottom.js +28 -0
  19. package/lib/layouts/DocumentationLayoutTop.d.ts +13 -0
  20. package/lib/layouts/DocumentationLayoutTop.js +33 -0
  21. package/lib/layouts/Forbidden.js +22 -18
  22. package/package.json +3 -3
  23. package/src/components/Catalog/CatalogEntity/CatalogEntity.tsx +1 -1
  24. package/src/components/Catalog/CatalogEntity/CatalogEntityHistory/CatalogEntityHistorySidebar.tsx +1 -2
  25. package/src/components/Catalog/CatalogEntity/CatalogEntityProperties/TagsProperty.tsx +1 -1
  26. package/src/components/Catalog/CatalogTableView/CatalogTableViewRow.tsx +1 -2
  27. package/src/components/JsonViewer/JsonViewer.tsx +1 -2
  28. package/src/components/Search/SearchAiMessage.tsx +2 -3
  29. package/src/core/hooks/__mocks__/use-theme-hooks.ts +1 -0
  30. package/src/core/hooks/use-page-actions.ts +3 -2
  31. package/src/core/hooks/use-unique-svg-ids.ts +12 -0
  32. package/src/core/openapi/index.ts +1 -0
  33. package/src/core/styles/global.ts +29 -11
  34. package/src/core/types/l10n.ts +1 -0
  35. package/src/index.ts +3 -0
  36. package/src/layouts/DocumentationLayout.tsx +4 -30
  37. package/src/layouts/DocumentationLayoutBottom.tsx +42 -0
  38. package/src/layouts/DocumentationLayoutTop.tsx +52 -0
  39. package/src/layouts/Forbidden.tsx +36 -21
@@ -18,7 +18,7 @@ const hooks_1 = require("../../../core/hooks");
18
18
  const CatalogEntitySchema_1 = require("../../../components/Catalog/CatalogEntity/CatalogEntitySchema");
19
19
  const CatalogEntityMethodAndPath_1 = require("../../../components/Catalog/CatalogEntity/CatalogEntityMethodAndPath");
20
20
  const CatalogEntityRelationsGraph_lazy_1 = require("../../../components/Catalog/CatalogEntity/CatalogEntityGraph/CatalogEntityRelationsGraph.lazy");
21
- const theme_1 = require("../../../index.js");
21
+ const CatalogEntityHistorySidebar_1 = require("../../../components/Catalog/CatalogEntity/CatalogEntityHistory/CatalogEntityHistorySidebar");
22
22
  const renderFirstColumnEntitySection = (entity) => {
23
23
  switch (entity.type) {
24
24
  case 'api-operation':
@@ -45,7 +45,7 @@ function CatalogEntity({ RedocSchema, StoreProvider, GraphqlTypeRenderer, }) {
45
45
  const linkToMainCatalogLabel = translate(catalogConfig.titleTranslationKey);
46
46
  const { searchQuery, setSearchQuery } = useCatalog();
47
47
  return (react_1.default.createElement(CatalogPageWrapper, { "data-component-name": "Catalog/CatalogEntity/CatalogEntity" },
48
- react_1.default.createElement(theme_1.CatalogEntityHistorySidebar, { entityKey: entity.key, revision: revision, version: version }),
48
+ react_1.default.createElement(CatalogEntityHistorySidebar_1.CatalogEntityHistorySidebar, { entityKey: entity.key, revision: revision, version: version }),
49
49
  react_1.default.createElement(CatalogPageContent, null,
50
50
  react_1.default.createElement(Breadcrumbs_1.Breadcrumbs, { additionalBreadcrumbs: [
51
51
  { label: linkToMainCatalogLabel, link: linkToMainCatalog },
@@ -45,7 +45,7 @@ const hooks_1 = require("../../../../core/hooks");
45
45
  const MenuContainer_1 = require("../../../../components/Menu/MenuContainer");
46
46
  const utils_1 = require("../../../../core/utils");
47
47
  const constants_1 = require("../../../../core/constants");
48
- const CatalogEntityVersionItem_1 = require("./CatalogEntityVersionItem");
48
+ const CatalogEntityVersionItem_1 = require("../../../../components/Catalog/CatalogEntity/CatalogEntityHistory/CatalogEntityVersionItem");
49
49
  function CatalogEntityHistorySidebar({ entityKey, revision, version, className, }) {
50
50
  const [isOpen, setIsOpen] = (0, react_1.useState)(false);
51
51
  const location = (0, react_router_dom_1.useLocation)();
@@ -6,7 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.TagsProperty = TagsProperty;
7
7
  const react_1 = __importDefault(require("react"));
8
8
  const TagsIcon_1 = require("../../../../icons/TagsIcon/TagsIcon");
9
- const theme_1 = require("../../../../index.js");
9
+ const CatalogTagsWithTooltip_1 = require("../../../../components/Catalog/CatalogTagsWithTooltip");
10
10
  const CatalogEntityPropertyCard_1 = require("../../../../components/Catalog/CatalogEntity/CatalogEntityProperties/CatalogEntityPropertyCard");
11
11
  const hooks_1 = require("../../../../core/hooks");
12
12
  function TagsProperty({ entity }) {
@@ -15,7 +15,7 @@ function TagsProperty({ entity }) {
15
15
  return (react_1.default.createElement("div", { "data-component-name": "Catalog/CatalogEntity/CatalogEntityProperties/TagsProperty" },
16
16
  react_1.default.createElement(CatalogEntityPropertyCard_1.CatalogEntityPropertyCard, { header: react_1.default.createElement(react_1.default.Fragment, null,
17
17
  react_1.default.createElement(TagsIcon_1.TagsIcon, null),
18
- translate('catalog.tags.label', 'Tags')), content: react_1.default.createElement(theme_1.CatalogTagsWithTooltip, { itemsToShow: 8, items: entity.tags || [], tagProps: {
18
+ translate('catalog.tags.label', 'Tags')), content: react_1.default.createElement(CatalogTagsWithTooltip_1.CatalogTagsWithTooltip, { itemsToShow: 8, items: entity.tags || [], tagProps: {
19
19
  style: {
20
20
  fontSize: 'var(--font-size-base)',
21
21
  borderRadius: 'var(--border-radius)',
@@ -12,7 +12,7 @@ const CatalogEntityCell_1 = require("../../../components/Catalog/CatalogTableVie
12
12
  const CatalogTagsCell_1 = require("../../../components/Catalog/CatalogTableView/CatalogTagsCell");
13
13
  const hooks_1 = require("../../../core/hooks");
14
14
  const CatalogEntityTypeTag_1 = require("../../../components/Catalog/CatalogEntityTypeTag");
15
- const Link_1 = require("../../Link/Link");
15
+ const Link_1 = require("../../../components/Link/Link");
16
16
  const baseColumns = [
17
17
  {
18
18
  key: 'entity',
@@ -40,7 +40,7 @@ exports.JsonViewerWrap = exports.JsonViewer = void 0;
40
40
  const react_1 = __importStar(require("react"));
41
41
  const styled_components_1 = __importDefault(require("styled-components"));
42
42
  const CodeBlock_1 = require("../../components/CodeBlock/CodeBlock");
43
- const Helpers_1 = require("./Helpers");
43
+ const Helpers_1 = require("../../components/JsonViewer/Helpers");
44
44
  function JsonComponent({ data, expandLevel = 1, className, onCopyClick, onPanelToggle, title, controls = {}, }) {
45
45
  const showFoldingButtons = data && Object.values(data).some((value) => typeof value === 'object' && value !== null);
46
46
  const [expandAllSignal, setExpandAllSignal] = react_1.default.useState(undefined);
@@ -48,8 +48,8 @@ const Markdown_1 = require("../../components/Markdown/Markdown");
48
48
  const DocumentIcon_1 = require("../../icons/DocumentIcon/DocumentIcon");
49
49
  const AiStarsIcon_1 = require("../../icons/AiStarsIcon/AiStarsIcon");
50
50
  const CheckmarkOutlineIcon_1 = require("../../icons/CheckmarkOutlineIcon/CheckmarkOutlineIcon");
51
- const SearchAiActionButtons_1 = require("./SearchAiActionButtons");
52
- const SearchAiNegativeFeedbackForm_1 = require("./SearchAiNegativeFeedbackForm");
51
+ const SearchAiActionButtons_1 = require("../../components/Search/SearchAiActionButtons");
52
+ const SearchAiNegativeFeedbackForm_1 = require("../../components/Search/SearchAiNegativeFeedbackForm");
53
53
  function SearchAiMessageComponent({ role, content, isThinking, resources, className, messageId, feedback, onFeedbackChange, }) {
54
54
  var _a;
55
55
  const { useMarkdownText, useTranslate, useTelemetry } = (0, hooks_1.useThemeHooks)();
@@ -181,7 +181,7 @@ function createMCPAction({ clientType, mcpConfig, translate, onClickCallback, })
181
181
  return Object.assign(Object.assign({}, sharedProps), { buttonText: translate('page.actions.connectMcp.vscode', 'Connect to VS Code'), title: translate('page.actions.connectMcp.vscode', 'Connect to VS Code'), description: translate('page.actions.connectMcp.vscodeDescription', 'Install MCP server on VS Code'), iconComponent: VSCodeIcon_1.VSCodeIcon });
182
182
  }
183
183
  function shouldHidePageActions(pageProps, themeConfig, openapiExcludeFromSearch) {
184
- var _a, _b, _c, _d, _e, _f;
184
+ var _a, _b, _c, _d, _e, _f, _g;
185
185
  // Can't use any actions if no markdown files are generated for LLMs
186
186
  if ((_b = (_a = pageProps === null || pageProps === void 0 ? void 0 : pageProps.seo) === null || _a === void 0 ? void 0 : _a.llmstxt) === null || _b === void 0 ? void 0 : _b.hide) {
187
187
  return true;
@@ -191,8 +191,8 @@ function shouldHidePageActions(pageProps, themeConfig, openapiExcludeFromSearch)
191
191
  return true;
192
192
  }
193
193
  // Page is excluded from search
194
- const isPageExcludedFromSearch = ((_e = pageProps === null || pageProps === void 0 ? void 0 : pageProps.frontmatter) === null || _e === void 0 ? void 0 : _e.excludeFromSearch) ||
195
- (((_f = pageProps === null || pageProps === void 0 ? void 0 : pageProps.metadata) === null || _f === void 0 ? void 0 : _f.type) === 'openapi' && openapiExcludeFromSearch);
194
+ const isOpenApiPage = ((_e = pageProps === null || pageProps === void 0 ? void 0 : pageProps.metadata) === null || _e === void 0 ? void 0 : _e.type) === 'openapi' || ((_f = pageProps === null || pageProps === void 0 ? void 0 : pageProps.metadata) === null || _f === void 0 ? void 0 : _f.subType) === 'openapi-operation';
195
+ const isPageExcludedFromSearch = ((_g = pageProps === null || pageProps === void 0 ? void 0 : pageProps.frontmatter) === null || _g === void 0 ? void 0 : _g.excludeFromSearch) || (isOpenApiPage && openapiExcludeFromSearch);
196
196
  if (isPageExcludedFromSearch) {
197
197
  return true;
198
198
  }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Returns a function that appends a per-component-instance suffix to SVG ids.
3
+ * This prevents collisions when multiple identical SVGs are rendered on the same page,
4
+ * which can break `url(#...)` references (gradients, clipPath, masks, filters) on reflow.
5
+ */
6
+ export declare function useUniqueSvgIds(): (id: string) => string;
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useUniqueSvgIds = useUniqueSvgIds;
4
+ const react_1 = require("react");
5
+ /**
6
+ * Returns a function that appends a per-component-instance suffix to SVG ids.
7
+ * This prevents collisions when multiple identical SVGs are rendered on the same page,
8
+ * which can break `url(#...)` references (gradients, clipPath, masks, filters) on reflow.
9
+ */
10
+ function useUniqueSvgIds() {
11
+ const reactId = (0, react_1.useId)();
12
+ const safeSuffix = reactId.replace(/:/g, '_');
13
+ return (id) => `${id}-${safeSuffix}`;
14
+ }
15
+ //# sourceMappingURL=use-unique-svg-ids.js.map
@@ -25,3 +25,4 @@ export { SecurityVariablesEnvSuffix } from '../constants/environments';
25
25
  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
+ export { useUniqueSvgIds } from '../hooks/use-unique-svg-ids';
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- 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.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");
@@ -54,4 +54,6 @@ Object.defineProperty(exports, "ThemeDataContext", { enumerable: true, get: func
54
54
  var SearchContext_1 = require("../contexts/SearchContext");
55
55
  Object.defineProperty(exports, "SearchSessionProvider", { enumerable: true, get: function () { return SearchContext_1.SearchSessionProvider; } });
56
56
  Object.defineProperty(exports, "SearchSessionContext", { enumerable: true, get: function () { return SearchContext_1.SearchSessionContext; } });
57
+ var use_unique_svg_ids_1 = require("../hooks/use-unique-svg-ids");
58
+ Object.defineProperty(exports, "useUniqueSvgIds", { enumerable: true, get: function () { return use_unique_svg_ids_1.useUniqueSvgIds; } });
57
59
  //# sourceMappingURL=index.js.map
@@ -958,23 +958,41 @@ const pages = (0, styled_components_1.css) `
958
958
  */
959
959
 
960
960
  --page-403-font-family: var(--font-family-base); // @presenter FontFamily
961
+ --page-403-margin-vertical: var(--spacing-xl); // @presenter Spacing
962
+ --page-403-margin-horizontal: calc(var(--spacing-xxl) * 2); // @presenter Spacing
963
+ --page-403-gap: var(--spacing-lg); // @presenter Spacing
964
+ --page-403-max-width: 680px; // @presenter Width
965
+
966
+ --page-403-status-text-color: var(--text-color-helper); // @presenter Color
967
+ --page-403-status-font-size: var(--font-size-lg); // @presenter FontSize
968
+ --page-403-status-font-weight: var(--font-weight-semibold); // @presenter FontWeight
969
+ --page-403-status-line-height: var(--line-height-lg); // @presenter LineHeight
970
+
971
+ --page-403-title-text-color: var(--text-color-primary); // @presenter Color
972
+ --page-403-title-font-size: 42px; // @presenter FontSize
973
+ --page-403-title-font-weight: var(--font-weight-bold); // @presenter FontWeight
974
+ --page-403-title-line-height: 50px; // @presenter LineHeight
975
+
976
+ --page-403-description-text-color: var(--text-color-secondary); // @presenter Color
977
+ --page-403-description-font-size: var(--font-size-xl); // @presenter FontSize
978
+ --page-403-description-font-weight: var(--font-weight-regular); // @presenter FontWeight
979
+ --page-403-description-line-height: var(--line-height-xl); // @presenter LineHeight
980
+
981
+ // @tokens End
961
982
 
962
- --page-403-header-text-color: var(--h1-text-color);
983
+ /**
984
+ * @tokens 403 Page OIDC Forbidden
985
+ * @presenter Color
986
+ */
987
+
988
+ --page-403-header-text-color: var(--h1-text-color); // @presenter Color
963
989
  --page-403-header-font-size: var(--h1-font-size); // @presenter FontSize
964
990
  --page-403-header-font-weight: var(--h1-font-weight); // @presenter FontWeight
965
991
  --page-403-header-line-height: var(--h1-line-height); // @presenter LineHeight
966
992
  --page-403-header-margin: 0; // @presenter Spacing
967
-
968
- --page-403-description-text-color: var(--text-color-secondary);
969
- --page-403-description-font-size: 1.5em; // @presenter FontSize
970
- --page-403-description-font-weight: var(--font-weight-regular); // @presenter FontWeight
971
- --page-403-description-line-height: 1; // @presenter LineHeight
972
993
  --page-403-description-margin: 0; // @presenter Spacing
973
-
974
- --page-403-button-margin: 4em; // @presenter Spacing
975
-
976
- --page-403-oidc-description-font-size: var(--font-size-lg);
977
- --page-403-oidc-description-margin: var(--spacing-md) var(--spacing-sm);
994
+ --page-403-oidc-description-font-size: var(--font-size-lg); // @presenter FontSize
995
+ --page-403-oidc-description-margin: var(--spacing-md) var(--spacing-sm); // @presenter Spacing
978
996
 
979
997
  // @tokens End
980
998
 
@@ -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.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.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.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';
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.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.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';
3
3
  export type Locale = {
4
4
  code: string;
5
5
  name: string;
package/lib/index.d.ts CHANGED
@@ -274,5 +274,8 @@ export * from './layouts/OIDCForbidden';
274
274
  export * from './layouts/ThreePanelLayout';
275
275
  export * from './layouts/CodeWalkthroughLayout';
276
276
  export * from './layouts/InternalServerErrorLayout';
277
+ export * from './layouts/DocumentationLayout';
278
+ export * from './layouts/DocumentationLayoutTop';
279
+ export * from './layouts/DocumentationLayoutBottom';
277
280
  export * as markdoc from './markdoc/default';
278
281
  export * from './components/DatePicker/DatePicker';
package/lib/index.js CHANGED
@@ -338,6 +338,9 @@ __exportStar(require("./layouts/OIDCForbidden"), exports);
338
338
  __exportStar(require("./layouts/ThreePanelLayout"), exports);
339
339
  __exportStar(require("./layouts/CodeWalkthroughLayout"), exports);
340
340
  __exportStar(require("./layouts/InternalServerErrorLayout"), exports);
341
+ __exportStar(require("./layouts/DocumentationLayout"), exports);
342
+ __exportStar(require("./layouts/DocumentationLayoutTop"), exports);
343
+ __exportStar(require("./layouts/DocumentationLayoutBottom"), exports);
341
344
  /* Markdoc */
342
345
  exports.markdoc = __importStar(require("./markdoc/default"));
343
346
  /* DatePicker */
@@ -6,26 +6,18 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.DocumentationLayout = DocumentationLayout;
7
7
  const react_1 = __importDefault(require("react"));
8
8
  const styled_components_1 = __importDefault(require("styled-components"));
9
- const EditPageButton_1 = require("../components/Buttons/EditPageButton");
10
9
  const utils_1 = require("../core/utils");
11
- const PageNavigation_1 = require("../components/PageNavigation/PageNavigation");
12
- const LastUpdated_1 = require("../components/LastUpdated/LastUpdated");
13
- const Breadcrumbs_1 = require("../components/Breadcrumbs/Breadcrumbs");
14
10
  const CodeSnippetContext_1 = require("../core/contexts/CodeSnippetContext");
11
+ const DocumentationLayoutTop_1 = require("../layouts/DocumentationLayoutTop");
12
+ const DocumentationLayoutBottom_1 = require("../layouts/DocumentationLayoutBottom");
15
13
  function DocumentationLayout({ tableOfContent, feedback, config, editPage, lastModified, nextPage, prevPage, className, children, }) {
16
14
  var _a;
17
- const { editPage: themeEditPage } = config || {};
18
- const mergedConf = editPage ? Object.assign(Object.assign({}, themeEditPage), editPage) : undefined;
19
15
  return (react_1.default.createElement(CodeSnippetContext_1.CodeSnippetProvider, null,
20
16
  react_1.default.createElement(LayoutWrapper, { "data-component-name": "Layout/DocumentationLayout", className: className },
21
17
  react_1.default.createElement(ContentWrapper, { withToc: !((_a = config === null || config === void 0 ? void 0 : config.toc) === null || _a === void 0 ? void 0 : _a.hide) },
22
- react_1.default.createElement(Breadcrumbs, null),
23
- react_1.default.createElement(LayoutTop, null,
24
- lastModified && react_1.default.createElement(LastUpdated_1.LastUpdated, { lastModified: new Date(lastModified) }),
25
- mergedConf && react_1.default.createElement(EditPageButton_1.EditPageButton, { to: mergedConf.to })),
18
+ react_1.default.createElement(DocumentationLayoutTop_1.DocumentationLayoutTop, { config: config, editPage: editPage, lastModified: lastModified }),
26
19
  children,
27
- react_1.default.createElement(LayoutBottom, null, feedback),
28
- react_1.default.createElement(PageNavigation_1.PageNavigation, { nextPage: nextPage, prevPage: prevPage })),
20
+ react_1.default.createElement(DocumentationLayoutBottom_1.DocumentationLayoutBottom, { feedback: feedback, nextPage: nextPage, prevPage: prevPage })),
29
21
  tableOfContent)));
30
22
  }
31
23
  const LayoutWrapper = styled_components_1.default.div.attrs(({ className }) => ({
@@ -58,17 +50,4 @@ const ContentWrapper = styled_components_1.default.section `
58
50
  width: ${({ withToc }) => (withToc ? `calc(90% - var(--toc-width))` : '90%')};
59
51
  }
60
52
  `;
61
- const LayoutTop = styled_components_1.default.div `
62
- display: flex;
63
- justify-content: space-between;
64
- flex-flow: row nowrap;
65
- `;
66
- const Breadcrumbs = (0, styled_components_1.default)(Breadcrumbs_1.Breadcrumbs) `
67
- margin-bottom: var(--breadcrumbs-margin-bottom);
68
- `;
69
- const LayoutBottom = (0, styled_components_1.default)(LayoutTop) `
70
- > * {
71
- margin: 25px 0;
72
- }
73
- `;
74
53
  //# sourceMappingURL=DocumentationLayout.js.map
@@ -0,0 +1,11 @@
1
+ import React from 'react';
2
+ import type { JSX } from 'react';
3
+ import type { ResolvedNavItemWithLink } from '@redocly/config';
4
+ type DocumentationLayoutBottomProps = {
5
+ feedback: React.ReactNode;
6
+ nextPage?: ResolvedNavItemWithLink | null;
7
+ prevPage?: ResolvedNavItemWithLink | null;
8
+ className?: string;
9
+ };
10
+ export declare function DocumentationLayoutBottom({ feedback, nextPage, prevPage, }: React.PropsWithChildren<DocumentationLayoutBottomProps>): JSX.Element;
11
+ export {};
@@ -0,0 +1,28 @@
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.DocumentationLayoutBottom = DocumentationLayoutBottom;
7
+ const react_1 = __importDefault(require("react"));
8
+ const styled_components_1 = __importDefault(require("styled-components"));
9
+ const PageNavigation_1 = require("../components/PageNavigation/PageNavigation");
10
+ function DocumentationLayoutBottom({ feedback, nextPage, prevPage, }) {
11
+ return (react_1.default.createElement(Wrapper, { "data-component-name": "Layout/DocumentationLayoutBottom" },
12
+ react_1.default.createElement(LayoutBottom, null, feedback),
13
+ react_1.default.createElement(PageNavigation_1.PageNavigation, { nextPage: nextPage, prevPage: prevPage })));
14
+ }
15
+ const Wrapper = styled_components_1.default.div `
16
+ display: flex;
17
+ flex-direction: column;
18
+ `;
19
+ const LayoutBottom = styled_components_1.default.div `
20
+ display: flex;
21
+ justify-content: space-between;
22
+ flex-flow: row nowrap;
23
+
24
+ > * {
25
+ margin: 25px 0;
26
+ }
27
+ `;
28
+ //# sourceMappingURL=DocumentationLayoutBottom.js.map
@@ -0,0 +1,13 @@
1
+ import React from 'react';
2
+ import type { JSX } from 'react';
3
+ import type { MarkdownConfig } from '@redocly/config';
4
+ type DocumentationLayoutTopProps = {
5
+ config?: MarkdownConfig;
6
+ editPage?: {
7
+ to: string;
8
+ };
9
+ /** String in ISO format */
10
+ lastModified?: string | null;
11
+ };
12
+ export declare function DocumentationLayoutTop({ config, editPage, lastModified, }: React.PropsWithChildren<DocumentationLayoutTopProps>): JSX.Element;
13
+ export {};
@@ -0,0 +1,33 @@
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.DocumentationLayoutTop = DocumentationLayoutTop;
7
+ const react_1 = __importDefault(require("react"));
8
+ const styled_components_1 = __importDefault(require("styled-components"));
9
+ const EditPageButton_1 = require("../components/Buttons/EditPageButton");
10
+ const LastUpdated_1 = require("../components/LastUpdated/LastUpdated");
11
+ const Breadcrumbs_1 = require("../components/Breadcrumbs/Breadcrumbs");
12
+ function DocumentationLayoutTop({ config, editPage, lastModified, }) {
13
+ const { editPage: themeEditPage } = config || {};
14
+ const mergedConf = editPage ? Object.assign(Object.assign({}, themeEditPage), editPage) : undefined;
15
+ return (react_1.default.createElement(Wrapper, { "data-component-name": "Layout/DocumentationLayoutTop" },
16
+ react_1.default.createElement(Breadcrumbs, null),
17
+ react_1.default.createElement(LayoutTop, null,
18
+ lastModified && react_1.default.createElement(LastUpdated_1.LastUpdated, { lastModified: new Date(lastModified) }),
19
+ mergedConf && react_1.default.createElement(EditPageButton_1.EditPageButton, { to: mergedConf.to }))));
20
+ }
21
+ const Wrapper = styled_components_1.default.div `
22
+ display: flex;
23
+ flex-direction: column;
24
+ `;
25
+ const LayoutTop = styled_components_1.default.div `
26
+ display: flex;
27
+ justify-content: space-between;
28
+ flex-flow: row nowrap;
29
+ `;
30
+ const Breadcrumbs = (0, styled_components_1.default)(Breadcrumbs_1.Breadcrumbs) `
31
+ margin-bottom: var(--breadcrumbs-margin-bottom);
32
+ `;
33
+ //# sourceMappingURL=DocumentationLayoutTop.js.map
@@ -8,38 +8,42 @@ const react_1 = __importDefault(require("react"));
8
8
  const styled_components_1 = __importDefault(require("styled-components"));
9
9
  const hooks_1 = require("../core/hooks");
10
10
  const Button_1 = require("../components/Button/Button");
11
+ const ArrowLeftIcon_1 = require("../icons/ArrowLeftIcon/ArrowLeftIcon");
11
12
  function Forbidden() {
12
13
  const { useTranslate } = (0, hooks_1.useThemeHooks)();
13
14
  const { translate } = useTranslate();
14
- return (react_1.default.createElement(Wrapper, { "data-component-name": "Pages/Forbidden" },
15
- react_1.default.createElement(Header, null, "403"),
16
- react_1.default.createElement(Description, { "data-translation-key": "page.forbidden.title" }, translate('page.forbidden.title', 'Access forbidden')),
17
- react_1.default.createElement(HomeButton, { variant: "primary", size: "large", to: "/", "data-translation-key": "page.homeButton" }, translate('page.homeButton', 'Go home'))));
15
+ return (react_1.default.createElement(ForbiddenWrapper, { "data-component-name": "layouts/Forbidden" },
16
+ react_1.default.createElement(StatusText, null, "403"),
17
+ react_1.default.createElement(Title, { "data-translation-key": "page.forbidden.title" }, translate('page.forbidden.title', 'Access forbidden')),
18
+ react_1.default.createElement(Description, { "data-translation-key": "page.forbidden.description" }, translate('page.forbidden.description', "You don't have permission to access this page. If you believe this is an error, contact your administrator or return to the homepage.")),
19
+ react_1.default.createElement(Button_1.Button, { variant: "primary", size: "large", to: "/", "data-translation-key": "page.homeButton", icon: react_1.default.createElement(ArrowLeftIcon_1.ArrowLeftIcon, null) }, translate('page.homeButton', 'Go home'))));
18
20
  }
19
- const Wrapper = styled_components_1.default.div `
21
+ const ForbiddenWrapper = styled_components_1.default.div `
22
+ height: 100%;
23
+ max-width: var(--page-403-max-width);
20
24
  display: flex;
21
25
  flex-direction: column;
22
- align-items: center;
23
26
  justify-content: center;
24
- margin: 25px auto;
27
+ margin: var(--page-403-margin-vertical) var(--page-403-margin-horizontal);
25
28
  font-family: var(--page-403-font-family);
26
- text-align: center;
29
+ gap: var(--page-403-gap);
27
30
  `;
28
- const Header = styled_components_1.default.div `
29
- color: var(--page-403-header-text-color);
30
- margin: var(--page-403-header-margin);
31
- font-size: var(--page-403-header-font-size);
32
- line-height: var(--page-403-header-line-height);
33
- font-weight: var(--page-403-header-font-weight);
31
+ const StatusText = styled_components_1.default.div `
32
+ color: var(--page-403-status-text-color);
33
+ font-size: var(--page-403-status-font-size);
34
+ line-height: var(--page-403-status-line-height);
35
+ font-weight: var(--page-403-status-font-weight);
36
+ `;
37
+ const Title = styled_components_1.default.div `
38
+ color: var(--page-403-title-text-color);
39
+ font-size: var(--page-403-title-font-size);
40
+ line-height: var(--page-403-title-line-height);
41
+ font-weight: var(--page-403-title-font-weight);
34
42
  `;
35
43
  const Description = styled_components_1.default.div `
36
44
  color: var(--page-403-description-text-color);
37
- margin: var(--page-403-description-margin);
38
45
  font-size: var(--page-403-description-font-size);
39
46
  line-height: var(--page-403-description-line-height);
40
47
  font-weight: var(--page-403-description-font-weight);
41
48
  `;
42
- const HomeButton = (0, styled_components_1.default)(Button_1.Button) `
43
- margin-top: var(--page-403-button-margin);
44
- `;
45
49
  //# sourceMappingURL=Forbidden.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@redocly/theme",
3
- "version": "0.61.1",
3
+ "version": "0.62.0-next.1",
4
4
  "description": "Shared UI components lib",
5
5
  "keywords": [
6
6
  "theme",
@@ -63,7 +63,7 @@
63
63
  "vitest": "4.0.10",
64
64
  "vitest-when": "0.6.2",
65
65
  "webpack": "5.94.0",
66
- "@redocly/realm-asyncapi-sdk": "0.7.0"
66
+ "@redocly/realm-asyncapi-sdk": "0.8.0-next.1"
67
67
  },
68
68
  "dependencies": {
69
69
  "@tanstack/react-query": "5.62.3",
@@ -88,7 +88,7 @@
88
88
  "ts:check": "tsc --noEmit --skipLibCheck",
89
89
  "clean": "rimraf lib",
90
90
  "compile": "tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json",
91
- "build": "npm run clean && npm run compile",
91
+ "build": "pnpm run clean && pnpm run compile",
92
92
  "test": "vitest run",
93
93
  "test:update": "vitest run --update",
94
94
  "test:watch": "vitest",
@@ -20,7 +20,7 @@ import { useThemeHooks } from '@redocly/theme/core/hooks';
20
20
  import { CatalogEntitySchema } from '@redocly/theme/components/Catalog/CatalogEntity/CatalogEntitySchema';
21
21
  import { CatalogEntityMethodAndPath } from '@redocly/theme/components/Catalog/CatalogEntity/CatalogEntityMethodAndPath';
22
22
  import { CatalogEntityRelationsGraph } from '@redocly/theme/components/Catalog/CatalogEntity/CatalogEntityGraph/CatalogEntityRelationsGraph.lazy';
23
- import { CatalogEntityHistorySidebar } from '@redocly/theme';
23
+ import { CatalogEntityHistorySidebar } from '@redocly/theme/components/Catalog/CatalogEntity/CatalogEntityHistory/CatalogEntityHistorySidebar';
24
24
 
25
25
  export type CatalogEntityProps = {
26
26
  /* eslint-disable-next-line @typescript-eslint/no-explicit-any */
@@ -7,8 +7,7 @@ import { useThemeHooks } from '@redocly/theme/core/hooks';
7
7
  import { MenuContainer } from '@redocly/theme/components/Menu/MenuContainer';
8
8
  import { transformRevisionsToVersionHistory } from '@redocly/theme/core/utils';
9
9
  import { DEFAULT_LOCALE_PLACEHOLDER } from '@redocly/theme/core/constants';
10
-
11
- import { CatalogEntityVersionItem } from './CatalogEntityVersionItem';
10
+ import { CatalogEntityVersionItem } from '@redocly/theme/components/Catalog/CatalogEntity/CatalogEntityHistory/CatalogEntityVersionItem';
12
11
 
13
12
  export type CatalogHistorySidebarProps = {
14
13
  entityKey: string;
@@ -2,7 +2,7 @@ import React, { JSX } from 'react';
2
2
 
3
3
  import { BffCatalogEntity } from '@redocly/theme/core/types';
4
4
  import { TagsIcon } from '@redocly/theme/icons/TagsIcon/TagsIcon';
5
- import { CatalogTagsWithTooltip } from '@redocly/theme';
5
+ import { CatalogTagsWithTooltip } from '@redocly/theme/components/Catalog/CatalogTagsWithTooltip';
6
6
  import { CatalogEntityPropertyCard } from '@redocly/theme/components/Catalog/CatalogEntity/CatalogEntityProperties/CatalogEntityPropertyCard';
7
7
  import { useThemeHooks } from '@redocly/theme/core/hooks';
8
8
 
@@ -10,8 +10,7 @@ import { CatalogEntityCell } from '@redocly/theme/components/Catalog/CatalogTabl
10
10
  import { CatalogTagsCell } from '@redocly/theme/components/Catalog/CatalogTableView/CatalogTagsCell';
11
11
  import { useCatalogEntityDetails } from '@redocly/theme/core/hooks';
12
12
  import { CatalogEntityTypeTag } from '@redocly/theme/components/Catalog/CatalogEntityTypeTag';
13
-
14
- import { Link } from '../../Link/Link';
13
+ import { Link } from '@redocly/theme/components/Link/Link';
15
14
 
16
15
  export type BaseEntity = {
17
16
  id: string;
@@ -5,8 +5,7 @@ import type { JSX } from 'react';
5
5
  import type { CodeBlockControlsProps } from '@redocly/theme/components/CodeBlock/CodeBlockControls';
6
6
 
7
7
  import { CodeBlock } from '@redocly/theme/components/CodeBlock/CodeBlock';
8
-
9
- import { JsonValue } from './Helpers';
8
+ import { JsonValue } from '@redocly/theme/components/JsonViewer/Helpers';
10
9
 
11
10
  export type PanelType = 'request' | 'responses' | 'request-samples' | 'response-samples';
12
11
 
@@ -12,9 +12,8 @@ import { Markdown } from '@redocly/theme/components/Markdown/Markdown';
12
12
  import { DocumentIcon } from '@redocly/theme/icons/DocumentIcon/DocumentIcon';
13
13
  import { AiStarsIcon } from '@redocly/theme/icons/AiStarsIcon/AiStarsIcon';
14
14
  import { CheckmarkOutlineIcon } from '@redocly/theme/icons/CheckmarkOutlineIcon/CheckmarkOutlineIcon';
15
-
16
- import { SearchAiActionButtons } from './SearchAiActionButtons';
17
- import { SearchAiNegativeFeedbackForm } from './SearchAiNegativeFeedbackForm';
15
+ import { SearchAiActionButtons } from '@redocly/theme/components/Search/SearchAiActionButtons';
16
+ import { SearchAiNegativeFeedbackForm } from '@redocly/theme/components/Search/SearchAiNegativeFeedbackForm';
18
17
 
19
18
  export type SearchAiMessageProps = {
20
19
  role: AiSearchConversationRole;
@@ -16,6 +16,7 @@ export const useThemeHooks = vi.fn(() => ({
16
16
  send: vi.fn(),
17
17
  sendCodeSnippetReportedMessage: vi.fn(),
18
18
  sendPageActionsButtonClickedMessage: vi.fn(),
19
+ sendColorModeSwitchedMessage: vi.fn(),
19
20
  })),
20
21
  useBreadcrumbs: vi.fn().mockReturnValue({ breadcrumbs: [], siblings: undefined }),
21
22
  useBanner: vi.fn(() => ({
@@ -280,9 +280,10 @@ function shouldHidePageActions(
280
280
  }
281
281
 
282
282
  // Page is excluded from search
283
+ const isOpenApiPage =
284
+ pageProps?.metadata?.type === 'openapi' || pageProps?.metadata?.subType === 'openapi-operation';
283
285
  const isPageExcludedFromSearch =
284
- pageProps?.frontmatter?.excludeFromSearch ||
285
- (pageProps?.metadata?.type === 'openapi' && openapiExcludeFromSearch);
286
+ pageProps?.frontmatter?.excludeFromSearch || (isOpenApiPage && openapiExcludeFromSearch);
286
287
 
287
288
  if (isPageExcludedFromSearch) {
288
289
  return true;
@@ -0,0 +1,12 @@
1
+ import { useId } from 'react';
2
+
3
+ /**
4
+ * Returns a function that appends a per-component-instance suffix to SVG ids.
5
+ * This prevents collisions when multiple identical SVGs are rendered on the same page,
6
+ * which can break `url(#...)` references (gradients, clipPath, masks, filters) on reflow.
7
+ */
8
+ export function useUniqueSvgIds(): (id: string) => string {
9
+ const reactId = useId();
10
+ const safeSuffix = reactId.replace(/:/g, '_');
11
+ return (id: string): string => `${id}-${safeSuffix}`;
12
+ }
@@ -38,3 +38,4 @@ export { SecurityVariablesEnvSuffix } from '../constants/environments';
38
38
  export { isUndefined, isString, isNotNull, isObject } from '../utils/type-guards';
39
39
  export { ThemeDataContext, type ThemeDataTransferObject } from '../contexts/ThemeDataContext';
40
40
  export { SearchSessionProvider, SearchSessionContext } from '../contexts/SearchContext';
41
+ export { useUniqueSvgIds } from '../hooks/use-unique-svg-ids';
@@ -971,23 +971,41 @@ const pages = css`
971
971
  */
972
972
 
973
973
  --page-403-font-family: var(--font-family-base); // @presenter FontFamily
974
+ --page-403-margin-vertical: var(--spacing-xl); // @presenter Spacing
975
+ --page-403-margin-horizontal: calc(var(--spacing-xxl) * 2); // @presenter Spacing
976
+ --page-403-gap: var(--spacing-lg); // @presenter Spacing
977
+ --page-403-max-width: 680px; // @presenter Width
978
+
979
+ --page-403-status-text-color: var(--text-color-helper); // @presenter Color
980
+ --page-403-status-font-size: var(--font-size-lg); // @presenter FontSize
981
+ --page-403-status-font-weight: var(--font-weight-semibold); // @presenter FontWeight
982
+ --page-403-status-line-height: var(--line-height-lg); // @presenter LineHeight
983
+
984
+ --page-403-title-text-color: var(--text-color-primary); // @presenter Color
985
+ --page-403-title-font-size: 42px; // @presenter FontSize
986
+ --page-403-title-font-weight: var(--font-weight-bold); // @presenter FontWeight
987
+ --page-403-title-line-height: 50px; // @presenter LineHeight
988
+
989
+ --page-403-description-text-color: var(--text-color-secondary); // @presenter Color
990
+ --page-403-description-font-size: var(--font-size-xl); // @presenter FontSize
991
+ --page-403-description-font-weight: var(--font-weight-regular); // @presenter FontWeight
992
+ --page-403-description-line-height: var(--line-height-xl); // @presenter LineHeight
993
+
994
+ // @tokens End
974
995
 
975
- --page-403-header-text-color: var(--h1-text-color);
996
+ /**
997
+ * @tokens 403 Page OIDC Forbidden
998
+ * @presenter Color
999
+ */
1000
+
1001
+ --page-403-header-text-color: var(--h1-text-color); // @presenter Color
976
1002
  --page-403-header-font-size: var(--h1-font-size); // @presenter FontSize
977
1003
  --page-403-header-font-weight: var(--h1-font-weight); // @presenter FontWeight
978
1004
  --page-403-header-line-height: var(--h1-line-height); // @presenter LineHeight
979
1005
  --page-403-header-margin: 0; // @presenter Spacing
980
-
981
- --page-403-description-text-color: var(--text-color-secondary);
982
- --page-403-description-font-size: 1.5em; // @presenter FontSize
983
- --page-403-description-font-weight: var(--font-weight-regular); // @presenter FontWeight
984
- --page-403-description-line-height: 1; // @presenter LineHeight
985
1006
  --page-403-description-margin: 0; // @presenter Spacing
986
-
987
- --page-403-button-margin: 4em; // @presenter Spacing
988
-
989
- --page-403-oidc-description-font-size: var(--font-size-lg);
990
- --page-403-oidc-description-margin: var(--spacing-md) var(--spacing-sm);
1007
+ --page-403-oidc-description-font-size: var(--font-size-lg); // @presenter FontSize
1008
+ --page-403-oidc-description-margin: var(--spacing-md) var(--spacing-sm); // @presenter Spacing
991
1009
 
992
1010
  // @tokens End
993
1011
 
@@ -116,6 +116,7 @@ export type TranslationKey =
116
116
  | 'footer.copyrightText'
117
117
  | 'page.homeButton'
118
118
  | 'page.forbidden.title'
119
+ | 'page.forbidden.description'
119
120
  | 'page.notFound.title'
120
121
  | 'page.notFound.description'
121
122
  | 'page.lastUpdated.timeago'
package/src/index.ts CHANGED
@@ -302,6 +302,9 @@ export * from '@redocly/theme/layouts/OIDCForbidden';
302
302
  export * from '@redocly/theme/layouts/ThreePanelLayout';
303
303
  export * from '@redocly/theme/layouts/CodeWalkthroughLayout';
304
304
  export * from '@redocly/theme/layouts/InternalServerErrorLayout';
305
+ export * from '@redocly/theme/layouts/DocumentationLayout';
306
+ export * from '@redocly/theme/layouts/DocumentationLayoutTop';
307
+ export * from '@redocly/theme/layouts/DocumentationLayoutBottom';
305
308
  /* Markdoc */
306
309
  export * as markdoc from '@redocly/theme/markdoc/default';
307
310
  /* DatePicker */
@@ -4,12 +4,10 @@ import styled from 'styled-components';
4
4
  import type { JSX } from 'react';
5
5
  import type { ResolvedNavItemWithLink, MarkdownConfig } from '@redocly/config';
6
6
 
7
- import { EditPageButton } from '@redocly/theme/components/Buttons/EditPageButton';
8
7
  import { breakpoints } from '@redocly/theme/core/utils';
9
- import { PageNavigation } from '@redocly/theme/components/PageNavigation/PageNavigation';
10
- import { LastUpdated } from '@redocly/theme/components/LastUpdated/LastUpdated';
11
- import { Breadcrumbs as ThemeBreadcrumbs } from '@redocly/theme/components/Breadcrumbs/Breadcrumbs';
12
8
  import { CodeSnippetProvider } from '@redocly/theme/core/contexts/CodeSnippetContext';
9
+ import { DocumentationLayoutTop } from '@redocly/theme/layouts/DocumentationLayoutTop';
10
+ import { DocumentationLayoutBottom } from '@redocly/theme/layouts/DocumentationLayoutBottom';
13
11
 
14
12
  type DocumentationLayoutProps = {
15
13
  tableOfContent: React.ReactNode;
@@ -36,21 +34,13 @@ export function DocumentationLayout({
36
34
  className,
37
35
  children,
38
36
  }: React.PropsWithChildren<DocumentationLayoutProps>): JSX.Element {
39
- const { editPage: themeEditPage } = config || {};
40
- const mergedConf = editPage ? { ...themeEditPage, ...editPage } : undefined;
41
-
42
37
  return (
43
38
  <CodeSnippetProvider>
44
39
  <LayoutWrapper data-component-name="Layout/DocumentationLayout" className={className}>
45
40
  <ContentWrapper withToc={!config?.toc?.hide}>
46
- <Breadcrumbs />
47
- <LayoutTop>
48
- {lastModified && <LastUpdated lastModified={new Date(lastModified)} />}
49
- {mergedConf && <EditPageButton to={mergedConf.to} />}
50
- </LayoutTop>
41
+ <DocumentationLayoutTop config={config} editPage={editPage} lastModified={lastModified} />
51
42
  {children}
52
- <LayoutBottom>{feedback}</LayoutBottom>
53
- <PageNavigation nextPage={nextPage} prevPage={prevPage} />
43
+ <DocumentationLayoutBottom feedback={feedback} nextPage={nextPage} prevPage={prevPage} />
54
44
  </ContentWrapper>
55
45
  {tableOfContent}
56
46
  </LayoutWrapper>
@@ -89,19 +79,3 @@ const ContentWrapper = styled.section<{ withToc: boolean }>`
89
79
  width: ${({ withToc }) => (withToc ? `calc(90% - var(--toc-width))` : '90%')};
90
80
  }
91
81
  `;
92
-
93
- const LayoutTop = styled.div`
94
- display: flex;
95
- justify-content: space-between;
96
- flex-flow: row nowrap;
97
- `;
98
-
99
- const Breadcrumbs = styled(ThemeBreadcrumbs)`
100
- margin-bottom: var(--breadcrumbs-margin-bottom);
101
- `;
102
-
103
- const LayoutBottom = styled(LayoutTop)`
104
- > * {
105
- margin: 25px 0;
106
- }
107
- `;
@@ -0,0 +1,42 @@
1
+ import React from 'react';
2
+ import styled from 'styled-components';
3
+
4
+ import type { JSX } from 'react';
5
+ import type { ResolvedNavItemWithLink } from '@redocly/config';
6
+
7
+ import { PageNavigation } from '@redocly/theme/components/PageNavigation/PageNavigation';
8
+
9
+ type DocumentationLayoutBottomProps = {
10
+ feedback: React.ReactNode;
11
+ nextPage?: ResolvedNavItemWithLink | null;
12
+ prevPage?: ResolvedNavItemWithLink | null;
13
+ className?: string;
14
+ };
15
+
16
+ export function DocumentationLayoutBottom({
17
+ feedback,
18
+ nextPage,
19
+ prevPage,
20
+ }: React.PropsWithChildren<DocumentationLayoutBottomProps>): JSX.Element {
21
+ return (
22
+ <Wrapper data-component-name="Layout/DocumentationLayoutBottom">
23
+ <LayoutBottom>{feedback}</LayoutBottom>
24
+ <PageNavigation nextPage={nextPage} prevPage={prevPage} />
25
+ </Wrapper>
26
+ );
27
+ }
28
+
29
+ const Wrapper = styled.div`
30
+ display: flex;
31
+ flex-direction: column;
32
+ `;
33
+
34
+ const LayoutBottom = styled.div`
35
+ display: flex;
36
+ justify-content: space-between;
37
+ flex-flow: row nowrap;
38
+
39
+ > * {
40
+ margin: 25px 0;
41
+ }
42
+ `;
@@ -0,0 +1,52 @@
1
+ import React from 'react';
2
+ import styled from 'styled-components';
3
+
4
+ import type { JSX } from 'react';
5
+ import type { MarkdownConfig } from '@redocly/config';
6
+
7
+ import { EditPageButton } from '@redocly/theme/components/Buttons/EditPageButton';
8
+ import { LastUpdated } from '@redocly/theme/components/LastUpdated/LastUpdated';
9
+ import { Breadcrumbs as ThemeBreadcrumbs } from '@redocly/theme/components/Breadcrumbs/Breadcrumbs';
10
+
11
+ type DocumentationLayoutTopProps = {
12
+ config?: MarkdownConfig;
13
+ editPage?: {
14
+ to: string;
15
+ };
16
+ /** String in ISO format */
17
+ lastModified?: string | null;
18
+ };
19
+
20
+ export function DocumentationLayoutTop({
21
+ config,
22
+ editPage,
23
+ lastModified,
24
+ }: React.PropsWithChildren<DocumentationLayoutTopProps>): JSX.Element {
25
+ const { editPage: themeEditPage } = config || {};
26
+ const mergedConf = editPage ? { ...themeEditPage, ...editPage } : undefined;
27
+
28
+ return (
29
+ <Wrapper data-component-name="Layout/DocumentationLayoutTop">
30
+ <Breadcrumbs />
31
+ <LayoutTop>
32
+ {lastModified && <LastUpdated lastModified={new Date(lastModified)} />}
33
+ {mergedConf && <EditPageButton to={mergedConf.to} />}
34
+ </LayoutTop>
35
+ </Wrapper>
36
+ );
37
+ }
38
+
39
+ const Wrapper = styled.div`
40
+ display: flex;
41
+ flex-direction: column;
42
+ `;
43
+
44
+ const LayoutTop = styled.div`
45
+ display: flex;
46
+ justify-content: space-between;
47
+ flex-flow: row nowrap;
48
+ `;
49
+
50
+ const Breadcrumbs = styled(ThemeBreadcrumbs)`
51
+ margin-bottom: var(--breadcrumbs-margin-bottom);
52
+ `;
@@ -5,50 +5,65 @@ import type { JSX } from 'react';
5
5
 
6
6
  import { useThemeHooks } from '@redocly/theme/core/hooks';
7
7
  import { Button } from '@redocly/theme/components/Button/Button';
8
+ import { ArrowLeftIcon } from '@redocly/theme/icons/ArrowLeftIcon/ArrowLeftIcon';
8
9
 
9
10
  export function Forbidden(): JSX.Element {
10
11
  const { useTranslate } = useThemeHooks();
11
12
  const { translate } = useTranslate();
12
13
 
13
14
  return (
14
- <Wrapper data-component-name="Pages/Forbidden">
15
- <Header>403</Header>
16
- <Description data-translation-key="page.forbidden.title">
15
+ <ForbiddenWrapper data-component-name="layouts/Forbidden">
16
+ <StatusText>403</StatusText>
17
+ <Title data-translation-key="page.forbidden.title">
17
18
  {translate('page.forbidden.title', 'Access forbidden')}
19
+ </Title>
20
+ <Description data-translation-key="page.forbidden.description">
21
+ {translate(
22
+ 'page.forbidden.description',
23
+ "You don't have permission to access this page. If you believe this is an error, contact your administrator or return to the homepage.",
24
+ )}
18
25
  </Description>
19
- <HomeButton variant="primary" size="large" to="/" data-translation-key="page.homeButton">
26
+ <Button
27
+ variant="primary"
28
+ size="large"
29
+ to="/"
30
+ data-translation-key="page.homeButton"
31
+ icon={<ArrowLeftIcon />}
32
+ >
20
33
  {translate('page.homeButton', 'Go home')}
21
- </HomeButton>
22
- </Wrapper>
34
+ </Button>
35
+ </ForbiddenWrapper>
23
36
  );
24
37
  }
25
38
 
26
- const Wrapper = styled.div`
39
+ const ForbiddenWrapper = styled.div`
40
+ height: 100%;
41
+ max-width: var(--page-403-max-width);
27
42
  display: flex;
28
43
  flex-direction: column;
29
- align-items: center;
30
44
  justify-content: center;
31
- margin: 25px auto;
45
+ margin: var(--page-403-margin-vertical) var(--page-403-margin-horizontal);
32
46
  font-family: var(--page-403-font-family);
33
- text-align: center;
47
+ gap: var(--page-403-gap);
34
48
  `;
35
49
 
36
- const Header = styled.div`
37
- color: var(--page-403-header-text-color);
38
- margin: var(--page-403-header-margin);
39
- font-size: var(--page-403-header-font-size);
40
- line-height: var(--page-403-header-line-height);
41
- font-weight: var(--page-403-header-font-weight);
50
+ const StatusText = styled.div`
51
+ color: var(--page-403-status-text-color);
52
+ font-size: var(--page-403-status-font-size);
53
+ line-height: var(--page-403-status-line-height);
54
+ font-weight: var(--page-403-status-font-weight);
55
+ `;
56
+
57
+ const Title = styled.div`
58
+ color: var(--page-403-title-text-color);
59
+ font-size: var(--page-403-title-font-size);
60
+ line-height: var(--page-403-title-line-height);
61
+ font-weight: var(--page-403-title-font-weight);
42
62
  `;
43
63
 
44
64
  const Description = styled.div`
45
65
  color: var(--page-403-description-text-color);
46
- margin: var(--page-403-description-margin);
47
66
  font-size: var(--page-403-description-font-size);
48
67
  line-height: var(--page-403-description-line-height);
49
68
  font-weight: var(--page-403-description-font-weight);
50
69
  `;
51
-
52
- const HomeButton = styled(Button)`
53
- margin-top: var(--page-403-button-margin);
54
- `;