diff --git a/.changeset/all-camels-agree.md b/.changeset/all-camels-agree.md new file mode 100644 index 0000000000..44d8a4f2c9 --- /dev/null +++ b/.changeset/all-camels-agree.md @@ -0,0 +1,5 @@ +--- +'@backstage/ui': patch +--- + +Fix broken external links in Backstage UI Header component. diff --git a/.changeset/all-parrots-change.md b/.changeset/all-parrots-change.md new file mode 100644 index 0000000000..65d5c37658 --- /dev/null +++ b/.changeset/all-parrots-change.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-org': patch +--- + +Improved responsiveness of GroupProfileCard component diff --git a/.changeset/better-hats-cross.md b/.changeset/better-hats-cross.md new file mode 100644 index 0000000000..99444b7fbf --- /dev/null +++ b/.changeset/better-hats-cross.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-defaults': patch +--- + +Adjusted the log line wording of task worker starting diff --git a/.changeset/better-steaks-act.md b/.changeset/better-steaks-act.md new file mode 100644 index 0000000000..b3ce601245 --- /dev/null +++ b/.changeset/better-steaks-act.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': patch +--- + +The templates executed with the `yarn new` command now supports templating filenames. diff --git a/.changeset/brave-teeth-reply.md b/.changeset/brave-teeth-reply.md deleted file mode 100644 index 8a500692a7..0000000000 --- a/.changeset/brave-teeth-reply.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog-backend': patch ---- - -Internal refactor to remove remnants of the old backend system diff --git a/.changeset/breezy-times-ring.md b/.changeset/breezy-times-ring.md new file mode 100644 index 0000000000..f65dd6ff55 --- /dev/null +++ b/.changeset/breezy-times-ring.md @@ -0,0 +1,5 @@ +--- +'@backstage/repo-tools': patch +--- + +Updated knip-reports to detect dependencies in dev/alpha pattern diff --git a/.changeset/bright-ears-send.md b/.changeset/bright-ears-send.md new file mode 100644 index 0000000000..5104f2377b --- /dev/null +++ b/.changeset/bright-ears-send.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend': minor +--- + +Enable YAML merge keys in yamlPlaceholderResolver diff --git a/.changeset/brown-falcons-own.md b/.changeset/brown-falcons-own.md deleted file mode 100644 index e38f3bbfbc..0000000000 --- a/.changeset/brown-falcons-own.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-search-backend-module-pg': patch ---- - -Truncate long docs to fit PG index size limit diff --git a/.changeset/busy-goats-create.md b/.changeset/busy-goats-create.md new file mode 100644 index 0000000000..78b8f65571 --- /dev/null +++ b/.changeset/busy-goats-create.md @@ -0,0 +1,10 @@ +--- +'@backstage/plugin-search-backend-module-elasticsearch': patch +'@backstage/plugin-kubernetes-backend': patch +'@backstage/plugin-techdocs-backend': patch +'@backstage/plugin-signals-backend': patch +'@backstage/plugin-techdocs': patch +'@backstage/plugin-signals': patch +--- + +Removed/moved unused dependencies diff --git a/.changeset/calm-trains-tie.md b/.changeset/calm-trains-tie.md deleted file mode 100644 index 8c399ba123..0000000000 --- a/.changeset/calm-trains-tie.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/backend-defaults': minor ---- - -implement support for direct url for AzureBlobStorageUrlReader search function diff --git a/.changeset/clever-boats-clap.md b/.changeset/clever-boats-clap.md new file mode 100644 index 0000000000..6b40f05a76 --- /dev/null +++ b/.changeset/clever-boats-clap.md @@ -0,0 +1,8 @@ +--- +'@backstage/plugin-search-react': minor +'@backstage/plugin-techdocs': minor +'@backstage/plugin-catalog': minor +'@backstage/plugin-search': minor +--- + +Add support for customizable icons in `SearchResultListItemBlueprint` and related components diff --git a/.changeset/clever-papers-watch.md b/.changeset/clever-papers-watch.md deleted file mode 100644 index 831d4c3455..0000000000 --- a/.changeset/clever-papers-watch.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/ui': patch ---- - -remove default selection of tab diff --git a/.changeset/cold-coats-show.md b/.changeset/cold-coats-show.md deleted file mode 100644 index a048ec308c..0000000000 --- a/.changeset/cold-coats-show.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog-backend': patch ---- - -Fix for duplicate search results in entity-facets API call diff --git a/.changeset/cool-baboons-count.md b/.changeset/cool-baboons-count.md deleted file mode 100644 index c5dac494d9..0000000000 --- a/.changeset/cool-baboons-count.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog-graph': patch ---- - -Add missing API implementation for catalog graph plugin in NFS apps. diff --git a/.changeset/create-app-1758639549.md b/.changeset/create-app-1761312116.md similarity index 100% rename from .changeset/create-app-1758639549.md rename to .changeset/create-app-1761312116.md diff --git a/.changeset/create-app-1758718573.md b/.changeset/create-app-1762897504.md similarity index 100% rename from .changeset/create-app-1758718573.md rename to .changeset/create-app-1762897504.md diff --git a/.changeset/cruel-items-dig.md b/.changeset/cruel-items-dig.md new file mode 100644 index 0000000000..65f07956ac --- /dev/null +++ b/.changeset/cruel-items-dig.md @@ -0,0 +1,27 @@ +--- +'@backstage/ui': minor +--- + +**BREAKING**: Migrated Avatar component from Base UI to custom implementation with size changes: + +- Base UI-specific props are no longer supported +- Size values have been updated: + - New `x-small` size added (1.25rem / 20px) + - `small` size unchanged (1.5rem / 24px) + - `medium` size unchanged (2rem / 32px, default) + - `large` size **changed from 3rem to 2.5rem** (40px) + - New `x-large` size added (3rem / 48px) + +Migration: + +```diff +# Remove Base UI-specific props +- ++ + +# Update large size usage to x-large for same visual size +- ++ +``` + +Added `purpose` prop for accessibility control (`'informative'` or `'decoration'`). diff --git a/.changeset/cruel-plums-talk.md b/.changeset/cruel-plums-talk.md new file mode 100644 index 0000000000..bc7a30b048 --- /dev/null +++ b/.changeset/cruel-plums-talk.md @@ -0,0 +1,58 @@ +--- +'@backstage/ui': minor +--- + +**BREAKING**: Migrated Checkbox component from Base UI to React Aria Components. + +API changes required: + +- `checked` → `isSelected` +- `defaultChecked` → `defaultSelected` +- `disabled` → `isDisabled` +- `required` → `isRequired` +- `label` prop removed - use `children` instead +- CSS: `bui-CheckboxLabel` class removed +- Data attribute: `data-checked` → `data-selected` +- Use without label is no longer supported + +Migration examples: + +Before: + +```tsx + +``` + +After: + +```tsx + + Accept terms + +``` + +Before: + +```tsx + +``` + +After: + +```tsx +Option +``` + +Before: + +```tsx + +``` + +After: + +```tsx + + Accessible label + +``` diff --git a/.changeset/deep-gifts-slide.md b/.changeset/deep-gifts-slide.md new file mode 100644 index 0000000000..f3be77e74d --- /dev/null +++ b/.changeset/deep-gifts-slide.md @@ -0,0 +1,5 @@ +--- +'@backstage/ui': patch +--- + +Fixed CSS issues in Select component including popover width constraints, focus outline behavior, and overflow handling. diff --git a/.changeset/eager-toes-start.md b/.changeset/eager-toes-start.md deleted file mode 100644 index f36cb1bb0a..0000000000 --- a/.changeset/eager-toes-start.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog-react': patch ---- - -Correct translation key from "type" to "owner" for owner column in entity table to ensure the right translation is loaded. diff --git a/.changeset/easy-hands-grow.md b/.changeset/easy-hands-grow.md new file mode 100644 index 0000000000..fc3cfd96e1 --- /dev/null +++ b/.changeset/easy-hands-grow.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-catalog-backend': minor +'@backstage/plugin-catalog-node': minor +--- + +Make YAML merge (<<:) support configurable in the Backstage Catalog instead of always being enabled diff --git a/.changeset/eighty-mails-leave.md b/.changeset/eighty-mails-leave.md new file mode 100644 index 0000000000..3c6ec5ab8f --- /dev/null +++ b/.changeset/eighty-mails-leave.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-home': patch +--- + +Allow customization of VisitList by adding optional enrichVisit, transformPathname, canSave functions to VisitsStorageApi, along with VisitDisplayProvider for colors, labels diff --git a/.changeset/eighty-results-prove.md b/.changeset/eighty-results-prove.md new file mode 100644 index 0000000000..6df2d2f11a --- /dev/null +++ b/.changeset/eighty-results-prove.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-app-visualizer': patch +--- + +Improve tree visualizer to use a horizontal layout and fill the content space. diff --git a/.changeset/eleven-carpets-win.md b/.changeset/eleven-carpets-win.md new file mode 100644 index 0000000000..69d677786a --- /dev/null +++ b/.changeset/eleven-carpets-win.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-defaults': patch +--- + +Fix a bug in the Gitlab URL reader where `search` did not handle multiple globs diff --git a/.changeset/eleven-lights-taste.md b/.changeset/eleven-lights-taste.md new file mode 100644 index 0000000000..e575b909e6 --- /dev/null +++ b/.changeset/eleven-lights-taste.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-react': patch +--- + +Added missing i18n diff --git a/.changeset/every-ants-count.md b/.changeset/every-ants-count.md new file mode 100644 index 0000000000..963c3a521a --- /dev/null +++ b/.changeset/every-ants-count.md @@ -0,0 +1,7 @@ +--- +'@backstage/backend-test-utils': minor +--- + +Added support for Postgres 18 to the available `TestDatabases`. + +Note that the set of _default_ databases to test against for users of the `TestDatabases` class was also updated to include Postgres 14 and 18, instead of 13 and 17. If you need to override this, you can pass in an explicit `ids` argument, for example `ids: ['POSTGRES_17', 'POSTGRES_13', 'SQLITE_3']`. diff --git a/.changeset/every-clocks-arrive.md b/.changeset/every-clocks-arrive.md new file mode 100644 index 0000000000..d2061913e3 --- /dev/null +++ b/.changeset/every-clocks-arrive.md @@ -0,0 +1,5 @@ +--- +'@backstage/dev-utils': patch +--- + +Fixed styling of the dev app by adding a lazy import of `@backstage/ui/css/styles.css`. diff --git a/.changeset/fancy-years-camp.md b/.changeset/fancy-years-camp.md new file mode 100644 index 0000000000..41782d4e0c --- /dev/null +++ b/.changeset/fancy-years-camp.md @@ -0,0 +1,9 @@ +--- +'@backstage/plugin-notifications-backend': minor +'@backstage/plugin-notifications-common': minor +--- + +Adds support for default configuration for an entire notification channel. +This setting will also be inherited down to origins and topics while still respecting the users individual choices. + +This will be handy if you want to use a "opt-in" strategy. diff --git a/.changeset/fast-heads-brake.md b/.changeset/fast-heads-brake.md deleted file mode 100644 index bc9f525520..0000000000 --- a/.changeset/fast-heads-brake.md +++ /dev/null @@ -1,25 +0,0 @@ ---- -'@backstage/plugin-catalog-backend': patch ---- - -Moved catalog processor and provider disabling and priorities under own config objects. - -This is due to issue with some existing providers, such as GitHub, using array syntax for the provider configuration. - -The new config format is not backwards compatible, so users will need to update their config files. The new format -is as follows: - -```yaml -catalog: - providerOptions: - providerA: - disabled: false - providerB: - disabled: true - processorOptions: - processorA: - disabled: false - priority: 10 - processorB: - disabled: true -``` diff --git a/.changeset/fast-queens-guess.md b/.changeset/fast-queens-guess.md deleted file mode 100644 index b2e5086e9e..0000000000 --- a/.changeset/fast-queens-guess.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/core-components': patch ---- - -Swap base token for semantic token in ItemCardHeader to ensure readability in light mode. diff --git a/.changeset/fast-tables-sink.md b/.changeset/fast-tables-sink.md new file mode 100644 index 0000000000..a7c240fc5d --- /dev/null +++ b/.changeset/fast-tables-sink.md @@ -0,0 +1,5 @@ +--- +'@backstage/ui': patch +--- + +Improved visual consistency of PasswordField, SearchField, and MenuAutocomplete components. diff --git a/.changeset/fast-tools-mate.md b/.changeset/fast-tools-mate.md new file mode 100644 index 0000000000..2e212ce1b3 --- /dev/null +++ b/.changeset/fast-tools-mate.md @@ -0,0 +1,10 @@ +--- +'@backstage/plugin-notifications-backend': patch +--- + +Show default settings for notifications even before receiving first notification. + +Previously, it was not possible for the users to see or modify their notification settings until they had received at +least one notification from specific origin or topic. +This update ensures that default settings are displayed from the outset, +allowing users to customize their preferences immediately. diff --git a/.changeset/fine-hands-return.md b/.changeset/fine-hands-return.md new file mode 100644 index 0000000000..d3bf69c9b9 --- /dev/null +++ b/.changeset/fine-hands-return.md @@ -0,0 +1,5 @@ +--- +'@backstage/ui': patch +--- + +Fix default text color in Backstage UI diff --git a/.changeset/five-olives-bet.md b/.changeset/five-olives-bet.md deleted file mode 100644 index 8623e780b5..0000000000 --- a/.changeset/five-olives-bet.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/integration': patch ---- - -remove host from azure blob storage integration type diff --git a/.changeset/five-seas-jam.md b/.changeset/five-seas-jam.md new file mode 100644 index 0000000000..4aa481422f --- /dev/null +++ b/.changeset/five-seas-jam.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-notifications-backend-module-email': patch +--- + +Updated `AWS SES` client to version 2 to support `nodemailer` version 7. diff --git a/.changeset/fix-text-truncate-prop.md b/.changeset/fix-text-truncate-prop.md new file mode 100644 index 0000000000..26aee3280c --- /dev/null +++ b/.changeset/fix-text-truncate-prop.md @@ -0,0 +1,5 @@ +--- +'@backstage/ui': patch +--- + +Fixed Text component to prevent `truncate` prop from being spread to the underlying DOM element. diff --git a/.changeset/flat-paws-do.md b/.changeset/flat-paws-do.md new file mode 100644 index 0000000000..13d78c1423 --- /dev/null +++ b/.changeset/flat-paws-do.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend-module-openapi': patch +--- + +fix wrong dereferencing for AsyncApi 3 documents diff --git a/.changeset/fruity-snails-laugh.md b/.changeset/fruity-snails-laugh.md new file mode 100644 index 0000000000..3b7775efac --- /dev/null +++ b/.changeset/fruity-snails-laugh.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend-module-gitlab': minor +--- + +fix scaffolder action createDeployToken to allow usage of oauth tokens diff --git a/.changeset/funny-stars-open.md b/.changeset/funny-stars-open.md new file mode 100644 index 0000000000..336e68bac1 --- /dev/null +++ b/.changeset/funny-stars-open.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': patch +--- + +Fix inconsistent behavior in the `new` command for the `@internal` scope: it now consistently defaults to the `backstage-plugin-` infix whether the `--scope` option is not set or it's set to `internal`. diff --git a/.changeset/gentle-bikes-relax.md b/.changeset/gentle-bikes-relax.md new file mode 100644 index 0000000000..7a4aeda5f8 --- /dev/null +++ b/.changeset/gentle-bikes-relax.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-techdocs-module-addons-contrib': patch +--- + +ExpandableCollapse Techdocs Addon was breaking native sidebar collapse on Firefox diff --git a/.changeset/giant-lamps-happen.md b/.changeset/giant-lamps-happen.md new file mode 100644 index 0000000000..1bc4ae6eab --- /dev/null +++ b/.changeset/giant-lamps-happen.md @@ -0,0 +1,5 @@ +--- +'@backstage/ui': patch +--- + +Improved the Link component structure in Backstage UI. diff --git a/.changeset/grumpy-planes-bet.md b/.changeset/grumpy-planes-bet.md new file mode 100644 index 0000000000..bf7d36be41 --- /dev/null +++ b/.changeset/grumpy-planes-bet.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-react': patch +--- + +Fixed a bug in the Scaffolder's template parsing in the `useTemplateSchema` hook by removing the title instead of setting it to `undefined` diff --git a/.changeset/heavy-cars-wash.md b/.changeset/heavy-cars-wash.md new file mode 100644 index 0000000000..87da5a6d95 --- /dev/null +++ b/.changeset/heavy-cars-wash.md @@ -0,0 +1,70 @@ +--- +'@backstage/plugin-catalog-backend-module-ldap': minor +--- + +Moved from `ldapjs` dependency to `ldapts` + +### Breaking Changes + +**Type Migration** + +Custom transformers must now accept `Entry` from ldapts instead of `SearchEntry` +from ldapjs The Entry type provides direct property access without need for +`.object()` or `.raw()` methods. + +If you have custom user or group transformers, update the signature from: + +```typescript +(vendor: LdapVendor, config: UserConfig, entry: SearchEntry) => + Promise; +``` + +to + +```typescript +(vendor: LdapVendor, config: UserConfig, entry: Entry) => + Promise; +``` + +**Search Options** + +Updated LDAP search configuration `typesOnly: false` → `attributeValues: true` +This inverts the boolean logic: ldapjs used negative form while ldapts uses +positive form. Both achieve the same result: retrieving attribute values rather +than just attribute names. + +Update LDAP search options in configuration from + +```yaml +options: + typesOnly: false +``` + +to + +```yaml +options: + attributeValues: true +``` + +**API Changes** Removed `LdapClient.searchStreaming()` method. Users should +migrate to `LdapClient.search()` instead + +If you're using `searchStreaming` directly: + +```typescript +// Before +await client.searchStreaming(dn, options, async entry => { + // process each entry +}); + +// After +const entries = await client.search(dn, options); +for (const entry of entries) { + // process each entry +} +``` + +> **_NOTE:_**: Both methods have always loaded all entries into memory. The +> searchStreaming method was only needed internally to handle ldapjs's +> event-based API. diff --git a/.changeset/honest-lamps-occur.md b/.changeset/honest-lamps-occur.md new file mode 100644 index 0000000000..27b2eb44f5 --- /dev/null +++ b/.changeset/honest-lamps-occur.md @@ -0,0 +1,5 @@ +--- +'@backstage/ui': patch +--- + +Fixed dialog backdrop appearance in dark mode. diff --git a/.changeset/honest-pandas-win.md b/.changeset/honest-pandas-win.md new file mode 100644 index 0000000000..a9dc5d7e9d --- /dev/null +++ b/.changeset/honest-pandas-win.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-plugin-api': minor +--- + +Promote `instanceMetadata` service to main entrypoint. diff --git a/.changeset/huge-taxis-grab.md b/.changeset/huge-taxis-grab.md new file mode 100644 index 0000000000..a021fcb82c --- /dev/null +++ b/.changeset/huge-taxis-grab.md @@ -0,0 +1,5 @@ +--- +'@backstage/ui': patch +--- + +Migrated CellProfile component from Base UI Avatar to Backstage UI Avatar component. diff --git a/.changeset/hungry-crews-fetch.md b/.changeset/hungry-crews-fetch.md deleted file mode 100644 index a9728e9cb0..0000000000 --- a/.changeset/hungry-crews-fetch.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/cli': patch ---- - -Fixed module federation config by only setting `import: false` on shared libraries for remote. diff --git a/.changeset/itchy-bars-smell.md b/.changeset/itchy-bars-smell.md new file mode 100644 index 0000000000..b82779502e --- /dev/null +++ b/.changeset/itchy-bars-smell.md @@ -0,0 +1,5 @@ +--- +'@backstage/ui': minor +--- + +Fixing styles on SearchField in Backstage UI after migration to CSS modules. `SearchField` has now its own set of class names. We previously used class names from `TextField` but this approach was creating some confusion so going forward in your theme you'll be able to theme `TextField` and `SearchField` separately. diff --git a/.changeset/itchy-falcons-leave.md b/.changeset/itchy-falcons-leave.md deleted file mode 100644 index 07188e08c5..0000000000 --- a/.changeset/itchy-falcons-leave.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend-module-gcp': patch ---- - -Fix documentation strings to mention GCP instead of Azure diff --git a/.changeset/kind-places-reply.md b/.changeset/kind-places-reply.md deleted file mode 100644 index 4b1d361e6e..0000000000 --- a/.changeset/kind-places-reply.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-kubernetes-common': patch ---- - -Make SERVICEACCOUNT_CA_PATH public so it can be imported by external modules. diff --git a/.changeset/legal-eagles-jog.md b/.changeset/legal-eagles-jog.md deleted file mode 100644 index 5b520af91f..0000000000 --- a/.changeset/legal-eagles-jog.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend-module-confluence-to-markdown': patch -'@backstage/plugin-scaffolder-backend-module-bitbucket-server': patch -'@backstage/plugin-scaffolder-backend-module-bitbucket-cloud': patch -'@backstage/plugin-scaffolder-backend-module-notifications': patch -'@backstage/plugin-scaffolder-backend-module-cookiecutter': patch -'@backstage/plugin-scaffolder-backend-module-bitbucket': patch -'@backstage/plugin-scaffolder-backend-module-gerrit': patch -'@backstage/plugin-scaffolder-backend-module-github': patch -'@backstage/plugin-scaffolder-backend-module-gitlab': patch -'@backstage/plugin-scaffolder-backend-module-sentry': patch -'@backstage/plugin-scaffolder-backend-module-yeoman': patch -'@backstage/plugin-scaffolder-backend-module-azure': patch -'@backstage/plugin-scaffolder-backend-module-gitea': patch -'@backstage/plugin-scaffolder-backend-module-rails': patch ---- - -Updating import for the `scaffolderActionsExtensionPoint` to be the main export diff --git a/.changeset/legal-weeks-walk.md b/.changeset/legal-weeks-walk.md new file mode 100644 index 0000000000..2068ff40b2 --- /dev/null +++ b/.changeset/legal-weeks-walk.md @@ -0,0 +1,5 @@ +--- +'@backstage/create-app': patch +--- + +Added `@backstage/plugin-app-visualizer` to the app in the `--next` template. diff --git a/.changeset/lemon-spies-sleep.md b/.changeset/lemon-spies-sleep.md new file mode 100644 index 0000000000..15ec451f62 --- /dev/null +++ b/.changeset/lemon-spies-sleep.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-react': patch +--- + +Apply default ordering of templates diff --git a/.changeset/long-humans-sink.md b/.changeset/long-humans-sink.md new file mode 100644 index 0000000000..9959caf1b1 --- /dev/null +++ b/.changeset/long-humans-sink.md @@ -0,0 +1,5 @@ +--- +'@backstage/ui': patch +--- + +Avatar components in x-small and small sizes now display only one initial instead of two, improving readability at smaller dimensions. diff --git a/.changeset/long-impalas-burn.md b/.changeset/long-impalas-burn.md new file mode 100644 index 0000000000..ef70f65f78 --- /dev/null +++ b/.changeset/long-impalas-burn.md @@ -0,0 +1,5 @@ +--- +'@backstage/repo-tools': minor +--- + +The `package-docs` command will now automatically use a `typedoc.json` file if one exists at the root of your project. diff --git a/.changeset/loud-carpets-throw.md b/.changeset/loud-carpets-throw.md new file mode 100644 index 0000000000..169703d815 --- /dev/null +++ b/.changeset/loud-carpets-throw.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': patch +--- + +Added a template for the `yarn new` command to create an catalog entity provider. To add this template to an explicit list in the root `package.json`, use `@backstage/cli/templates/catalog-provider-module`. diff --git a/.changeset/moody-plums-add.md b/.changeset/moody-plums-add.md new file mode 100644 index 0000000000..a88e11b347 --- /dev/null +++ b/.changeset/moody-plums-add.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-gateway-backend': minor +--- + +Update usage of the `instanceMetadata` service. diff --git a/.changeset/nice-readers-judge.md b/.changeset/nice-readers-judge.md deleted file mode 100644 index a70ee8e501..0000000000 --- a/.changeset/nice-readers-judge.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-search-backend-module-pg': patch ---- - -Added the < character to the query filter regexp diff --git a/.changeset/ninety-cobras-feel.md b/.changeset/ninety-cobras-feel.md new file mode 100644 index 0000000000..e287ef33ed --- /dev/null +++ b/.changeset/ninety-cobras-feel.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-techdocs': patch +--- + +Fixed an issue where the entire TechDocs page would re-render when navigating between pages within the same entity's documentation. diff --git a/.changeset/odd-states-melt.md b/.changeset/odd-states-melt.md new file mode 100644 index 0000000000..514e890bab --- /dev/null +++ b/.changeset/odd-states-melt.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-plugin-api': minor +--- + +Explicitly mark `coreServices.rootInstanceMetadata` as a root service. diff --git a/.changeset/open-cloths-listen.md b/.changeset/open-cloths-listen.md new file mode 100644 index 0000000000..3af368c7ce --- /dev/null +++ b/.changeset/open-cloths-listen.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-test-utils': patch +--- + +Tweak some of the mock services to have more precise types diff --git a/.changeset/open-items-open.md b/.changeset/open-items-open.md new file mode 100644 index 0000000000..12fcefab4a --- /dev/null +++ b/.changeset/open-items-open.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-app-api': minor +--- + +Updates API for `instanceMetadata` service to return a list of plugins not features. diff --git a/.changeset/plugin-relative-extensions.md b/.changeset/plugin-relative-extensions.md new file mode 100644 index 0000000000..dc27a2283f --- /dev/null +++ b/.changeset/plugin-relative-extensions.md @@ -0,0 +1,17 @@ +--- +'@backstage/frontend-plugin-api': patch +--- + +Added support for plugin-relative `attachTo` declarations for extension definitions. This allows for the creation of extension and extension blueprints that attach to other extensions of a particular `kind` in the same plugin, rather than needing to provide the exact extension ID. This is particularly useful when wanting to provide extension blueprints with a built-in hierarchy where the extensions created from one blueprint attach to extensions created from the other blueprint, for example: + +```ts +// kind: 'tabbed-page' +const parentPage = TabbedPageBlueprint.make({ + params: {....} +}) +// attachTo: { kind: 'tabbed-page', input: 'tabs' } +const child1 = TabContentBlueprint.make({ + name: 'tab1', + params: {....} +}) +``` diff --git a/.changeset/polite-eggs-dance.md b/.changeset/polite-eggs-dance.md new file mode 100644 index 0000000000..e9725be567 --- /dev/null +++ b/.changeset/polite-eggs-dance.md @@ -0,0 +1,5 @@ +--- +'@backstage/ui': patch +--- + +Removed `@base-ui-components/react` dependency as all components now use React Aria Components. diff --git a/.changeset/polite-seas-divide.md b/.changeset/polite-seas-divide.md new file mode 100644 index 0000000000..e265e6df90 --- /dev/null +++ b/.changeset/polite-seas-divide.md @@ -0,0 +1,5 @@ +--- +'@backstage/ui': patch +--- + +Fix the default font size in Backstage UI. diff --git a/.changeset/pre.json b/.changeset/pre.json index 03b4d762e2..f068415e9f 100644 --- a/.changeset/pre.json +++ b/.changeset/pre.json @@ -2,238 +2,292 @@ "mode": "pre", "tag": "next", "initialVersions": { - "example-app": "0.2.113", - "@backstage/app-defaults": "1.7.0", - "example-app-next": "0.0.27", - "app-next-example-plugin": "0.0.27", - "example-backend": "0.0.42", - "@backstage/backend-app-api": "1.2.7", - "@backstage/backend-defaults": "0.12.1", + "example-app": "0.2.114", + "@backstage/app-defaults": "1.7.1", + "example-app-next": "0.0.28", + "app-next-example-plugin": "0.0.28", + "example-backend": "0.0.43", + "@backstage/backend-app-api": "1.2.8", + "@backstage/backend-defaults": "0.13.0", "@backstage/backend-dev-utils": "0.1.5", - "@backstage/backend-dynamic-feature-service": "0.7.4", - "@backstage/backend-openapi-utils": "0.6.1", - "@backstage/backend-plugin-api": "1.4.3", - "@backstage/backend-test-utils": "1.9.0", + "@backstage/backend-dynamic-feature-service": "0.7.5", + "@backstage/backend-openapi-utils": "0.6.2", + "@backstage/backend-plugin-api": "1.4.4", + "@backstage/backend-test-utils": "1.9.1", "@backstage/catalog-client": "1.12.0", "@backstage/catalog-model": "1.7.5", - "@backstage/cli": "0.34.2", + "@backstage/cli": "0.34.4", "@backstage/cli-common": "0.1.15", "@backstage/cli-node": "0.2.14", "@backstage/codemods": "0.1.52", - "@backstage/config": "1.3.3", - "@backstage/config-loader": "1.10.3", - "@backstage/core-app-api": "1.19.0", - "@backstage/core-compat-api": "0.5.2", - "@backstage/core-components": "0.18.0", - "@backstage/core-plugin-api": "1.11.0", - "@backstage/create-app": "0.7.4", - "@backstage/dev-utils": "1.1.14", - "e2e-test": "0.2.32", + "@backstage/config": "1.3.5", + "@backstage/config-loader": "1.10.5", + "@backstage/core-app-api": "1.19.1", + "@backstage/core-compat-api": "0.5.3", + "@backstage/core-components": "0.18.2", + "@backstage/core-plugin-api": "1.11.1", + "@backstage/create-app": "0.7.5", + "@backstage/dev-utils": "1.1.15", + "e2e-test": "0.2.33", "@backstage/e2e-test-utils": "0.1.1", "@backstage/errors": "1.2.7", - "@backstage/eslint-plugin": "0.1.11", - "@backstage/frontend-app-api": "0.13.0", - "@backstage/frontend-defaults": "0.3.1", - "@backstage/frontend-dynamic-feature-loader": "0.1.5", - "@internal/frontend": "0.0.13", - "@backstage/frontend-plugin-api": "0.12.0", - "@backstage/frontend-test-utils": "0.3.6", - "@backstage/integration": "1.18.0", - "@backstage/integration-aws-node": "0.1.17", - "@backstage/integration-react": "1.2.10", + "@backstage/eslint-plugin": "0.1.12", + "@backstage/frontend-app-api": "0.13.1", + "@backstage/frontend-defaults": "0.3.2", + "@backstage/frontend-dynamic-feature-loader": "0.1.6", + "@internal/frontend": "0.0.14", + "@backstage/frontend-plugin-api": "0.12.1", + "@backstage/frontend-test-utils": "0.4.0", + "@backstage/integration": "1.18.1", + "@backstage/integration-aws-node": "0.1.18", + "@backstage/integration-react": "1.2.11", "@internal/opaque": "0.0.1", "@backstage/release-manifests": "0.0.13", - "@backstage/repo-tools": "0.15.2", - "@internal/scaffolder": "0.0.13", - "@techdocs/cli": "1.9.8", - "techdocs-cli-embedded-app": "0.2.112", - "@backstage/test-utils": "1.7.11", - "@backstage/theme": "0.6.8", + "@backstage/repo-tools": "0.15.3", + "@internal/scaffolder": "0.0.14", + "@techdocs/cli": "1.10.0", + "techdocs-cli-embedded-app": "0.2.113", + "@backstage/test-utils": "1.7.12", + "@backstage/theme": "0.7.0", "@backstage/types": "1.2.2", - "@backstage/ui": "0.7.1", + "@backstage/ui": "0.8.0", "@backstage/version-bridge": "1.0.11", "yarn-plugin-backstage": "0.0.7", - "@backstage/plugin-api-docs": "0.12.11", + "@backstage/plugin-api-docs": "0.13.0", "@backstage/plugin-api-docs-module-protoc-gen-doc": "0.1.10", - "@backstage/plugin-app": "0.3.0", - "@backstage/plugin-app-backend": "0.5.6", - "@backstage/plugin-app-node": "0.1.37", - "@backstage/plugin-app-visualizer": "0.1.23", - "@backstage/plugin-auth": "0.1.0", - "@backstage/plugin-auth-backend": "0.25.4", - "@backstage/plugin-auth-backend-module-atlassian-provider": "0.4.7", - "@backstage/plugin-auth-backend-module-auth0-provider": "0.2.7", - "@backstage/plugin-auth-backend-module-aws-alb-provider": "0.4.7", - "@backstage/plugin-auth-backend-module-azure-easyauth-provider": "0.2.12", - "@backstage/plugin-auth-backend-module-bitbucket-provider": "0.3.7", - "@backstage/plugin-auth-backend-module-bitbucket-server-provider": "0.2.7", - "@backstage/plugin-auth-backend-module-cloudflare-access-provider": "0.4.7", - "@backstage/plugin-auth-backend-module-gcp-iap-provider": "0.4.7", - "@backstage/plugin-auth-backend-module-github-provider": "0.3.7", - "@backstage/plugin-auth-backend-module-gitlab-provider": "0.3.7", - "@backstage/plugin-auth-backend-module-google-provider": "0.3.7", - "@backstage/plugin-auth-backend-module-guest-provider": "0.2.12", - "@backstage/plugin-auth-backend-module-microsoft-provider": "0.3.7", - "@backstage/plugin-auth-backend-module-oauth2-provider": "0.4.7", - "@backstage/plugin-auth-backend-module-oauth2-proxy-provider": "0.2.12", - "@backstage/plugin-auth-backend-module-oidc-provider": "0.4.7", - "@backstage/plugin-auth-backend-module-okta-provider": "0.2.7", - "@backstage/plugin-auth-backend-module-onelogin-provider": "0.3.7", - "@backstage/plugin-auth-backend-module-openshift-provider": "0.1.0", - "@backstage/plugin-auth-backend-module-pinniped-provider": "0.3.7", - "@backstage/plugin-auth-backend-module-vmware-cloud-provider": "0.5.7", - "@backstage/plugin-auth-node": "0.6.7", - "@backstage/plugin-auth-react": "0.1.19", - "@backstage/plugin-bitbucket-cloud-common": "0.3.2", - "@backstage/plugin-catalog": "1.31.3", - "@backstage/plugin-catalog-backend": "3.1.0", - "@backstage/plugin-catalog-backend-module-aws": "0.4.15", - "@backstage/plugin-catalog-backend-module-azure": "0.3.9", - "@backstage/plugin-catalog-backend-module-backstage-openapi": "0.5.6", - "@backstage/plugin-catalog-backend-module-bitbucket-cloud": "0.5.3", - "@backstage/plugin-catalog-backend-module-bitbucket-server": "0.5.3", - "@backstage/plugin-catalog-backend-module-gcp": "0.3.12", - "@backstage/plugin-catalog-backend-module-gerrit": "0.3.6", - "@backstage/plugin-catalog-backend-module-gitea": "0.1.4", - "@backstage/plugin-catalog-backend-module-github": "0.11.0", - "@backstage/plugin-catalog-backend-module-github-org": "0.3.14", - "@backstage/plugin-catalog-backend-module-gitlab": "0.7.3", - "@backstage/plugin-catalog-backend-module-gitlab-org": "0.2.13", - "@backstage/plugin-catalog-backend-module-incremental-ingestion": "0.7.4", - "@backstage/plugin-catalog-backend-module-ldap": "0.11.9", - "@backstage/plugin-catalog-backend-module-logs": "0.1.14", - "@backstage/plugin-catalog-backend-module-msgraph": "0.8.0", - "@backstage/plugin-catalog-backend-module-openapi": "0.2.14", - "@backstage/plugin-catalog-backend-module-puppetdb": "0.2.14", - "@backstage/plugin-catalog-backend-module-scaffolder-entity-model": "0.2.12", - "@backstage/plugin-catalog-backend-module-unprocessed": "0.6.4", - "@backstage/plugin-catalog-common": "1.1.5", - "@backstage/plugin-catalog-graph": "0.5.0", - "@backstage/plugin-catalog-import": "0.13.5", - "@backstage/plugin-catalog-node": "1.19.0", - "@backstage/plugin-catalog-react": "1.21.0", - "@backstage/plugin-catalog-unprocessed-entities": "0.2.21", - "@backstage/plugin-catalog-unprocessed-entities-common": "0.0.9", - "@backstage/plugin-config-schema": "0.1.72", - "@backstage/plugin-devtools": "0.1.31", - "@backstage/plugin-devtools-backend": "0.5.9", - "@backstage/plugin-devtools-common": "0.1.17", - "@backstage/plugin-events-backend": "0.5.6", - "@backstage/plugin-events-backend-module-aws-sqs": "0.4.15", - "@backstage/plugin-events-backend-module-azure": "0.2.24", - "@backstage/plugin-events-backend-module-bitbucket-cloud": "0.2.24", - "@backstage/plugin-events-backend-module-bitbucket-server": "0.1.5", - "@backstage/plugin-events-backend-module-gerrit": "0.2.24", - "@backstage/plugin-events-backend-module-github": "0.4.4", - "@backstage/plugin-events-backend-module-gitlab": "0.3.5", - "@backstage/plugin-events-backend-module-google-pubsub": "0.1.4", - "@backstage/plugin-events-backend-module-kafka": "0.1.3", - "@backstage/plugin-events-backend-test-utils": "0.1.48", - "@backstage/plugin-events-node": "0.4.15", - "@internal/plugin-todo-list": "1.0.43", - "@internal/plugin-todo-list-backend": "1.0.43", - "@internal/plugin-todo-list-common": "1.0.26", - "@backstage/plugin-gateway-backend": "1.0.5", - "@backstage/plugin-home": "0.8.12", - "@backstage/plugin-home-react": "0.1.30", - "@backstage/plugin-kubernetes": "0.12.11", - "@backstage/plugin-kubernetes-backend": "0.20.2", - "@backstage/plugin-kubernetes-cluster": "0.0.29", - "@backstage/plugin-kubernetes-common": "0.9.6", - "@backstage/plugin-kubernetes-node": "0.3.4", - "@backstage/plugin-kubernetes-react": "0.5.11", - "@backstage/plugin-mcp-actions-backend": "0.1.3", - "@backstage/plugin-notifications": "0.5.9", - "@backstage/plugin-notifications-backend": "0.5.10", - "@backstage/plugin-notifications-backend-module-email": "0.3.13", - "@backstage/plugin-notifications-backend-module-slack": "0.1.5", - "@backstage/plugin-notifications-common": "0.1.0", - "@backstage/plugin-notifications-node": "0.2.19", - "@backstage/plugin-org": "0.6.44", - "@backstage/plugin-org-react": "0.1.42", - "@backstage/plugin-permission-backend": "0.7.4", - "@backstage/plugin-permission-backend-module-allow-all-policy": "0.2.12", - "@backstage/plugin-permission-common": "0.9.1", - "@backstage/plugin-permission-node": "0.10.4", - "@backstage/plugin-permission-react": "0.4.36", - "@backstage/plugin-proxy-backend": "0.6.6", - "@backstage/plugin-proxy-node": "0.1.8", - "@backstage/plugin-scaffolder": "1.34.1", - "@backstage/plugin-scaffolder-backend": "2.2.1", - "@backstage/plugin-scaffolder-backend-module-azure": "0.2.13", - "@backstage/plugin-scaffolder-backend-module-bitbucket": "0.3.14", - "@backstage/plugin-scaffolder-backend-module-bitbucket-cloud": "0.2.13", - "@backstage/plugin-scaffolder-backend-module-bitbucket-server": "0.2.13", - "@backstage/plugin-scaffolder-backend-module-confluence-to-markdown": "0.3.13", - "@backstage/plugin-scaffolder-backend-module-cookiecutter": "0.3.15", - "@backstage/plugin-scaffolder-backend-module-gcp": "0.2.13", - "@backstage/plugin-scaffolder-backend-module-gerrit": "0.2.13", - "@backstage/plugin-scaffolder-backend-module-gitea": "0.2.13", - "@backstage/plugin-scaffolder-backend-module-github": "0.9.0", - "@backstage/plugin-scaffolder-backend-module-gitlab": "0.9.5", - "@backstage/plugin-scaffolder-backend-module-notifications": "0.1.14", - "@backstage/plugin-scaffolder-backend-module-rails": "0.5.13", - "@backstage/plugin-scaffolder-backend-module-sentry": "0.2.13", - "@backstage/plugin-scaffolder-backend-module-yeoman": "0.4.14", - "@backstage/plugin-scaffolder-common": "1.7.1", - "@backstage/plugin-scaffolder-node": "0.11.1", - "@backstage/plugin-scaffolder-node-test-utils": "0.3.3", - "@backstage/plugin-scaffolder-react": "1.19.1", - "@backstage/plugin-search": "1.4.30", - "@backstage/plugin-search-backend": "2.0.6", - "@backstage/plugin-search-backend-module-catalog": "0.3.8", - "@backstage/plugin-search-backend-module-elasticsearch": "1.7.6", - "@backstage/plugin-search-backend-module-explore": "0.3.7", - "@backstage/plugin-search-backend-module-pg": "0.5.48", - "@backstage/plugin-search-backend-module-stack-overflow-collator": "0.3.13", - "@backstage/plugin-search-backend-module-techdocs": "0.4.6", - "@backstage/plugin-search-backend-node": "1.3.15", - "@backstage/plugin-search-common": "1.2.19", - "@backstage/plugin-search-react": "1.9.4", - "@backstage/plugin-signals": "0.0.23", - "@backstage/plugin-signals-backend": "0.3.8", - "@backstage/plugin-signals-node": "0.1.24", - "@backstage/plugin-signals-react": "0.0.15", - "@backstage/plugin-techdocs": "1.15.0", - "@backstage/plugin-techdocs-addons-test-utils": "1.1.0", - "@backstage/plugin-techdocs-backend": "2.1.0", + "@backstage/plugin-app": "0.3.1", + "@backstage/plugin-app-backend": "0.5.7", + "@backstage/plugin-app-node": "0.1.38", + "@backstage/plugin-app-visualizer": "0.1.24", + "@backstage/plugin-auth": "0.1.1", + "@backstage/plugin-auth-backend": "0.25.5", + "@backstage/plugin-auth-backend-module-atlassian-provider": "0.4.8", + "@backstage/plugin-auth-backend-module-auth0-provider": "0.2.8", + "@backstage/plugin-auth-backend-module-aws-alb-provider": "0.4.8", + "@backstage/plugin-auth-backend-module-azure-easyauth-provider": "0.2.13", + "@backstage/plugin-auth-backend-module-bitbucket-provider": "0.3.8", + "@backstage/plugin-auth-backend-module-bitbucket-server-provider": "0.2.8", + "@backstage/plugin-auth-backend-module-cloudflare-access-provider": "0.4.8", + "@backstage/plugin-auth-backend-module-gcp-iap-provider": "0.4.8", + "@backstage/plugin-auth-backend-module-github-provider": "0.3.8", + "@backstage/plugin-auth-backend-module-gitlab-provider": "0.3.8", + "@backstage/plugin-auth-backend-module-google-provider": "0.3.8", + "@backstage/plugin-auth-backend-module-guest-provider": "0.2.13", + "@backstage/plugin-auth-backend-module-microsoft-provider": "0.3.8", + "@backstage/plugin-auth-backend-module-oauth2-provider": "0.4.8", + "@backstage/plugin-auth-backend-module-oauth2-proxy-provider": "0.2.13", + "@backstage/plugin-auth-backend-module-oidc-provider": "0.4.8", + "@backstage/plugin-auth-backend-module-okta-provider": "0.2.8", + "@backstage/plugin-auth-backend-module-onelogin-provider": "0.3.8", + "@backstage/plugin-auth-backend-module-openshift-provider": "0.1.1", + "@backstage/plugin-auth-backend-module-pinniped-provider": "0.3.8", + "@backstage/plugin-auth-backend-module-vmware-cloud-provider": "0.5.8", + "@backstage/plugin-auth-node": "0.6.8", + "@backstage/plugin-auth-react": "0.1.20", + "@backstage/plugin-bitbucket-cloud-common": "0.3.3", + "@backstage/plugin-catalog": "1.31.4", + "@backstage/plugin-catalog-backend": "3.1.2", + "@backstage/plugin-catalog-backend-module-aws": "0.4.16", + "@backstage/plugin-catalog-backend-module-azure": "0.3.10", + "@backstage/plugin-catalog-backend-module-backstage-openapi": "0.5.7", + "@backstage/plugin-catalog-backend-module-bitbucket-cloud": "0.5.4", + "@backstage/plugin-catalog-backend-module-bitbucket-server": "0.5.4", + "@backstage/plugin-catalog-backend-module-gcp": "0.3.13", + "@backstage/plugin-catalog-backend-module-gerrit": "0.3.7", + "@backstage/plugin-catalog-backend-module-gitea": "0.1.5", + "@backstage/plugin-catalog-backend-module-github": "0.11.1", + "@backstage/plugin-catalog-backend-module-github-org": "0.3.15", + "@backstage/plugin-catalog-backend-module-gitlab": "0.7.4", + "@backstage/plugin-catalog-backend-module-gitlab-org": "0.2.14", + "@backstage/plugin-catalog-backend-module-incremental-ingestion": "0.7.5", + "@backstage/plugin-catalog-backend-module-ldap": "0.11.10", + "@backstage/plugin-catalog-backend-module-logs": "0.1.15", + "@backstage/plugin-catalog-backend-module-msgraph": "0.8.1", + "@backstage/plugin-catalog-backend-module-openapi": "0.2.15", + "@backstage/plugin-catalog-backend-module-puppetdb": "0.2.15", + "@backstage/plugin-catalog-backend-module-scaffolder-entity-model": "0.2.13", + "@backstage/plugin-catalog-backend-module-unprocessed": "0.6.5", + "@backstage/plugin-catalog-common": "1.1.6", + "@backstage/plugin-catalog-graph": "0.5.2", + "@backstage/plugin-catalog-import": "0.13.6", + "@backstage/plugin-catalog-node": "1.19.1", + "@backstage/plugin-catalog-react": "1.21.2", + "@backstage/plugin-catalog-unprocessed-entities": "0.2.22", + "@backstage/plugin-catalog-unprocessed-entities-common": "0.0.10", + "@backstage/plugin-config-schema": "0.1.73", + "@backstage/plugin-devtools": "0.1.32", + "@backstage/plugin-devtools-backend": "0.5.10", + "@backstage/plugin-devtools-common": "0.1.18", + "@backstage/plugin-events-backend": "0.5.7", + "@backstage/plugin-events-backend-module-aws-sqs": "0.4.16", + "@backstage/plugin-events-backend-module-azure": "0.2.25", + "@backstage/plugin-events-backend-module-bitbucket-cloud": "0.2.25", + "@backstage/plugin-events-backend-module-bitbucket-server": "0.1.6", + "@backstage/plugin-events-backend-module-gerrit": "0.2.25", + "@backstage/plugin-events-backend-module-github": "0.4.5", + "@backstage/plugin-events-backend-module-gitlab": "0.3.6", + "@backstage/plugin-events-backend-module-google-pubsub": "0.1.5", + "@backstage/plugin-events-backend-module-kafka": "0.1.4", + "@backstage/plugin-events-backend-test-utils": "0.1.49", + "@backstage/plugin-events-node": "0.4.16", + "@internal/plugin-todo-list": "1.0.44", + "@internal/plugin-todo-list-backend": "1.0.44", + "@internal/plugin-todo-list-common": "1.0.27", + "@backstage/plugin-gateway-backend": "1.0.6", + "@backstage/plugin-home": "0.8.13", + "@backstage/plugin-home-react": "0.1.31", + "@backstage/plugin-kubernetes": "0.12.12", + "@backstage/plugin-kubernetes-backend": "0.20.3", + "@backstage/plugin-kubernetes-cluster": "0.0.30", + "@backstage/plugin-kubernetes-common": "0.9.7", + "@backstage/plugin-kubernetes-node": "0.3.5", + "@backstage/plugin-kubernetes-react": "0.5.12", + "@backstage/plugin-mcp-actions-backend": "0.1.4", + "@backstage/plugin-mui-to-bui": "0.2.0", + "@backstage/plugin-notifications": "0.5.10", + "@backstage/plugin-notifications-backend": "0.5.11", + "@backstage/plugin-notifications-backend-module-email": "0.3.14", + "@backstage/plugin-notifications-backend-module-slack": "0.2.0", + "@backstage/plugin-notifications-common": "0.1.1", + "@backstage/plugin-notifications-node": "0.2.20", + "@backstage/plugin-org": "0.6.45", + "@backstage/plugin-org-react": "0.1.43", + "@backstage/plugin-permission-backend": "0.7.5", + "@backstage/plugin-permission-backend-module-allow-all-policy": "0.2.13", + "@backstage/plugin-permission-common": "0.9.2", + "@backstage/plugin-permission-node": "0.10.5", + "@backstage/plugin-permission-react": "0.4.37", + "@backstage/plugin-proxy-backend": "0.6.7", + "@backstage/plugin-proxy-node": "0.1.9", + "@backstage/plugin-scaffolder": "1.34.2", + "@backstage/plugin-scaffolder-backend": "3.0.0", + "@backstage/plugin-scaffolder-backend-module-azure": "0.2.14", + "@backstage/plugin-scaffolder-backend-module-bitbucket": "0.3.15", + "@backstage/plugin-scaffolder-backend-module-bitbucket-cloud": "0.2.14", + "@backstage/plugin-scaffolder-backend-module-bitbucket-server": "0.2.14", + "@backstage/plugin-scaffolder-backend-module-confluence-to-markdown": "0.3.14", + "@backstage/plugin-scaffolder-backend-module-cookiecutter": "0.3.16", + "@backstage/plugin-scaffolder-backend-module-gcp": "0.2.14", + "@backstage/plugin-scaffolder-backend-module-gerrit": "0.2.14", + "@backstage/plugin-scaffolder-backend-module-gitea": "0.2.14", + "@backstage/plugin-scaffolder-backend-module-github": "0.9.1", + "@backstage/plugin-scaffolder-backend-module-gitlab": "0.9.6", + "@backstage/plugin-scaffolder-backend-module-notifications": "0.1.15", + "@backstage/plugin-scaffolder-backend-module-rails": "0.5.14", + "@backstage/plugin-scaffolder-backend-module-sentry": "0.2.14", + "@backstage/plugin-scaffolder-backend-module-yeoman": "0.4.15", + "@backstage/plugin-scaffolder-common": "1.7.2", + "@backstage/plugin-scaffolder-node": "0.12.0", + "@backstage/plugin-scaffolder-node-test-utils": "0.3.4", + "@backstage/plugin-scaffolder-react": "1.19.2", + "@backstage/plugin-search": "1.4.31", + "@backstage/plugin-search-backend": "2.0.7", + "@backstage/plugin-search-backend-module-catalog": "0.3.9", + "@backstage/plugin-search-backend-module-elasticsearch": "1.7.7", + "@backstage/plugin-search-backend-module-explore": "0.3.8", + "@backstage/plugin-search-backend-module-pg": "0.5.49", + "@backstage/plugin-search-backend-module-stack-overflow-collator": "0.3.14", + "@backstage/plugin-search-backend-module-techdocs": "0.4.7", + "@backstage/plugin-search-backend-node": "1.3.16", + "@backstage/plugin-search-common": "1.2.20", + "@backstage/plugin-search-react": "1.9.5", + "@backstage/plugin-signals": "0.0.24", + "@backstage/plugin-signals-backend": "0.3.9", + "@backstage/plugin-signals-node": "0.1.25", + "@backstage/plugin-signals-react": "0.0.16", + "@backstage/plugin-techdocs": "1.15.1", + "@backstage/plugin-techdocs-addons-test-utils": "1.1.1", + "@backstage/plugin-techdocs-backend": "2.1.1", "@backstage/plugin-techdocs-common": "0.1.1", - "@backstage/plugin-techdocs-module-addons-contrib": "1.1.28", - "@backstage/plugin-techdocs-node": "1.13.7", - "@backstage/plugin-techdocs-react": "1.3.3", - "@backstage/plugin-user-settings": "0.8.26", - "@backstage/plugin-user-settings-backend": "0.3.6", + "@backstage/plugin-techdocs-module-addons-contrib": "1.1.29", + "@backstage/plugin-techdocs-node": "1.13.8", + "@backstage/plugin-techdocs-react": "1.3.4", + "@backstage/plugin-user-settings": "0.8.27", + "@backstage/plugin-user-settings-backend": "0.3.7", "@backstage/plugin-user-settings-common": "0.0.1" }, "changesets": [ - "brave-teeth-reply", - "brown-falcons-own", - "calm-trains-tie", - "clever-papers-watch", - "cold-coats-show", - "cool-baboons-count", - "create-app-1758639549", - "create-app-1758718573", - "eager-toes-start", - "fast-heads-brake", - "fast-queens-guess", - "five-olives-bet", - "hungry-crews-fetch", - "itchy-falcons-leave", - "kind-places-reply", - "legal-eagles-jog", - "nice-readers-judge", - "public-sites-admire", - "rare-states-pay", - "ready-poems-change", - "ready-pots-arrive", - "red-times-bet", - "salty-words-wash", - "short-aliens-invite", - "six-cooks-battle", - "solid-bikes-leave", - "tame-hairs-smash", - "tender-cups-tap", - "wet-spiders-wait" + "all-camels-agree", + "all-parrots-change", + "better-hats-cross", + "better-steaks-act", + "breezy-times-ring", + "bright-ears-send", + "busy-goats-create", + "clever-boats-clap", + "create-app-1761312116", + "create-app-1762897504", + "cruel-items-dig", + "cruel-plums-talk", + "deep-gifts-slide", + "easy-hands-grow", + "eighty-mails-leave", + "eighty-results-prove", + "eleven-carpets-win", + "eleven-lights-taste", + "every-ants-count", + "every-clocks-arrive", + "fancy-years-camp", + "fast-tables-sink", + "fast-tools-mate", + "fine-hands-return", + "five-seas-jam", + "fix-text-truncate-prop", + "flat-paws-do", + "fruity-snails-laugh", + "funny-stars-open", + "gentle-bikes-relax", + "giant-lamps-happen", + "grumpy-planes-bet", + "heavy-cars-wash", + "honest-lamps-occur", + "honest-pandas-win", + "huge-taxis-grab", + "itchy-bars-smell", + "legal-weeks-walk", + "lemon-spies-sleep", + "long-humans-sink", + "long-impalas-burn", + "loud-carpets-throw", + "moody-plums-add", + "ninety-cobras-feel", + "odd-states-melt", + "open-cloths-listen", + "open-items-open", + "plugin-relative-extensions", + "polite-eggs-dance", + "polite-seas-divide", + "pretty-kids-allow", + "purple-deer-bet", + "quiet-singers-pick", + "renovate-cd75086", + "rich-streets-rule", + "ripe-crabs-care", + "seven-cycles-pick", + "sharp-dragons-relate", + "short-sides-feel", + "silver-garlics-thank", + "sixty-clowns-run", + "smart-donuts-teach", + "solid-bees-agree", + "solid-dancers-march", + "stupid-doodles-love", + "ten-houses-attack", + "tender-regions-know", + "thirty-hoops-own", + "tough-sloths-spend", + "true-taxes-cover", + "twelve-spoons-feel", + "typescript-constructor-refactor", + "upset-teeth-add", + "warm-carrots-lead", + "warm-ducks-notice", + "warm-moments-repeat", + "warm-shrimps-clap", + "wicked-cycles-enter", + "wide-papers-run", + "wild-donkeys-sneeze", + "wild-owls-divide", + "yummy-socks-brake" ] } diff --git a/.changeset/pretty-kids-allow.md b/.changeset/pretty-kids-allow.md new file mode 100644 index 0000000000..6b3b11a895 --- /dev/null +++ b/.changeset/pretty-kids-allow.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend-module-github': patch +--- + +Added configurable `pageSizes` for GitHub GraphQL API queries to prevent `RESOURCE_LIMITS_EXCEEDED` errors with organizations with large number of repositories. Please see the [GitHub Discovery documentation](https://backstage.io/docs/integrations/github/discovery#configuration) for new configuration options. diff --git a/.changeset/public-sites-admire.md b/.changeset/public-sites-admire.md deleted file mode 100644 index f5f3304f9f..0000000000 --- a/.changeset/public-sites-admire.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/core-components': patch -'@backstage/plugin-catalog-graph': patch ---- - -Fixed DependencyGraph `svg` size not adapting to the container size diff --git a/.changeset/purple-deer-bet.md b/.changeset/purple-deer-bet.md new file mode 100644 index 0000000000..c60f2ada7a --- /dev/null +++ b/.changeset/purple-deer-bet.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-graph': patch +--- + +Ensure the catalog graph entity card respects the height prop so the visualization scales down properly on wide screens. diff --git a/.changeset/quiet-singers-pick.md b/.changeset/quiet-singers-pick.md new file mode 100644 index 0000000000..cc66db1bcd --- /dev/null +++ b/.changeset/quiet-singers-pick.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-signals': patch +--- + +Fixes a bug where the `SignalClient` would try to subscribe to the same channel twice after an error, instead of just once. diff --git a/.changeset/rare-states-pay.md b/.changeset/rare-states-pay.md deleted file mode 100644 index 4f7c639af7..0000000000 --- a/.changeset/rare-states-pay.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/plugin-catalog-backend-module-azure': patch -'@backstage/integration': patch ---- - -Adds config definitions for Azure Blob Storage. diff --git a/.changeset/ready-poems-change.md b/.changeset/ready-poems-change.md deleted file mode 100644 index fc95ddb99a..0000000000 --- a/.changeset/ready-poems-change.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -'@backstage/plugin-scaffolder-node': minor ---- - -**BREAKING** - Marking optional fields as required in the `TaskBroker`, these can be fixed with a no-op `() => void` if you don't want to implement the functions. - -- `cancel`, `recoverTasks` and `retry` are the required methods on the `TaskBroker` interface. - -**NOTE**: If you're affected by this breaking change, please reach out to us in an issue as we're thinking about completely removing the `TaskBroker` extension point soon and would like to hear your use cases for the upcoming re-architecture of the `scaffolder-backend` plugin. diff --git a/.changeset/ready-pots-arrive.md b/.changeset/ready-pots-arrive.md deleted file mode 100644 index 96f13a12ae..0000000000 --- a/.changeset/ready-pots-arrive.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder': patch ---- - -Add missing `templatingExtensions` option to RouterProps.contextMenu to allow global control across scaffolder pages diff --git a/.changeset/red-times-bet.md b/.changeset/red-times-bet.md deleted file mode 100644 index 29e89fb07f..0000000000 --- a/.changeset/red-times-bet.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -'@backstage/plugin-scaffolder-node': patch ---- - -**BREAKING ALPHA**: We've moved the `scaffolderActionsExtensionPoint` from `/alpha` to the main export. - -```tsx -// before -import { scaffolderActionsExtensionPoint } from '@backstage/plugin-scaffolder-node/alpha'; - -// after -import { scaffolderActionsExtensionPoint } from '@backstage/plugin-scaffolder-node'; -``` diff --git a/.changeset/renovate-cd75086.md b/.changeset/renovate-cd75086.md new file mode 100644 index 0000000000..5bc38af431 --- /dev/null +++ b/.changeset/renovate-cd75086.md @@ -0,0 +1,8 @@ +--- +'@backstage/plugin-kubernetes-backend': patch +'@backstage/plugin-kubernetes-common': patch +'@backstage/plugin-kubernetes-node': patch +'@backstage/plugin-kubernetes-react': patch +--- + +Updated dependency `@kubernetes/client-node` to `1.4.0`. diff --git a/.changeset/rich-streets-rule.md b/.changeset/rich-streets-rule.md new file mode 100644 index 0000000000..07f431b6a7 --- /dev/null +++ b/.changeset/rich-streets-rule.md @@ -0,0 +1,5 @@ +--- +'@backstage/ui': patch +--- + +Fix CSS layer ordering in Backstage UI to make sure component styles are loaded after tokens and base declarations. diff --git a/.changeset/ripe-crabs-care.md b/.changeset/ripe-crabs-care.md new file mode 100644 index 0000000000..d8cdc27652 --- /dev/null +++ b/.changeset/ripe-crabs-care.md @@ -0,0 +1,5 @@ +--- +'@backstage/ui': patch +--- + +Fixed RadioGroup radio button ellipse distortion by preventing flex shrink and grow. diff --git a/.changeset/salty-words-wash.md b/.changeset/salty-words-wash.md deleted file mode 100644 index eec2e85331..0000000000 --- a/.changeset/salty-words-wash.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -'@backstage/backend-defaults': minor ---- - -Adds support for configuring server-level HTTP options through the -`app-config.yaml` file under the `backend.server` key. Supported options -include `headersTimeout`, `keepAliveTimeout`, `requestTimeout`, `timeout`, -`maxHeadersCount`, and `maxRequestsPerSocket`. - -These are passed directly to the underlying Node.js HTTP server. -If omitted, Node.js defaults are used. diff --git a/.changeset/seven-cycles-pick.md b/.changeset/seven-cycles-pick.md new file mode 100644 index 0000000000..fbd1a3895f --- /dev/null +++ b/.changeset/seven-cycles-pick.md @@ -0,0 +1,17 @@ +--- +'@backstage/ui': minor +--- + +**BREAKING**: Changed className prop behavior to augment default styles instead of being ignored or overriding them. + +Affected components: + +- Menu, MenuListBox, MenuAutocomplete, MenuAutocompleteListbox, MenuItem, MenuListBoxItem, MenuSection, MenuSeparator +- Switch +- Skeleton +- FieldLabel +- Header, HeaderToolbar +- HeaderPage +- Tabs, TabList, Tab, TabPanel + +If you were passing custom className values to any of these components that relied on the previous behavior, you may need to adjust your styles to account for the default classes now being applied alongside your custom classes. diff --git a/.changeset/sharp-dragons-relate.md b/.changeset/sharp-dragons-relate.md new file mode 100644 index 0000000000..1f7e5e0d4a --- /dev/null +++ b/.changeset/sharp-dragons-relate.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-app-visualizer': patch +--- + +Migrated to use `@backstage/ui`. diff --git a/.changeset/short-aliens-invite.md b/.changeset/short-aliens-invite.md deleted file mode 100644 index ae3bbc3831..0000000000 --- a/.changeset/short-aliens-invite.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -'@backstage/plugin-api-docs': minor ---- - -Remove explicit dependency on `isomorphic-form-data`. - -This explicit dependency was added to address [an issue](https://github.com/swagger-api/swagger-ui/issues/7436) in the -dependency `swagger-ui-react`. That [issue has since been resolved](https://github.com/swagger-api/swagger-ui/issues/7436#issuecomment-889792304), -and `isomorphic-form-data` no longer needs to be declared. - -Additionally, this changeset updates the `swagger-ui-react` dependency to version `5.19.0` or higher, which includes -[compatibility](https://github.com/swagger-api/swagger-ui?tab=readme-ov-file#compatibility) with the latest versions of -the OpenAPI specification. diff --git a/.changeset/short-sides-feel.md b/.changeset/short-sides-feel.md new file mode 100644 index 0000000000..a7e3542ba1 --- /dev/null +++ b/.changeset/short-sides-feel.md @@ -0,0 +1,5 @@ +--- +'@backstage/create-app': patch +--- + +Added the new `@backstage/cli/templates/catalog-provider-module` template to the explicit template configuration for the `next-app` template. diff --git a/.changeset/silver-garlics-thank.md b/.changeset/silver-garlics-thank.md new file mode 100644 index 0000000000..d95781eb04 --- /dev/null +++ b/.changeset/silver-garlics-thank.md @@ -0,0 +1,5 @@ +--- +'@backstage/ui': patch +--- + +Fix font smoothing as default in Backstage UI. diff --git a/.changeset/six-cooks-battle.md b/.changeset/six-cooks-battle.md deleted file mode 100644 index ecf856225e..0000000000 --- a/.changeset/six-cooks-battle.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/config-loader': patch -'@backstage/config': patch ---- - -Allow colon to be used as config key. diff --git a/.changeset/sixty-clowns-run.md b/.changeset/sixty-clowns-run.md new file mode 100644 index 0000000000..595049df8f --- /dev/null +++ b/.changeset/sixty-clowns-run.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-components': patch +--- + +Line numbers in LogViewer will not be selectable in UI anymore diff --git a/.changeset/smart-donuts-teach.md b/.changeset/smart-donuts-teach.md new file mode 100644 index 0000000000..f258dee394 --- /dev/null +++ b/.changeset/smart-donuts-teach.md @@ -0,0 +1,5 @@ +--- +'@backstage/ui': patch +--- + +Enable tree-shaking of imports other than `*.css`. diff --git a/.changeset/solid-bees-agree.md b/.changeset/solid-bees-agree.md new file mode 100644 index 0000000000..8feecaf6d4 --- /dev/null +++ b/.changeset/solid-bees-agree.md @@ -0,0 +1,5 @@ +--- +'@backstage/eslint-plugin': minor +--- + +Added `@backstage/no-ui-css-imports-in-non-frontend` rule, which ensures that CSS from `@backstage/ui` is not imported outside of the frontend app. diff --git a/.changeset/solid-bikes-leave.md b/.changeset/solid-bikes-leave.md deleted file mode 100644 index 6f1bfe0b2f..0000000000 --- a/.changeset/solid-bikes-leave.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -'@backstage/plugin-scaffolder-node': patch ---- - -**DEPRECATION**: We're going to be working on refactoring a lot of the internals of the Scaffolder backend plugin, and with that comes a lot of deprecations and removals for public types that are making these things hard. - -If you're using these types, please reach out to us either on Discord or a GitHub issue with your use cases. - -- `SerializedTask`, `SerializedTaskEvent`, `TaskBroker`, `TaskContext`, `TaskBrokerDispatchOptions`, `TaskBrokerDispatchResult`, `TaskCompletionState`, `TaskEventType`, `TaskFilter`, `TaskFilters`, `TaskStatus` are the types that have now been marked as deprecated, and will be removed in a future release. diff --git a/.changeset/solid-dancers-march.md b/.changeset/solid-dancers-march.md new file mode 100644 index 0000000000..5633bb3e2e --- /dev/null +++ b/.changeset/solid-dancers-march.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-user-settings': patch +--- + +Revert `storageApiRef` implementation diff --git a/.changeset/stupid-doodles-love.md b/.changeset/stupid-doodles-love.md new file mode 100644 index 0000000000..878ad853cd --- /dev/null +++ b/.changeset/stupid-doodles-love.md @@ -0,0 +1,5 @@ +--- +'@techdocs/cli': patch +--- + +Fix for missing styles due to move to BUI. diff --git a/.changeset/tame-hairs-smash.md b/.changeset/tame-hairs-smash.md deleted file mode 100644 index db05708132..0000000000 --- a/.changeset/tame-hairs-smash.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/plugin-catalog-backend-module-bitbucket-cloud': patch -'@backstage/plugin-bitbucket-cloud-common': patch ---- - -Allow for passing a `pagelen` parameter to configure the `pagelength` property of the `BitbucketCloudEntityProvider` `searchCode` pagination to resolve [bug](https://jira.atlassian.com/browse/BCLOUD-23644) pertaining to duplicate results being returned. diff --git a/.changeset/ten-houses-attack.md b/.changeset/ten-houses-attack.md new file mode 100644 index 0000000000..9bb1ad2565 --- /dev/null +++ b/.changeset/ten-houses-attack.md @@ -0,0 +1,15 @@ +--- +'@backstage/plugin-scaffolder-backend-module-gitlab': patch +'@backstage/plugin-scaffolder-backend-module-rails': patch +'@backstage/plugin-permission-backend': patch +'@backstage/plugin-permission-common': patch +'@backstage/core-components': patch +'@backstage/core-plugin-api': patch +'@backstage/plugin-devtools-common': patch +'@backstage/plugin-search-backend': patch +'@backstage/core-app-api': patch +'@backstage/plugin-catalog-graph': patch +'@backstage/cli': patch +--- + +Convert all enums to erasable-syntax compliant patterns diff --git a/.changeset/tender-cups-tap.md b/.changeset/tender-cups-tap.md deleted file mode 100644 index e95fe2669e..0000000000 --- a/.changeset/tender-cups-tap.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/ui': patch ---- - -Improved SearchField component flex layout and animations. Fixed SearchField behavior in Header components by switching from width-based transitions to flex-basis transitions for better responsive behavior. Added new Storybook stories to test SearchField integration with Header component. diff --git a/.changeset/tender-regions-know.md b/.changeset/tender-regions-know.md new file mode 100644 index 0000000000..8f59567597 --- /dev/null +++ b/.changeset/tender-regions-know.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-auth': patch +--- + +Authentication content screen now uses application title. diff --git a/.changeset/thirty-hoops-own.md b/.changeset/thirty-hoops-own.md new file mode 100644 index 0000000000..88b55d5f98 --- /dev/null +++ b/.changeset/thirty-hoops-own.md @@ -0,0 +1,5 @@ +--- +'@backstage/frontend-plugin-api': patch +--- + +Updated to `ExtensionInput` to make all type parameters optional. diff --git a/.changeset/tough-sloths-spend.md b/.changeset/tough-sloths-spend.md new file mode 100644 index 0000000000..ed6a4ab366 --- /dev/null +++ b/.changeset/tough-sloths-spend.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend-module-github-org': patch +--- + +Added configurable `pageSizes` for GitHub GraphQL API queries to prevent `RESOURCE_LIMITS_EXCEEDED` errors with organizations with large number of teams and members. Please see the [GitHub Org Data documentation](https://backstage.io/docs/integrations/github/org#configuration-details) for new configuration options. diff --git a/.changeset/true-taxes-cover.md b/.changeset/true-taxes-cover.md new file mode 100644 index 0000000000..ec204084d0 --- /dev/null +++ b/.changeset/true-taxes-cover.md @@ -0,0 +1,5 @@ +--- +'@backstage/ui': patch +--- + +Added `loading` prop to Button and ButtonIcon components for displaying spinner during async operations. diff --git a/.changeset/twelve-spoons-feel.md b/.changeset/twelve-spoons-feel.md new file mode 100644 index 0000000000..21298b4e09 --- /dev/null +++ b/.changeset/twelve-spoons-feel.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-catalog-backend': minor +'@backstage/plugin-catalog-node': minor +--- + +Enable YAML merge keys in yamlPlaceholderResolver diff --git a/.changeset/typescript-constructor-refactor.md b/.changeset/typescript-constructor-refactor.md new file mode 100644 index 0000000000..b24cb5dbd0 --- /dev/null +++ b/.changeset/typescript-constructor-refactor.md @@ -0,0 +1,63 @@ +--- +'@backstage/plugin-catalog-backend': patch +'@backstage/plugin-kubernetes-react': patch +'@backstage/plugin-notifications-backend': patch +'@backstage/plugin-notifications-node': patch +'@backstage/plugin-permission-react': patch +'@backstage/plugin-scaffolder-backend-module-gcp': patch +'@backstage/plugin-scaffolder-backend': patch +'@backstage/plugin-search-backend-module-elasticsearch': patch +'@backstage/plugin-search-backend-module-pg': patch +'@backstage/plugin-search-backend': patch +'@backstage/plugin-search-backend-node': patch +'@backstage/plugin-search-react': patch +'@backstage/plugin-signals': patch +'@backstage/plugin-kubernetes-backend': patch +'@backstage/plugin-events-backend': patch +'@backstage/plugin-catalog-backend-module-backstage-openapi': patch +'@backstage/plugin-events-node': patch +'@backstage/plugin-user-settings': patch +'@backstage/plugin-auth-backend': patch +'@backstage/plugin-auth-backend-module-cloudflare-access-provider': patch +'@backstage/plugin-catalog-react': patch +'@backstage/plugin-notifications-backend-module-email': patch +'@backstage/plugin-events-backend-module-aws-sqs': patch +'@backstage/plugin-devtools-backend': patch +'@backstage/plugin-catalog-backend-module-unprocessed': patch +'@backstage/plugin-catalog-backend-module-azure': patch +'@backstage/plugin-catalog-backend-module-aws': patch +'@backstage/plugin-scaffolder': patch +'@backstage/plugin-techdocs-node': patch +'@backstage/plugin-catalog-unprocessed-entities': patch +'@backstage/plugin-catalog-backend-module-msgraph': patch +'@backstage/plugin-catalog-backend-module-ldap': patch +'@backstage/plugin-bitbucket-cloud-common': patch +'@backstage/plugin-auth-node': patch +'@backstage/backend-app-api': patch +'@backstage/backend-defaults': patch +'@backstage/config-loader': patch +'@backstage/core-app-api': patch +'@backstage/core-plugin-api': patch +'@backstage/frontend-app-api': patch +'@backstage/config': patch +'@backstage/backend-dynamic-feature-service': patch +'@backstage/core-components': patch +'@backstage/cli': patch +'@backstage/cli-node': patch +'@backstage/catalog-model': patch +'@backstage/integration': patch +'@backstage/frontend-plugin-api': patch +'@backstage/integration-aws-node': patch +'@backstage/repo-tools': patch +'@backstage/backend-test-utils': patch +'@backstage/plugin-catalog-import': patch +'@backstage/plugin-events-backend-module-kafka': patch +'@backstage/plugin-kubernetes-node': patch +'@backstage/plugin-mcp-actions-backend': patch +'@backstage/plugin-scaffolder-node-test-utils': patch +'@backstage/plugin-scaffolder-node': patch +'@backstage/plugin-scaffolder-react': patch +'@backstage/plugin-user-settings-backend': patch +--- + +Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. diff --git a/.changeset/upset-teeth-add.md b/.changeset/upset-teeth-add.md new file mode 100644 index 0000000000..834ce7be2c --- /dev/null +++ b/.changeset/upset-teeth-add.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-mui-to-bui': patch +--- + +Updated BUI checkbox preview example to align with new component API. diff --git a/.changeset/warm-carrots-lead.md b/.changeset/warm-carrots-lead.md new file mode 100644 index 0000000000..fdc84d026a --- /dev/null +++ b/.changeset/warm-carrots-lead.md @@ -0,0 +1,5 @@ +--- +'@backstage/ui': patch +--- + +Fixed Table Row component to properly support opening links in new tabs via right-click or Cmd+Click when using the href prop. diff --git a/.changeset/warm-ducks-notice.md b/.changeset/warm-ducks-notice.md new file mode 100644 index 0000000000..95e28b0836 --- /dev/null +++ b/.changeset/warm-ducks-notice.md @@ -0,0 +1,39 @@ +--- +'@backstage/ui': minor +--- + +**BREAKING**: Removed `Collapsible` component. Migrate to `Accordion` or use React Aria `Disclosure`. + +## Migration Path 1: Accordion (Opinionated Styled Component) + +Accordion provides preset styling with a similar component structure. + +```diff +- import { Collapsible } from '@backstage/ui'; ++ import { Accordion, AccordionTrigger, AccordionPanel } from '@backstage/ui'; + +- +- } /> +- Content +- + ++ ++ ++ Content ++ +``` + +CSS classes: `.bui-CollapsibleRoot` → `.bui-Accordion`, `.bui-CollapsibleTrigger` → `.bui-AccordionTrigger` (now on heading element), `.bui-CollapsiblePanel` → `.bui-AccordionPanel` + +## Migration Path 2: React Aria Disclosure (Full Customization) + +For custom styling without preset styles: + +```tsx +import { Disclosure, Button, DisclosurePanel } from 'react-aria-components'; + + + + Content +; +``` diff --git a/.changeset/warm-moments-repeat.md b/.changeset/warm-moments-repeat.md new file mode 100644 index 0000000000..a72387d720 --- /dev/null +++ b/.changeset/warm-moments-repeat.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-defaults': patch +--- + +Fix #31348 issue where BitbucketUrlReader ignored provided token and instead always used integration credentials diff --git a/.changeset/warm-shrimps-clap.md b/.changeset/warm-shrimps-clap.md new file mode 100644 index 0000000000..0386df05de --- /dev/null +++ b/.changeset/warm-shrimps-clap.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend-module-incremental-ingestion': patch +--- + +Correctly handle entity removal computation when DB count query returns string diff --git a/.changeset/wet-spiders-wait.md b/.changeset/wet-spiders-wait.md deleted file mode 100644 index aaffc92013..0000000000 --- a/.changeset/wet-spiders-wait.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend': major ---- - -**BREAKING** - Removing the deprecated types and interfaces, there's no replacement for these types, and hopefully not currently used as they offer no value with the plugin being on the new backend system and no way to consume them. - -Affected types: `CreateWorkerOptions`, `CurrentClaimedTask`, `DatabaseTaskStore`, `DatabaseTaskStoreOptions`, `TaskManager`, `TaskStore`, `TaskStoreCreateTaskOptions`, `TaskStoreCreateTaskResult`, `TaskStoreEmitOptions`, `TaskStoreListEventsOptions`, `TaskStoreRecoverTaskOptions`, `TaskStoreShutDownTaskOptions`, `TaskWorker` and `TemplateActionRegistry`. diff --git a/.changeset/wicked-cycles-enter.md b/.changeset/wicked-cycles-enter.md new file mode 100644 index 0000000000..cea5cff371 --- /dev/null +++ b/.changeset/wicked-cycles-enter.md @@ -0,0 +1,9 @@ +--- +'@backstage/ui': minor +--- + +**BREAKING**: The `SelectProps` interface now accepts a generic type parameter for selection mode. + +Added searchable and multiple selection support to Select component. The component now accepts `searchable`, `selectionMode`, and `searchPlaceholder` props to enable filtering and multi-selection modes. + +Migration: If you're using `SelectProps` type directly, update from `SelectProps` to `SelectProps<'single' | 'multiple'>`. Component usage remains backward compatible. diff --git a/.changeset/wide-papers-run.md b/.changeset/wide-papers-run.md new file mode 100644 index 0000000000..e0e16d71af --- /dev/null +++ b/.changeset/wide-papers-run.md @@ -0,0 +1,5 @@ +--- +'@backstage/ui': patch +--- + +Set the color-scheme property depending on theme diff --git a/.changeset/wild-donkeys-sneeze.md b/.changeset/wild-donkeys-sneeze.md new file mode 100644 index 0000000000..5e2c4474c5 --- /dev/null +++ b/.changeset/wild-donkeys-sneeze.md @@ -0,0 +1,5 @@ +--- +'@backstage/ui': patch +--- + +Added new VisuallyHidden component for hiding content visually while keeping it accessible to screen readers. diff --git a/.changeset/wild-owls-divide.md b/.changeset/wild-owls-divide.md new file mode 100644 index 0000000000..d903b76bd0 --- /dev/null +++ b/.changeset/wild-owls-divide.md @@ -0,0 +1,5 @@ +--- +'@backstage/ui': patch +--- + +Fix default font wight and font family in Backstage UI. diff --git a/.changeset/yummy-socks-brake.md b/.changeset/yummy-socks-brake.md new file mode 100644 index 0000000000..4f3dcfb598 --- /dev/null +++ b/.changeset/yummy-socks-brake.md @@ -0,0 +1,7 @@ +--- +'@backstage/plugin-auth-backend': patch +--- + +Allow configuring dynamic client registration token expiration with config `auth.experimentalDynamicClientRegistration.tokenExpiration`. + +Maximum expiration for the DCR token is 24 hours. Default expiration is 1 hour. diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 89fba0c8ad..8491b1a260 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -10,6 +10,7 @@ yarn.lock @backstage/maintainers @backst /.changeset @backstage/operations-maintainers /.changeset/*.md /.github @backstage/operations-maintainers +/.github/vale @backstage/documentation-maintainers /beps/0001-notifications-system @backstage/maintainers @backstage/notifications-maintainers /docs @backstage/maintainers @backstage/documentation-maintainers /docs/assets/search @backstage/search-maintainers diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 4f0e0ac47d..98073c5128 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -14,7 +14,7 @@ The following files contain guidelines for the project: Before any of these commands can be run, you need to run `yarn install` in the project root. - Build: There is no need to build the project during development, and it is verified automatically in the CI pipeline. -- Test: Use `yarn test ` in the project root to run tests. The path can be either a single file or a directory, and be omitted to run tests for all changed files. +- Test: Use `yarn test --no-watch ` in the project root to run tests. The path can be either a single file or a directory. Always provide a path, avoid running all tests. - Type checking: Use `yarn tsc` in the project root to run the type checker. - Code formatting: Use `yarn prettier --write ` to format code. - Lint: Use `yarn lint --fix` in the project root to run the linter. diff --git a/.github/vale/config/vocabularies/Backstage/accept.txt b/.github/vale/config/vocabularies/Backstage/accept.txt index 8f3dcb65ff..80d55d498a 100644 --- a/.github/vale/config/vocabularies/Backstage/accept.txt +++ b/.github/vale/config/vocabularies/Backstage/accept.txt @@ -21,9 +21,9 @@ asciidoc async Atlassian autofix +autologout automations autoscaling -Autoscaling autoselect Avro backend's @@ -47,8 +47,10 @@ bundler bundlers bursty callout +camelCase CDNs Chai +Chainguard changeset changesets Changesets @@ -108,6 +110,7 @@ dependabot deps dequeue dequeueing +dereferencing deserialization destructured destructuring @@ -177,6 +180,7 @@ graphql GraphQL graphviz Hackathons +hackathon haproxy hardcoded hardcoding @@ -220,11 +224,13 @@ jq js json jsonnet +JSONSchema jsx JWTs Kaewkasi Kaswell keepalive +Keycloak Keyv Knex knip @@ -282,6 +288,7 @@ modularization monorepo Monorepo monorepos +monospace morgan msgraph msw @@ -416,6 +423,7 @@ sanitization scaffolded scaffolder Scaffolder +scalprum SCM SCMs scrollable @@ -466,6 +474,8 @@ subheaders subkey subpage subpath +subpaths +subroute subroutes substring subtree @@ -490,6 +500,7 @@ templaters Templaters TFRecord theia +Themer threenager thumbsup todo @@ -560,3 +571,4 @@ Zolotusky zoomable zsh resizable +enums diff --git a/.github/workflows/api-breaking-changes-comment.yml b/.github/workflows/api-breaking-changes-comment.yml index f419a12599..981d5cb4d6 100644 --- a/.github/workflows/api-breaking-changes-comment.yml +++ b/.github/workflows/api-breaking-changes-comment.yml @@ -99,11 +99,11 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4 - name: Fetch cached Manifests File id: cache - uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4 + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 with: path: comment.md key: ${{ needs.setup.outputs.comment-cache-key }} diff --git a/.github/workflows/automate_stale.yml b/.github/workflows/automate_stale.yml index d6c86b9afb..5b92c6211b 100644 --- a/.github/workflows/automate_stale.yml +++ b/.github/workflows/automate_stale.yml @@ -27,7 +27,7 @@ jobs: recent activity. It will be closed if no further activity occurs. Thank you for your contributions. days-before-issue-stale: 60 - days-before-issue-close: 7 + days-before-issue-close: 14 exempt-issue-labels: after vacations,no stale stale-issue-label: stale stale-pr-message: > @@ -35,8 +35,8 @@ jobs: recent activity from the author. It will be closed if no further activity occurs. If the PR was closed and you want it re-opened, let us know and we'll re-open the PR so that you can continue the contribution! - days-before-pr-stale: 14 - days-before-pr-close: 7 + days-before-pr-stale: 30 + days-before-pr-close: 14 exempt-pr-labels: after vacations,no stale stale-pr-label: stale operations-per-run: 100 diff --git a/.github/workflows/ci-noop.yml b/.github/workflows/ci-noop.yml index 35901d37b2..0fce9ad6b9 100644 --- a/.github/workflows/ci-noop.yml +++ b/.github/workflows/ci-noop.yml @@ -24,7 +24,7 @@ jobs: name: Verify ${{ matrix.node-version }} steps: # - name: Harden Runner - # uses: step-security/harden-runner@8ca2b8b2ece13480cda6dacd3511b49857a23c09 # v2.5.1 + # uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.5.1 # with: # egress-policy: audit diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 53f599e926..8aabe7de58 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -158,8 +158,8 @@ jobs: name: Test ${{ matrix.node-version }} services: - postgres17: - image: postgres:17 + postgres18: + image: postgres:18 env: POSTGRES_PASSWORD: postgres options: >- @@ -169,8 +169,8 @@ jobs: --health-retries 5 ports: - 5432/tcp - postgres13: - image: postgres:13 + postgres14: + image: postgres:14 env: POSTGRES_PASSWORD: postgres options: >- @@ -236,7 +236,7 @@ jobs: # Use the lower-level cache actions for the success cache, so that we can store the cache even on failed builds - name: restore backstage-cli cache - uses: actions/cache/restore@0400d5f644dc74513175e3cd8d07132dd4860809 # v4 + uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 with: path: .cache/backstage-cli key: ${{ runner.os }}-v${{ matrix.node-version }}-backstage-cli-${{ github.run_id }} @@ -253,14 +253,14 @@ jobs: run: yarn backstage-cli repo test --maxWorkers=3 --workerIdleMemoryLimit=1300M --since origin/master --successCache --successCacheDir .cache/backstage-cli env: BACKSTAGE_TEST_DISABLE_DOCKER: 1 - BACKSTAGE_TEST_DATABASE_POSTGRES17_CONNECTION_STRING: postgresql://postgres:postgres@localhost:${{ job.services.postgres17.ports[5432] }} - BACKSTAGE_TEST_DATABASE_POSTGRES13_CONNECTION_STRING: postgresql://postgres:postgres@localhost:${{ job.services.postgres13.ports[5432] }} + BACKSTAGE_TEST_DATABASE_POSTGRES18_CONNECTION_STRING: postgresql://postgres:postgres@localhost:${{ job.services.postgres18.ports[5432] }} + BACKSTAGE_TEST_DATABASE_POSTGRES14_CONNECTION_STRING: postgresql://postgres:postgres@localhost:${{ job.services.postgres14.ports[5432] }} BACKSTAGE_TEST_DATABASE_MYSQL8_CONNECTION_STRING: mysql://root:root@localhost:${{ job.services.mysql8.ports[3306] }}/ignored BACKSTAGE_TEST_CACHE_REDIS7_CONNECTION_STRING: redis://localhost:${{ job.services.redis.ports[6379] }} # Always save success cache even if there were failures, that way it can be used in re-triggered builds - name: save backstage-cli cache - uses: actions/cache/save@0400d5f644dc74513175e3cd8d07132dd4860809 # v4 + uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 if: always() with: path: .cache/backstage-cli diff --git a/.github/workflows/deploy_microsite.yml b/.github/workflows/deploy_microsite.yml index ff0b46f0df..2d61bcc19b 100644 --- a/.github/workflows/deploy_microsite.yml +++ b/.github/workflows/deploy_microsite.yml @@ -28,7 +28,7 @@ jobs: egress-policy: audit - name: find latest release - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7 + uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7 id: find-release with: script: | @@ -82,7 +82,7 @@ jobs: # Use the lower-level cache actions for the success cache, so that we can store the cache even on failed builds - name: restore package-docs cache - uses: actions/cache/restore@0400d5f644dc74513175e3cd8d07132dd4860809 # v4 + uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 with: path: .cache/package-docs key: ${{ runner.os }}-v${{ matrix.node-version }}-package-docs-stable-${{ github.run_id }} @@ -102,7 +102,7 @@ jobs: # Always save success cache even if there were failures, that way it can be used in re-triggered builds - name: save package-docs cache - uses: actions/cache/save@0400d5f644dc74513175e3cd8d07132dd4860809 # v4 + uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 if: always() with: path: .cache/package-docs @@ -166,7 +166,7 @@ jobs: # Use the lower-level cache actions for the success cache, so that we can store the cache even on failed builds - name: restore package-docs cache - uses: actions/cache/restore@0400d5f644dc74513175e3cd8d07132dd4860809 # v4 + uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 with: path: .cache/package-docs key: ${{ runner.os }}-v${{ matrix.node-version }}-package-docs-${{ github.run_id }} @@ -186,7 +186,7 @@ jobs: # Always save success cache even if there were failures, that way it can be used in re-triggered builds - name: save package-docs cache - uses: actions/cache/save@0400d5f644dc74513175e3cd8d07132dd4860809 # v4 + uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 if: always() with: path: .cache/package-docs @@ -329,7 +329,7 @@ jobs: run: ls microsite/build && ls microsite/build/storybook && ls microsite/build/api/stable && ls microsite/build/api/next - name: Deploy both microsite and storybook to gh-pages - uses: JamesIves/github-pages-deploy-action@6c2d9db40f9296374acc17b90404b6e8864128c8 # v4.7.3 + uses: JamesIves/github-pages-deploy-action@4a3abc783e1a24aeb44c16e869ad83caf6b4cc23 # v4.7.4 with: branch: gh-pages folder: microsite/build diff --git a/.github/workflows/deploy_packages.yml b/.github/workflows/deploy_packages.yml index c208d98929..9e4e504e60 100644 --- a/.github/workflows/deploy_packages.yml +++ b/.github/workflows/deploy_packages.yml @@ -16,8 +16,8 @@ jobs: node-version: [20.x, 22.x] services: - postgres17: - image: postgres:17 + postgres18: + image: postgres:18 env: POSTGRES_PASSWORD: postgres options: >- @@ -27,8 +27,8 @@ jobs: --health-retries 5 ports: - 5432/tcp - postgres13: - image: postgres:13 + postgres14: + image: postgres:14 env: POSTGRES_PASSWORD: postgres options: >- @@ -94,7 +94,7 @@ jobs: run: yarn backstage-cli config:check --lax - name: backstage-cli cache - uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4 + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 with: path: .cache/backstage-cli key: ${{ runner.os }}-v${{ matrix.node-version }}-backstage-cli-${{ github.run_id }} @@ -122,8 +122,8 @@ jobs: yarn backstage-cli repo test --maxWorkers=3 --workerIdleMemoryLimit=1300M --coverage --successCache --successCacheDir .cache/backstage-cli env: BACKSTAGE_TEST_DISABLE_DOCKER: 1 - BACKSTAGE_TEST_DATABASE_POSTGRES17_CONNECTION_STRING: postgresql://postgres:postgres@localhost:${{ job.services.postgres17.ports[5432] }} - BACKSTAGE_TEST_DATABASE_POSTGRES13_CONNECTION_STRING: postgresql://postgres:postgres@localhost:${{ job.services.postgres13.ports[5432] }} + BACKSTAGE_TEST_DATABASE_postgres18_CONNECTION_STRING: postgresql://postgres:postgres@localhost:${{ job.services.postgres18.ports[5432] }} + BACKSTAGE_TEST_DATABASE_postgres14_CONNECTION_STRING: postgresql://postgres:postgres@localhost:${{ job.services.postgres14.ports[5432] }} BACKSTAGE_TEST_DATABASE_MYSQL8_CONNECTION_STRING: mysql://root:root@localhost:${{ job.services.mysql8.ports[3306] }}/ignored BACKSTAGE_TEST_CACHE_REDIS7_CONNECTION_STRING: redis://localhost:${{ job.services.redis.ports[6379] }} diff --git a/.github/workflows/issue.yaml b/.github/workflows/issue.yaml index 17c76c19c2..a759adcb46 100644 --- a/.github/workflows/issue.yaml +++ b/.github/workflows/issue.yaml @@ -22,7 +22,7 @@ jobs: # We need to checkout the `.github/ISSUE_TEMPLATE` for the advanced labeler action to be able to read the templates # While at it we might as well checkout all of `.github` so that the labeling actions don't need to fetch their configs - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + - uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4 with: sparse-checkout: .github diff --git a/.github/workflows/mui-migration-tracker.yml b/.github/workflows/mui-migration-tracker.yml new file mode 100644 index 0000000000..4754ec51b0 --- /dev/null +++ b/.github/workflows/mui-migration-tracker.yml @@ -0,0 +1,70 @@ +name: MUI to BUI Migration Tracker + +on: + schedule: + # Run daily at midnight UTC + - cron: '0 0 * * *' + workflow_dispatch: + # Allow manual triggering + +permissions: + issues: write + contents: read + +jobs: + update-migration-progress: + runs-on: ubuntu-latest + name: Update Migration Progress Issue + + steps: + - name: Harden Runner + uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0 + with: + egress-policy: audit + + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + with: + node-version: 20.x + registry-url: https://registry.npmjs.org/ # Needed for auth + + - name: yarn install + uses: backstage/actions/yarn-install@b3c1841fd69e1658ac631afafd0fb140a2309024 # v0.6.17 + with: + cache-prefix: ${{ runner.os }}-v20.x + + - name: Run migration analysis + id: analysis + run: | + # Run the migration script and save markdown output + yarn mui-to-bui --markdown > migration-report.md + + # Read the report into an environment variable (escape for GitHub Actions) + echo "REPORT<> $GITHUB_ENV + cat migration-report.md >> $GITHUB_ENV + echo "EOF" >> $GITHUB_ENV + + - name: Update GitHub Issue + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const issueNumber = 31467; + const reportBody = process.env.REPORT; + + try { + await github.rest.issues.update({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: issueNumber, + body: reportBody + }); + + console.log(`✅ Successfully updated issue #${issueNumber}`); + } catch (error) { + console.error(`❌ Error updating issue: ${error.message}`); + throw error; + } diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 232d055072..0766d1c8a5 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -39,7 +39,7 @@ jobs: persist-credentials: false - name: 'Run analysis' - uses: ossf/scorecard-action@05b42c624433fc40578a4040d5cf5e36ddca8cde # v2.4.2 + uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3 with: results_file: results.sarif results_format: sarif diff --git a/.github/workflows/sync_snyk-github-issues.yml b/.github/workflows/sync_snyk-github-issues.yml index 3716c1f48b..f18455f5fc 100644 --- a/.github/workflows/sync_snyk-github-issues.yml +++ b/.github/workflows/sync_snyk-github-issues.yml @@ -29,7 +29,7 @@ jobs: cache-prefix: ${{ runner.os }}-v20.x - name: Create Snyk report - uses: snyk/actions/node@77490d94e966421e076e95ad8fa87aa55e5ca409 # master + uses: snyk/actions/node@9adf32b1121593767fc3c057af55b55db032dc04 # master continue-on-error: true # Snyk CLI exits with error when vulnerabilities are found with: args: > diff --git a/.github/workflows/sync_snyk-monitor.yml b/.github/workflows/sync_snyk-monitor.yml index 205d146bb8..b928586aa8 100644 --- a/.github/workflows/sync_snyk-monitor.yml +++ b/.github/workflows/sync_snyk-monitor.yml @@ -31,7 +31,7 @@ jobs: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Monitor and Synchronize Snyk Policies - uses: snyk/actions/node@77490d94e966421e076e95ad8fa87aa55e5ca409 # master + uses: snyk/actions/node@9adf32b1121593767fc3c057af55b55db032dc04 # master with: command: monitor args: > @@ -46,7 +46,7 @@ jobs: # Above we run the `monitor` command, this runs the `test` command which is # the one that generates the SARIF report that we can upload to GitHub. - name: Create Snyk report - uses: snyk/actions/node@77490d94e966421e076e95ad8fa87aa55e5ca409 # master + uses: snyk/actions/node@9adf32b1121593767fc3c057af55b55db032dc04 # master continue-on-error: true # To make sure that SARIF upload gets called with: args: > diff --git a/.github/workflows/verify_chromatic.yml b/.github/workflows/verify_chromatic.yml index 0c9f110b4c..e6649c3e55 100644 --- a/.github/workflows/verify_chromatic.yml +++ b/.github/workflows/verify_chromatic.yml @@ -64,21 +64,29 @@ jobs: - name: Prepare Chromatic Message if: github.event_name == 'pull_request' && steps.chromatic.outputs.url - id: prepare-message + id: prepareMessage run: | if [ "${{ steps.chromatic.outputs.changeCount }}" = "0" ] || [ -z "${{ steps.chromatic.outputs.changeCount }}" ]; then - echo "changes-text=No visual changes detected - [**View build in Chromatic**](${{ steps.chromatic.outputs.buildUrl }})" >> $GITHUB_OUTPUT + echo "changesText=No visual changes detected - [**View build in Chromatic**](${{ steps.chromatic.outputs.buildUrl }})" >> $GITHUB_OUTPUT else - echo "changes-text=**${{ steps.chromatic.outputs.changeCount }}** visual changes are waiting for review - [**Review changes in Chromatic**](${{ steps.chromatic.outputs.buildUrl }})" >> $GITHUB_OUTPUT + echo "changesText=**${{ steps.chromatic.outputs.changeCount }}** visual changes are waiting for review - [**Review changes in Chromatic**](${{ steps.chromatic.outputs.buildUrl }})" >> $GITHUB_OUTPUT fi - - name: Post Chromatic Link in PR Comment + - name: Post Chromatic Results to Job Summary if: github.event_name == 'pull_request' && steps.chromatic.outputs.url + run: | + echo "## 🎨 Visual Testing with Chromatic" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "- ${{ steps.prepareMessage.outputs.changesText }}" >> $GITHUB_STEP_SUMMARY + echo "- **${{ steps.chromatic.outputs.specCount}}** stories for **${{ steps.chromatic.outputs.componentCount}}** Components - [**Preview changes in Storybook**](${{ steps.chromatic.outputs.storybookUrl }})" >> $GITHUB_STEP_SUMMARY + + - name: Post Chromatic Link in PR Comment + if: github.event_name == 'pull_request' && steps.chromatic.outputs.url && github.event.pull_request.head.repo.full_name == github.repository uses: mshick/add-pr-comment@v2 with: message: | ## 🎨 Visual Testing with Chromatic - - ${{ steps.prepare-message.outputs.changes-text }} + - ${{ steps.prepareMessage.outputs.changesText }} - **${{ steps.chromatic.outputs.specCount}}** stories for **${{ steps.chromatic.outputs.componentCount}}** Components - [**Preview changes in Storybook**](${{ steps.chromatic.outputs.storybookUrl }}) repo-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/verify_e2e-linux.yml b/.github/workflows/verify_e2e-linux.yml index 5ff4d2b0ce..dc81d42e8a 100644 --- a/.github/workflows/verify_e2e-linux.yml +++ b/.github/workflows/verify_e2e-linux.yml @@ -21,7 +21,7 @@ jobs: services: postgres: - image: postgres:13 + image: postgres:14 env: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres diff --git a/.github/workflows/verify_e2e-techdocs.yml b/.github/workflows/verify_e2e-techdocs.yml index 080d14a743..e933efe3f8 100644 --- a/.github/workflows/verify_e2e-techdocs.yml +++ b/.github/workflows/verify_e2e-techdocs.yml @@ -41,11 +41,14 @@ jobs: with: python-version: '3.9' - - name: install dependencies - run: yarn install --immutable - - - name: generate types - run: yarn tsc + - name: use node.js ${{ matrix.node-version }} + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + with: + node-version: ${{ matrix.node-version }} + - name: yarn install + uses: backstage/actions/yarn-install@b3c1841fd69e1658ac631afafd0fb140a2309024 # v0.6.17 + with: + cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }} - name: build techdocs-cli working-directory: packages/techdocs-cli diff --git a/.github/workflows/verify_microsite.yml b/.github/workflows/verify_microsite.yml index f3d48ef480..67ac397d43 100644 --- a/.github/workflows/verify_microsite.yml +++ b/.github/workflows/verify_microsite.yml @@ -33,7 +33,7 @@ jobs: egress-policy: audit - name: find latest release - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7 + uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7 id: find-release with: script: | @@ -87,7 +87,7 @@ jobs: # Use the lower-level cache actions for the success cache, so that we can store the cache even on failed builds - name: restore package-docs cache - uses: actions/cache/restore@0400d5f644dc74513175e3cd8d07132dd4860809 # v4 + uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 with: path: .cache/package-docs key: ${{ runner.os }}-v${{ matrix.node-version }}-package-docs-stable-${{ github.run_id }} @@ -105,7 +105,7 @@ jobs: # Always save success cache even if there were failures, that way it can be used in re-triggered builds - name: save package-docs cache - uses: actions/cache/save@0400d5f644dc74513175e3cd8d07132dd4860809 # v4 + uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 if: always() with: path: .cache/package-docs @@ -168,7 +168,7 @@ jobs: # Use the lower-level cache actions for the success cache, so that we can store the cache even on failed builds - name: restore package-docs cache - uses: actions/cache/restore@0400d5f644dc74513175e3cd8d07132dd4860809 # v4 + uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 with: path: .cache/package-docs key: ${{ runner.os }}-v${{ matrix.node-version }}-package-docs-next-${{ github.run_id }} @@ -186,7 +186,7 @@ jobs: # Always save success cache even if there were failures, that way it can be used in re-triggered builds - name: save package-docs cache - uses: actions/cache/save@0400d5f644dc74513175e3cd8d07132dd4860809 # v4 + uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4 if: always() with: path: .cache/package-docs diff --git a/.github/workflows/welcome.yml b/.github/workflows/welcome.yml new file mode 100644 index 0000000000..5d47ba77da --- /dev/null +++ b/.github/workflows/welcome.yml @@ -0,0 +1,42 @@ +name: Add a welcome comment + +on: + pull_request_target: + types: [opened] + +permissions: + issues: write + pull-requests: write + contents: read + +jobs: + welcome: + runs-on: ubuntu-latest + if: github.event.pull_request.author_association == 'FIRST_TIME_CONTRIBUTOR' + steps: + - name: Add a welcome comment + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const pr = context.payload.pull_request + const login = pr.user.login + + const message = ` + Hi @${login}, thanks for opening your first pull request in Backstage! 👋 + + A couple of useful links to help you get started: + + * [Contributing Guide](https://github.com/backstage/backstage/blob/master/CONTRIBUTING.md) + * [DCO Sign-off Instructions](https://github.com/backstage/backstage/blob/master/DCO) + * [Style Guide](https://github.com/backstage/backstage/blob/master/STYLE.md) + + We really appreciate your contribution and look forward to reviewing your work. Welcome aboard! + ` + + await github.rest.issues.createComment({ + issue_number: pr.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: message + }) diff --git a/ADOPTERS.md b/ADOPTERS.md index f0f880e89d..208cd370ba 100644 --- a/ADOPTERS.md +++ b/ADOPTERS.md @@ -286,3 +286,4 @@ _You can do this by using the [Adopter form](https://info.backstage.spotify.com/ | [Plata](https://platacard.mx/en) | [Shamil Ganiev](https://github.com/ganievs) | Internal Developer Portal for the whole engineering team | | [Syngenta Digital](https://www.syngentadigital.com) | [Bitan Mallick](https://www.linkedin.com/in/bitanmallick) | Internal developer portal, designed to empower developers and streamline workflows. We use software catalog, tech-radar, software templates, tech-docs and various custom plugins to ensure an efficient and collaborative development experience. | | [Sophotech](https://sopho.tech) | [@archy-rock3t-cloud](https://github.com/archy-rock3t-cloud), [Artem Muterko](mailto:artem@sopho.tech) | Custom Developer Platform based on Backstage, providing a service catalog, infrastructure templates, and integrated tooling to give developers a self-service experience. | +| [Swiss Mobiliar Insurance Company Ltd.](https://www.mobiliar.ch/) | [Patrick Wyler](mailto:patrick.wyler@mobiliar.ch) and [Beat Winistörfer](mailto:beat.winistoerfer@mobiliar.ch) | The portal provides a unified interface for accessing all relevant DevOps information previously scattered across various locations, enhancing accessibility and clarity for all IT employees. It relies on an internal graph database that enhances the Backstage software catalog with many additional elements. Significant effort has been invested in the visual representation of information through graphs and diagrams, facilitating analysis and improving the understanding of dependencies. | diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9413206197..ba40be86c8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -102,6 +102,16 @@ Visit and you should see the bleeding edge of Backstage If you want to get a better understanding of the layout of the repo now that you have a local copy running feel free to review the [Backstage Project Structure](https://backstage.io/docs/getting-started/#general-folder-structure) documentation. +#### Using Docker for the Example App + +You can run the Example App using Docker with Postgres, OpenSearch and Redis services. This setup very closely resembles how Backstage is run in production in many occasions. + +To start the Example App with Docker, make sure you have Docker and Docker Compose installed on your machine, then run the following command from the root of the repository: + +```bash +yarn start:docker +``` + ## Coding Guidelines All code is formatted with `prettier` using the configuration in the repo. If possible, we recommend configuring your editor to format automatically, but you can also use the `yarn prettier --write ` command to format files. diff --git a/README.md b/README.md index e3e9bd77dd..545874adf9 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,6 @@ +> [!NOTE] +> ✈️ From Monday November 10th through November 14rd, several maintainers and Spotify employees will be at KubeCon / BackstageCon! Expect the project to move a little slower than normal, and support to be limited. Normal service will resume after that! And do come visit our booth if you are there. ✈️ + [![headline](docs/assets/headline.png)](https://backstage.io/) # [Backstage](https://backstage.io) diff --git a/app-config.docker.yaml b/app-config.docker.yaml new file mode 100644 index 0000000000..07f7c7ac8c --- /dev/null +++ b/app-config.docker.yaml @@ -0,0 +1,23 @@ +# To run local backstage instance with docker services, +# run: +# 1. `docker compose -f docker-compose.deps.yml up --wait` +# 2. `BACKSTAGE_ENV=docker yarn dev` +backend: + database: + client: pg + connection: + host: localhost + port: 5432 + user: postgres + password: postgres + cache: + store: redis + connection: redis://localhost:6379 + +search: + elasticsearch: + provider: opensearch + node: 'http://localhost:9200' + auth: + username: admin + password: admin diff --git a/app-config.yaml b/app-config.yaml index bd3ebd87ff..92b7dfb848 100644 --- a/app-config.yaml +++ b/app-config.yaml @@ -65,7 +65,9 @@ backend: - host: example.com - host: '*.mozilla.org' # workingDirectory: /tmp # Use this to configure a working directory for the scaffolder, defaults to the OS temp-dir - + actions: + pluginSources: + - catalog # See README.md in the proxy-backend plugin for information on the configuration format proxy: endpoints: diff --git a/contrib/docker/frontend-with-nginx/Dockerfile.dockerbuild b/contrib/docker/frontend-with-nginx/Dockerfile.dockerbuild index 47fd506a34..6c87681391 100644 --- a/contrib/docker/frontend-with-nginx/Dockerfile.dockerbuild +++ b/contrib/docker/frontend-with-nginx/Dockerfile.dockerbuild @@ -6,7 +6,7 @@ # This dockerfile also performs the build first inside docker. This may come # with a build time impact, but is sometimes desirable. If you want to run the -# build on the host instead, use the file simply named Dockerfile in this folder +# build on the host instead, use the file simply named Dockerfile.hostbuild in this folder # instead. # USAGE: diff --git a/docker-compose.deps.yml b/docker-compose.deps.yml new file mode 100644 index 0000000000..2ad65d6085 --- /dev/null +++ b/docker-compose.deps.yml @@ -0,0 +1,39 @@ +#!/usr/bin/env docker compose -f +name: backstage +services: + psql: + image: postgres:17.6 + environment: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + ports: + - '5432:5432' + healthcheck: + test: ['CMD-SHELL', 'pg_isready'] + interval: 10s + retries: 5 + start_period: 30s + timeout: 10s + redis: + image: redis:8.2.1-alpine + ports: + - '6379:6379' + healthcheck: + test: ['CMD-SHELL', 'redis-cli ping | grep PONG'] + interval: 1s + timeout: 3s + retries: 5 + opensearch: + image: opensearchproject/opensearch:2.19.4 + environment: + plugins.security.disabled: true + discovery.type: single-node + OPENSEARCH_INITIAL_ADMIN_PASSWORD: Opensearch1! + ports: + - '9200:9200' + - '9600:9600' + healthcheck: + test: 'curl --fail opensearch:9200/_cat/health >/dev/null || exit 1' + interval: 5s + timeout: 5s + retries: 30 diff --git a/docs-ui/next.config.mjs b/docs-ui/next.config.mjs index 8e6ed8fdc0..2ff341b609 100644 --- a/docs-ui/next.config.mjs +++ b/docs-ui/next.config.mjs @@ -1,4 +1,6 @@ import createMDX from '@next/mdx'; +import path from 'path'; +import { fileURLToPath } from 'url'; const nextConfig = { pageExtensions: ['js', 'jsx', 'md', 'mdx', 'ts', 'tsx'], @@ -13,6 +15,10 @@ const nextConfig = { // These are type-level conflicts that don't affect runtime behavior ignoreBuildErrors: true, }, + outputFileTracingRoot: path.join( + path.dirname(fileURLToPath(import.meta.url)), + '..', + ), }; const withMDX = createMDX({}); diff --git a/docs-ui/package.json b/docs-ui/package.json index 50bcae8d72..d05fdf81bb 100644 --- a/docs-ui/package.json +++ b/docs-ui/package.json @@ -7,7 +7,7 @@ "build": "next build", "lint": "next lint", "prestart": "yarn sync:css", - "start": "concurrently \"yarn sync:css:watch\" \"next dev\"", + "start": "next dev", "sync:css": "node scripts/sync-css.js", "sync:css:watch": "node scripts/sync-css.js --watch" }, @@ -16,6 +16,7 @@ "@types/react-dom": "19.1.7" }, "dependencies": { + "@base-ui-components/react": "^1.0.0-beta.4", "@codemirror/lang-sass": "^6.0.2", "@codemirror/view": "^6.34.4", "@lezer/highlight": "^1.2.1", @@ -26,6 +27,7 @@ "@storybook/react": "^8.6.12", "@uiw/codemirror-themes": "^4.23.7", "@uiw/react-codemirror": "^4.23.7", + "clsx": "^2.1.1", "html-react-parser": "^5.2.5", "motion": "^12.4.1", "next": "15.4.7", @@ -33,16 +35,16 @@ "prop-types": "^15.8.1", "react": "19.1.1", "react-dom": "19.1.1", - "shiki": "^1.26.1", + "shiki": "^3.13.0", "storybook": "^8.6.12" }, "devDependencies": { + "@shikijs/transformers": "^3.13.0", "@types/mdx": "^2.0.13", "@types/node": "^20", "@types/react": "19.1.9", "@types/react-dom": "19.1.7", "chokidar": "^3.6.0", - "concurrently": "^8.2.2", "eslint": "^8", "eslint-config-next": "15.3.4", "lightningcss": "^1.28.2", diff --git a/docs-ui/public/theme-backstage.css b/docs-ui/public/theme-backstage.css index 98b0f62b47..59a5b986be 100644 --- a/docs-ui/public/theme-backstage.css +++ b/docs-ui/public/theme-backstage.css @@ -1,2 +1 @@ -/*! modern-normalize v3.0.1 | MIT License | https://github.com/sindresorhus/modern-normalize */ -@layer base{*,:before,:after{box-sizing:border-box}html{-webkit-text-size-adjust:100%;tab-size:4;font-family:system-ui,Segoe UI,Roboto,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji;line-height:1.15}body{margin:0}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Consolas,Liberation Mono,Menlo,monospace;font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{border-color:currentColor}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:100%;line-height:1.15}button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button}legend{padding:0}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}}.bui-p{padding:var(--p)}.bui-p-0\.5{padding:var(--bui-space-0_5)}.bui-p-1{padding:var(--bui-space-1)}.bui-p-1\.5{padding:var(--bui-space-1_5)}.bui-p-2{padding:var(--bui-space-2)}.bui-p-3{padding:var(--bui-space-3)}.bui-p-4{padding:var(--bui-space-4)}.bui-p-5{padding:var(--bui-space-5)}.bui-p-6{padding:var(--bui-space-6)}.bui-p-7{padding:var(--bui-space-7)}.bui-p-8{padding:var(--bui-space-8)}.bui-p-9{padding:var(--bui-space-9)}.bui-p-10{padding:var(--bui-space-10)}.bui-p-11{padding:var(--bui-space-11)}.bui-p-12{padding:var(--bui-space-12)}.bui-p-13{padding:var(--bui-space-13)}.bui-p-14{padding:var(--bui-space-14)}@media (width>=640px){.xs\:bui-p{padding:var(--p-xs)}.xs\:bui-p-0\.5{padding:var(--bui-space-0_5)}.xs\:bui-p-1{padding:var(--bui-space-1)}.xs\:bui-p-1\.5{padding:var(--bui-space-1_5)}.xs\:bui-p-2{padding:var(--bui-space-2)}.xs\:bui-p-3{padding:var(--bui-space-3)}.xs\:bui-p-4{padding:var(--bui-space-4)}.xs\:bui-p-5{padding:var(--bui-space-5)}.xs\:bui-p-6{padding:var(--bui-space-6)}.xs\:bui-p-7{padding:var(--bui-space-7)}.xs\:bui-p-8{padding:var(--bui-space-8)}.xs\:bui-p-9{padding:var(--bui-space-9)}.xs\:bui-p-10{padding:var(--bui-space-10)}.xs\:bui-p-11{padding:var(--bui-space-11)}.xs\:bui-p-12{padding:var(--bui-space-12)}.xs\:bui-p-13{padding:var(--bui-space-13)}.xs\:bui-p-14{padding:var(--bui-space-14)}}@media (width>=768px){.sm\:bui-p{padding:var(--p-sm)}.sm\:bui-p-0\.5{padding:var(--bui-space-0_5)}.sm\:bui-p-1{padding:var(--bui-space-1)}.sm\:bui-p-1\.5{padding:var(--bui-space-1_5)}.sm\:bui-p-2{padding:var(--bui-space-2)}.sm\:bui-p-3{padding:var(--bui-space-3)}.sm\:bui-p-4{padding:var(--bui-space-4)}.sm\:bui-p-5{padding:var(--bui-space-5)}.sm\:bui-p-6{padding:var(--bui-space-6)}.sm\:bui-p-7{padding:var(--bui-space-7)}.sm\:bui-p-8{padding:var(--bui-space-8)}.sm\:bui-p-9{padding:var(--bui-space-9)}.sm\:bui-p-10{padding:var(--bui-space-10)}.sm\:bui-p-11{padding:var(--bui-space-11)}.sm\:bui-p-12{padding:var(--bui-space-12)}.sm\:bui-p-13{padding:var(--bui-space-13)}.sm\:bui-p-14{padding:var(--bui-space-14)}}@media (width>=1024px){.md\:bui-p{padding:var(--p-md)}.md\:bui-p-0\.5{padding:var(--bui-space-0_5)}.md\:bui-p-1{padding:var(--bui-space-1)}.md\:bui-p-1\.5{padding:var(--bui-space-1_5)}.md\:bui-p-2{padding:var(--bui-space-2)}.md\:bui-p-3{padding:var(--bui-space-3)}.md\:bui-p-4{padding:var(--bui-space-4)}.md\:bui-p-5{padding:var(--bui-space-5)}.md\:bui-p-6{padding:var(--bui-space-6)}.md\:bui-p-7{padding:var(--bui-space-7)}.md\:bui-p-8{padding:var(--bui-space-8)}.md\:bui-p-9{padding:var(--bui-space-9)}.md\:bui-p-10{padding:var(--bui-space-10)}.md\:bui-p-11{padding:var(--bui-space-11)}.md\:bui-p-12{padding:var(--bui-space-12)}.md\:bui-p-13{padding:var(--bui-space-13)}.md\:bui-p-14{padding:var(--bui-space-14)}}@media (width>=1280px){.lg\:bui-p{padding:var(--p-lg)}.lg\:bui-p-0\.5{padding:var(--bui-space-0_5)}.lg\:bui-p-1{padding:var(--bui-space-1)}.lg\:bui-p-1\.5{padding:var(--bui-space-1_5)}.lg\:bui-p-2{padding:var(--bui-space-2)}.lg\:bui-p-3{padding:var(--bui-space-3)}.lg\:bui-p-4{padding:var(--bui-space-4)}.lg\:bui-p-5{padding:var(--bui-space-5)}.lg\:bui-p-6{padding:var(--bui-space-6)}.lg\:bui-p-7{padding:var(--bui-space-7)}.lg\:bui-p-8{padding:var(--bui-space-8)}.lg\:bui-p-9{padding:var(--bui-space-9)}.lg\:bui-p-10{padding:var(--bui-space-10)}.lg\:bui-p-11{padding:var(--bui-space-11)}.lg\:bui-p-12{padding:var(--bui-space-12)}.lg\:bui-p-13{padding:var(--bui-space-13)}.lg\:bui-p-14{padding:var(--bui-space-14)}}@media (width>=1536px){.xl\:bui-p{padding:var(--p-xl)}.xl\:bui-p-0\.5{padding:var(--bui-space-0_5)}.xl\:bui-p-1{padding:var(--bui-space-1)}.xl\:bui-p-1\.5{padding:var(--bui-space-1_5)}.xl\:bui-p-2{padding:var(--bui-space-2)}.xl\:bui-p-3{padding:var(--bui-space-3)}.xl\:bui-p-4{padding:var(--bui-space-4)}.xl\:bui-p-5{padding:var(--bui-space-5)}.xl\:bui-p-6{padding:var(--bui-space-6)}.xl\:bui-p-7{padding:var(--bui-space-7)}.xl\:bui-p-8{padding:var(--bui-space-8)}.xl\:bui-p-9{padding:var(--bui-space-9)}.xl\:bui-p-10{padding:var(--bui-space-10)}.xl\:bui-p-11{padding:var(--bui-space-11)}.xl\:bui-p-12{padding:var(--bui-space-12)}.xl\:bui-p-13{padding:var(--bui-space-13)}.xl\:bui-p-14{padding:var(--bui-space-14)}}.bui-pl{padding-left:var(--pl)}.bui-pl-0\.5{padding-left:var(--bui-space-0_5)}.bui-pl-1{padding-left:var(--bui-space-1)}.bui-pl-1\.5{padding-left:var(--bui-space-1_5)}.bui-pl-2{padding-left:var(--bui-space-2)}.bui-pl-3{padding-left:var(--bui-space-3)}.bui-pl-4{padding-left:var(--bui-space-4)}.bui-pl-5{padding-left:var(--bui-space-5)}.bui-pl-6{padding-left:var(--bui-space-6)}.bui-pl-7{padding-left:var(--bui-space-7)}.bui-pl-8{padding-left:var(--bui-space-8)}.bui-pl-9{padding-left:var(--bui-space-9)}.bui-pl-10{padding-left:var(--bui-space-10)}.bui-pl-11{padding-left:var(--bui-space-11)}.bui-pl-12{padding-left:var(--bui-space-12)}.bui-pl-13{padding-left:var(--bui-space-13)}.bui-pl-14{padding-left:var(--bui-space-14)}@media (width>=640px){.xs\:bui-pl{padding-left:var(--pl-xs)}.xs\:bui-pl-0\.5{padding-left:var(--bui-space-0_5)}.xs\:bui-pl-1{padding-left:var(--bui-space-1)}.xs\:bui-pl-1\.5{padding-left:var(--bui-space-1_5)}.xs\:bui-pl-2{padding-left:var(--bui-space-2)}.xs\:bui-pl-3{padding-left:var(--bui-space-3)}.xs\:bui-pl-4{padding-left:var(--bui-space-4)}.xs\:bui-pl-5{padding-left:var(--bui-space-5)}.xs\:bui-pl-6{padding-left:var(--bui-space-6)}.xs\:bui-pl-7{padding-left:var(--bui-space-7)}.xs\:bui-pl-8{padding-left:var(--bui-space-8)}.xs\:bui-pl-9{padding-left:var(--bui-space-9)}.xs\:bui-pl-10{padding-left:var(--bui-space-10)}.xs\:bui-pl-11{padding-left:var(--bui-space-11)}.xs\:bui-pl-12{padding-left:var(--bui-space-12)}.xs\:bui-pl-13{padding-left:var(--bui-space-13)}.xs\:bui-pl-14{padding-left:var(--bui-space-14)}}@media (width>=768px){.sm\:bui-pl{padding-left:var(--pl-sm)}.sm\:bui-pl-0\.5{padding-left:var(--bui-space-0_5)}.sm\:bui-pl-1{padding-left:var(--bui-space-1)}.sm\:bui-pl-1\.5{padding-left:var(--bui-space-1_5)}.sm\:bui-pl-2{padding-left:var(--bui-space-2)}.sm\:bui-pl-3{padding-left:var(--bui-space-3)}.sm\:bui-pl-4{padding-left:var(--bui-space-4)}.sm\:bui-pl-5{padding-left:var(--bui-space-5)}.sm\:bui-pl-6{padding-left:var(--bui-space-6)}.sm\:bui-pl-7{padding-left:var(--bui-space-7)}.sm\:bui-pl-8{padding-left:var(--bui-space-8)}.sm\:bui-pl-9{padding-left:var(--bui-space-9)}.sm\:bui-pl-10{padding-left:var(--bui-space-10)}.sm\:bui-pl-11{padding-left:var(--bui-space-11)}.sm\:bui-pl-12{padding-left:var(--bui-space-12)}.sm\:bui-pl-13{padding-left:var(--bui-space-13)}.sm\:bui-pl-14{padding-left:var(--bui-space-14)}}@media (width>=1024px){.md\:bui-pl{padding-left:var(--pl-md)}.md\:bui-pl-0\.5{padding-left:var(--bui-space-0_5)}.md\:bui-pl-1{padding-left:var(--bui-space-1)}.md\:bui-pl-1\.5{padding-left:var(--bui-space-1_5)}.md\:bui-pl-2{padding-left:var(--bui-space-2)}.md\:bui-pl-3{padding-left:var(--bui-space-3)}.md\:bui-pl-4{padding-left:var(--bui-space-4)}.md\:bui-pl-5{padding-left:var(--bui-space-5)}.md\:bui-pl-6{padding-left:var(--bui-space-6)}.md\:bui-pl-7{padding-left:var(--bui-space-7)}.md\:bui-pl-8{padding-left:var(--bui-space-8)}.md\:bui-pl-9{padding-left:var(--bui-space-9)}.md\:bui-pl-10{padding-left:var(--bui-space-10)}.md\:bui-pl-11{padding-left:var(--bui-space-11)}.md\:bui-pl-12{padding-left:var(--bui-space-12)}.md\:bui-pl-13{padding-left:var(--bui-space-13)}.md\:bui-pl-14{padding-left:var(--bui-space-14)}}@media (width>=1280px){.lg\:bui-pl{padding-left:var(--pl-lg)}.lg\:bui-pl-0\.5{padding-left:var(--bui-space-0_5)}.lg\:bui-pl-1{padding-left:var(--bui-space-1)}.lg\:bui-pl-1\.5{padding-left:var(--bui-space-1_5)}.lg\:bui-pl-2{padding-left:var(--bui-space-2)}.lg\:bui-pl-3{padding-left:var(--bui-space-3)}.lg\:bui-pl-4{padding-left:var(--bui-space-4)}.lg\:bui-pl-5{padding-left:var(--bui-space-5)}.lg\:bui-pl-6{padding-left:var(--bui-space-6)}.lg\:bui-pl-7{padding-left:var(--bui-space-7)}.lg\:bui-pl-8{padding-left:var(--bui-space-8)}.lg\:bui-pl-9{padding-left:var(--bui-space-9)}.lg\:bui-pl-10{padding-left:var(--bui-space-10)}.lg\:bui-pl-11{padding-left:var(--bui-space-11)}.lg\:bui-pl-12{padding-left:var(--bui-space-12)}.lg\:bui-pl-13{padding-left:var(--bui-space-13)}.lg\:bui-pl-14{padding-left:var(--bui-space-14)}}@media (width>=1536px){.xl\:bui-pl{padding-left:var(--pl-xl)}.xl\:bui-pl-0\.5{padding-left:var(--bui-space-0_5)}.xl\:bui-pl-1{padding-left:var(--bui-space-1)}.xl\:bui-pl-1\.5{padding-left:var(--bui-space-1_5)}.xl\:bui-pl-2{padding-left:var(--bui-space-2)}.xl\:bui-pl-3{padding-left:var(--bui-space-3)}.xl\:bui-pl-4{padding-left:var(--bui-space-4)}.xl\:bui-pl-5{padding-left:var(--bui-space-5)}.xl\:bui-pl-6{padding-left:var(--bui-space-6)}.xl\:bui-pl-7{padding-left:var(--bui-space-7)}.xl\:bui-pl-8{padding-left:var(--bui-space-8)}.xl\:bui-pl-9{padding-left:var(--bui-space-9)}.xl\:bui-pl-10{padding-left:var(--bui-space-10)}.xl\:bui-pl-11{padding-left:var(--bui-space-11)}.xl\:bui-pl-12{padding-left:var(--bui-space-12)}.xl\:bui-pl-13{padding-left:var(--bui-space-13)}.xl\:bui-pl-14{padding-left:var(--bui-space-14)}}.bui-pr{padding-right:var(--pr)}.bui-pr-0\.5{padding-right:var(--bui-space-0_5)}.bui-pr-1{padding-right:var(--bui-space-1)}.bui-pr-1\.5{padding-right:var(--bui-space-1_5)}.bui-pr-2{padding-right:var(--bui-space-2)}.bui-pr-3{padding-right:var(--bui-space-3)}.bui-pr-4{padding-right:var(--bui-space-4)}.bui-pr-5{padding-right:var(--bui-space-5)}.bui-pr-6{padding-right:var(--bui-space-6)}.bui-pr-7{padding-right:var(--bui-space-7)}.bui-pr-8{padding-right:var(--bui-space-8)}.bui-pr-9{padding-right:var(--bui-space-9)}.bui-pr-10{padding-right:var(--bui-space-10)}.bui-pr-11{padding-right:var(--bui-space-11)}.bui-pr-12{padding-right:var(--bui-space-12)}.bui-pr-13{padding-right:var(--bui-space-13)}.bui-pr-14{padding-right:var(--bui-space-14)}@media (width>=640px){.xs\:bui-pr{padding-right:var(--pr-xs)}.xs\:bui-pr-0\.5{padding-right:var(--bui-space-0_5)}.xs\:bui-pr-1{padding-right:var(--bui-space-1)}.xs\:bui-pr-1\.5{padding-right:var(--bui-space-1_5)}.xs\:bui-pr-2{padding-right:var(--bui-space-2)}.xs\:bui-pr-3{padding-right:var(--bui-space-3)}.xs\:bui-pr-4{padding-right:var(--bui-space-4)}.xs\:bui-pr-5{padding-right:var(--bui-space-5)}.xs\:bui-pr-6{padding-right:var(--bui-space-6)}.xs\:bui-pr-7{padding-right:var(--bui-space-7)}.xs\:bui-pr-8{padding-right:var(--bui-space-8)}.xs\:bui-pr-9{padding-right:var(--bui-space-9)}.xs\:bui-pr-10{padding-right:var(--bui-space-10)}.xs\:bui-pr-11{padding-right:var(--bui-space-11)}.xs\:bui-pr-12{padding-right:var(--bui-space-12)}.xs\:bui-pr-13{padding-right:var(--bui-space-13)}.xs\:bui-pr-14{padding-right:var(--bui-space-14)}}@media (width>=768px){.sm\:bui-pr{padding-right:var(--pr-sm)}.sm\:bui-pr-0\.5{padding-right:var(--bui-space-0_5)}.sm\:bui-pr-1{padding-right:var(--bui-space-1)}.sm\:bui-pr-1\.5{padding-right:var(--bui-space-1_5)}.sm\:bui-pr-2{padding-right:var(--bui-space-2)}.sm\:bui-pr-3{padding-right:var(--bui-space-3)}.sm\:bui-pr-4{padding-right:var(--bui-space-4)}.sm\:bui-pr-5{padding-right:var(--bui-space-5)}.sm\:bui-pr-6{padding-right:var(--bui-space-6)}.sm\:bui-pr-7{padding-right:var(--bui-space-7)}.sm\:bui-pr-8{padding-right:var(--bui-space-8)}.sm\:bui-pr-9{padding-right:var(--bui-space-9)}.sm\:bui-pr-10{padding-right:var(--bui-space-10)}.sm\:bui-pr-11{padding-right:var(--bui-space-11)}.sm\:bui-pr-12{padding-right:var(--bui-space-12)}.sm\:bui-pr-13{padding-right:var(--bui-space-13)}.sm\:bui-pr-14{padding-right:var(--bui-space-14)}}@media (width>=1024px){.md\:bui-pr{padding-right:var(--pr-md)}.md\:bui-pr-0\.5{padding-right:var(--bui-space-0_5)}.md\:bui-pr-1{padding-right:var(--bui-space-1)}.md\:bui-pr-1\.5{padding-right:var(--bui-space-1_5)}.md\:bui-pr-2{padding-right:var(--bui-space-2)}.md\:bui-pr-3{padding-right:var(--bui-space-3)}.md\:bui-pr-4{padding-right:var(--bui-space-4)}.md\:bui-pr-5{padding-right:var(--bui-space-5)}.md\:bui-pr-6{padding-right:var(--bui-space-6)}.md\:bui-pr-7{padding-right:var(--bui-space-7)}.md\:bui-pr-8{padding-right:var(--bui-space-8)}.md\:bui-pr-9{padding-right:var(--bui-space-9)}.md\:bui-pr-10{padding-right:var(--bui-space-10)}.md\:bui-pr-11{padding-right:var(--bui-space-11)}.md\:bui-pr-12{padding-right:var(--bui-space-12)}.md\:bui-pr-13{padding-right:var(--bui-space-13)}.md\:bui-pr-14{padding-right:var(--bui-space-14)}}@media (width>=1280px){.lg\:bui-pr{padding-right:var(--pr-lg)}.lg\:bui-pr-0\.5{padding-right:var(--bui-space-0_5)}.lg\:bui-pr-1{padding-right:var(--bui-space-1)}.lg\:bui-pr-1\.5{padding-right:var(--bui-space-1_5)}.lg\:bui-pr-2{padding-right:var(--bui-space-2)}.lg\:bui-pr-3{padding-right:var(--bui-space-3)}.lg\:bui-pr-4{padding-right:var(--bui-space-4)}.lg\:bui-pr-5{padding-right:var(--bui-space-5)}.lg\:bui-pr-6{padding-right:var(--bui-space-6)}.lg\:bui-pr-7{padding-right:var(--bui-space-7)}.lg\:bui-pr-8{padding-right:var(--bui-space-8)}.lg\:bui-pr-9{padding-right:var(--bui-space-9)}.lg\:bui-pr-10{padding-right:var(--bui-space-10)}.lg\:bui-pr-11{padding-right:var(--bui-space-11)}.lg\:bui-pr-12{padding-right:var(--bui-space-12)}.lg\:bui-pr-13{padding-right:var(--bui-space-13)}.lg\:bui-pr-14{padding-right:var(--bui-space-14)}}@media (width>=1536px){.xl\:bui-pr{padding-right:var(--pr-xl)}.xl\:bui-pr-0\.5{padding-right:var(--bui-space-0_5)}.xl\:bui-pr-1{padding-right:var(--bui-space-1)}.xl\:bui-pr-1\.5{padding-right:var(--bui-space-1_5)}.xl\:bui-pr-2{padding-right:var(--bui-space-2)}.xl\:bui-pr-3{padding-right:var(--bui-space-3)}.xl\:bui-pr-4{padding-right:var(--bui-space-4)}.xl\:bui-pr-5{padding-right:var(--bui-space-5)}.xl\:bui-pr-6{padding-right:var(--bui-space-6)}.xl\:bui-pr-7{padding-right:var(--bui-space-7)}.xl\:bui-pr-8{padding-right:var(--bui-space-8)}.xl\:bui-pr-9{padding-right:var(--bui-space-9)}.xl\:bui-pr-10{padding-right:var(--bui-space-10)}.xl\:bui-pr-11{padding-right:var(--bui-space-11)}.xl\:bui-pr-12{padding-right:var(--bui-space-12)}.xl\:bui-pr-13{padding-right:var(--bui-space-13)}.xl\:bui-pr-14{padding-right:var(--bui-space-14)}}.bui-pt{padding-top:var(--pt)}.bui-pt-0\.5{padding-top:var(--bui-space-0_5)}.bui-pt-1{padding-top:var(--bui-space-1)}.bui-pt-1\.5{padding-top:var(--bui-space-1_5)}.bui-pt-2{padding-top:var(--bui-space-2)}.bui-pt-3{padding-top:var(--bui-space-3)}.bui-pt-4{padding-top:var(--bui-space-4)}.bui-pt-5{padding-top:var(--bui-space-5)}.bui-pt-6{padding-top:var(--bui-space-6)}.bui-pt-7{padding-top:var(--bui-space-7)}.bui-pt-8{padding-top:var(--bui-space-8)}.bui-pt-9{padding-top:var(--bui-space-9)}.bui-pt-10{padding-top:var(--bui-space-10)}.bui-pt-11{padding-top:var(--bui-space-11)}.bui-pt-12{padding-top:var(--bui-space-12)}.bui-pt-13{padding-top:var(--bui-space-13)}.bui-pt-14{padding-top:var(--bui-space-14)}@media (width>=640px){.xs\:bui-pt{padding-top:var(--pt-xs)}.xs\:bui-pt-0\.5{padding-top:var(--bui-space-0_5)}.xs\:bui-pt-1{padding-top:var(--bui-space-1)}.xs\:bui-pt-1\.5{padding-top:var(--bui-space-1_5)}.xs\:bui-pt-2{padding-top:var(--bui-space-2)}.xs\:bui-pt-3{padding-top:var(--bui-space-3)}.xs\:bui-pt-4{padding-top:var(--bui-space-4)}.xs\:bui-pt-5{padding-top:var(--bui-space-5)}.xs\:bui-pt-6{padding-top:var(--bui-space-6)}.xs\:bui-pt-7{padding-top:var(--bui-space-7)}.xs\:bui-pt-8{padding-top:var(--bui-space-8)}.xs\:bui-pt-9{padding-top:var(--bui-space-9)}.xs\:bui-pt-10{padding-top:var(--bui-space-10)}.xs\:bui-pt-11{padding-top:var(--bui-space-11)}.xs\:bui-pt-12{padding-top:var(--bui-space-12)}.xs\:bui-pt-13{padding-top:var(--bui-space-13)}.xs\:bui-pt-14{padding-top:var(--bui-space-14)}}@media (width>=768px){.sm\:bui-pt{padding-top:var(--pt-sm)}.sm\:bui-pt-0\.5{padding-top:var(--bui-space-0_5)}.sm\:bui-pt-1{padding-top:var(--bui-space-1)}.sm\:bui-pt-1\.5{padding-top:var(--bui-space-1_5)}.sm\:bui-pt-2{padding-top:var(--bui-space-2)}.sm\:bui-pt-3{padding-top:var(--bui-space-3)}.sm\:bui-pt-4{padding-top:var(--bui-space-4)}.sm\:bui-pt-5{padding-top:var(--bui-space-5)}.sm\:bui-pt-6{padding-top:var(--bui-space-6)}.sm\:bui-pt-7{padding-top:var(--bui-space-7)}.sm\:bui-pt-8{padding-top:var(--bui-space-8)}.sm\:bui-pt-9{padding-top:var(--bui-space-9)}.sm\:bui-pt-10{padding-top:var(--bui-space-10)}.sm\:bui-pt-11{padding-top:var(--bui-space-11)}.sm\:bui-pt-12{padding-top:var(--bui-space-12)}.sm\:bui-pt-13{padding-top:var(--bui-space-13)}.sm\:bui-pt-14{padding-top:var(--bui-space-14)}}@media (width>=1024px){.md\:bui-pt{padding-top:var(--pt-md)}.md\:bui-pt-0\.5{padding-top:var(--bui-space-0_5)}.md\:bui-pt-1{padding-top:var(--bui-space-1)}.md\:bui-pt-1\.5{padding-top:var(--bui-space-1_5)}.md\:bui-pt-2{padding-top:var(--bui-space-2)}.md\:bui-pt-3{padding-top:var(--bui-space-3)}.md\:bui-pt-4{padding-top:var(--bui-space-4)}.md\:bui-pt-5{padding-top:var(--bui-space-5)}.md\:bui-pt-6{padding-top:var(--bui-space-6)}.md\:bui-pt-7{padding-top:var(--bui-space-7)}.md\:bui-pt-8{padding-top:var(--bui-space-8)}.md\:bui-pt-9{padding-top:var(--bui-space-9)}.md\:bui-pt-10{padding-top:var(--bui-space-10)}.md\:bui-pt-11{padding-top:var(--bui-space-11)}.md\:bui-pt-12{padding-top:var(--bui-space-12)}.md\:bui-pt-13{padding-top:var(--bui-space-13)}.md\:bui-pt-14{padding-top:var(--bui-space-14)}}@media (width>=1280px){.lg\:bui-pt{padding-top:var(--pt-lg)}.lg\:bui-pt-0\.5{padding-top:var(--bui-space-0_5)}.lg\:bui-pt-1{padding-top:var(--bui-space-1)}.lg\:bui-pt-1\.5{padding-top:var(--bui-space-1_5)}.lg\:bui-pt-2{padding-top:var(--bui-space-2)}.lg\:bui-pt-3{padding-top:var(--bui-space-3)}.lg\:bui-pt-4{padding-top:var(--bui-space-4)}.lg\:bui-pt-5{padding-top:var(--bui-space-5)}.lg\:bui-pt-6{padding-top:var(--bui-space-6)}.lg\:bui-pt-7{padding-top:var(--bui-space-7)}.lg\:bui-pt-8{padding-top:var(--bui-space-8)}.lg\:bui-pt-9{padding-top:var(--bui-space-9)}.lg\:bui-pt-10{padding-top:var(--bui-space-10)}.lg\:bui-pt-11{padding-top:var(--bui-space-11)}.lg\:bui-pt-12{padding-top:var(--bui-space-12)}.lg\:bui-pt-13{padding-top:var(--bui-space-13)}.lg\:bui-pt-14{padding-top:var(--bui-space-14)}}@media (width>=1536px){.xl\:bui-pt{padding-top:var(--pt-xl)}.xl\:bui-pt-0\.5{padding-top:var(--bui-space-0_5)}.xl\:bui-pt-1{padding-top:var(--bui-space-1)}.xl\:bui-pt-1\.5{padding-top:var(--bui-space-1_5)}.xl\:bui-pt-2{padding-top:var(--bui-space-2)}.xl\:bui-pt-3{padding-top:var(--bui-space-3)}.xl\:bui-pt-4{padding-top:var(--bui-space-4)}.xl\:bui-pt-5{padding-top:var(--bui-space-5)}.xl\:bui-pt-6{padding-top:var(--bui-space-6)}.xl\:bui-pt-7{padding-top:var(--bui-space-7)}.xl\:bui-pt-8{padding-top:var(--bui-space-8)}.xl\:bui-pt-9{padding-top:var(--bui-space-9)}.xl\:bui-pt-10{padding-top:var(--bui-space-10)}.xl\:bui-pt-11{padding-top:var(--bui-space-11)}.xl\:bui-pt-12{padding-top:var(--bui-space-12)}.xl\:bui-pt-13{padding-top:var(--bui-space-13)}.xl\:bui-pt-14{padding-top:var(--bui-space-14)}}.bui-pb{padding-bottom:var(--pb)}.bui-pb-0\.5{padding-bottom:var(--bui-space-0_5)}.bui-pb-1{padding-bottom:var(--bui-space-1)}.bui-pb-1\.5{padding-bottom:var(--bui-space-1_5)}.bui-pb-2{padding-bottom:var(--bui-space-2)}.bui-pb-3{padding-bottom:var(--bui-space-3)}.bui-pb-4{padding-bottom:var(--bui-space-4)}.bui-pb-5{padding-bottom:var(--bui-space-5)}.bui-pb-6{padding-bottom:var(--bui-space-6)}.bui-pb-7{padding-bottom:var(--bui-space-7)}.bui-pb-8{padding-bottom:var(--bui-space-8)}.bui-pb-9{padding-bottom:var(--bui-space-9)}.bui-pb-10{padding-bottom:var(--bui-space-10)}.bui-pb-11{padding-bottom:var(--bui-space-11)}.bui-pb-12{padding-bottom:var(--bui-space-12)}.bui-pb-13{padding-bottom:var(--bui-space-13)}.bui-pb-14{padding-bottom:var(--bui-space-14)}@media (width>=640px){.xs\:bui-pb{padding-bottom:var(--pb-xs)}.xs\:bui-pb-0\.5{padding-bottom:var(--bui-space-0_5)}.xs\:bui-pb-1{padding-bottom:var(--bui-space-1)}.xs\:bui-pb-1\.5{padding-bottom:var(--bui-space-1_5)}.xs\:bui-pb-2{padding-bottom:var(--bui-space-2)}.xs\:bui-pb-3{padding-bottom:var(--bui-space-3)}.xs\:bui-pb-4{padding-bottom:var(--bui-space-4)}.xs\:bui-pb-5{padding-bottom:var(--bui-space-5)}.xs\:bui-pb-6{padding-bottom:var(--bui-space-6)}.xs\:bui-pb-7{padding-bottom:var(--bui-space-7)}.xs\:bui-pb-8{padding-bottom:var(--bui-space-8)}.xs\:bui-pb-9{padding-bottom:var(--bui-space-9)}.xs\:bui-pb-10{padding-bottom:var(--bui-space-10)}.xs\:bui-pb-11{padding-bottom:var(--bui-space-11)}.xs\:bui-pb-12{padding-bottom:var(--bui-space-12)}.xs\:bui-pb-13{padding-bottom:var(--bui-space-13)}.xs\:bui-pb-14{padding-bottom:var(--bui-space-14)}}@media (width>=768px){.sm\:bui-pb{padding-bottom:var(--pb-sm)}.sm\:bui-pb-0\.5{padding-bottom:var(--bui-space-0_5)}.sm\:bui-pb-1{padding-bottom:var(--bui-space-1)}.sm\:bui-pb-1\.5{padding-bottom:var(--bui-space-1_5)}.sm\:bui-pb-2{padding-bottom:var(--bui-space-2)}.sm\:bui-pb-3{padding-bottom:var(--bui-space-3)}.sm\:bui-pb-4{padding-bottom:var(--bui-space-4)}.sm\:bui-pb-5{padding-bottom:var(--bui-space-5)}.sm\:bui-pb-6{padding-bottom:var(--bui-space-6)}.sm\:bui-pb-7{padding-bottom:var(--bui-space-7)}.sm\:bui-pb-8{padding-bottom:var(--bui-space-8)}.sm\:bui-pb-9{padding-bottom:var(--bui-space-9)}.sm\:bui-pb-10{padding-bottom:var(--bui-space-10)}.sm\:bui-pb-11{padding-bottom:var(--bui-space-11)}.sm\:bui-pb-12{padding-bottom:var(--bui-space-12)}.sm\:bui-pb-13{padding-bottom:var(--bui-space-13)}.sm\:bui-pb-14{padding-bottom:var(--bui-space-14)}}@media (width>=1024px){.md\:bui-pb{padding-bottom:var(--pb-md)}.md\:bui-pb-0\.5{padding-bottom:var(--bui-space-0_5)}.md\:bui-pb-1{padding-bottom:var(--bui-space-1)}.md\:bui-pb-1\.5{padding-bottom:var(--bui-space-1_5)}.md\:bui-pb-2{padding-bottom:var(--bui-space-2)}.md\:bui-pb-3{padding-bottom:var(--bui-space-3)}.md\:bui-pb-4{padding-bottom:var(--bui-space-4)}.md\:bui-pb-5{padding-bottom:var(--bui-space-5)}.md\:bui-pb-6{padding-bottom:var(--bui-space-6)}.md\:bui-pb-7{padding-bottom:var(--bui-space-7)}.md\:bui-pb-8{padding-bottom:var(--bui-space-8)}.md\:bui-pb-9{padding-bottom:var(--bui-space-9)}.md\:bui-pb-10{padding-bottom:var(--bui-space-10)}.md\:bui-pb-11{padding-bottom:var(--bui-space-11)}.md\:bui-pb-12{padding-bottom:var(--bui-space-12)}.md\:bui-pb-13{padding-bottom:var(--bui-space-13)}.md\:bui-pb-14{padding-bottom:var(--bui-space-14)}}@media (width>=1280px){.lg\:bui-pb{padding-bottom:var(--pb-lg)}.lg\:bui-pb-0\.5{padding-bottom:var(--bui-space-0_5)}.lg\:bui-pb-1{padding-bottom:var(--bui-space-1)}.lg\:bui-pb-1\.5{padding-bottom:var(--bui-space-1_5)}.lg\:bui-pb-2{padding-bottom:var(--bui-space-2)}.lg\:bui-pb-3{padding-bottom:var(--bui-space-3)}.lg\:bui-pb-4{padding-bottom:var(--bui-space-4)}.lg\:bui-pb-5{padding-bottom:var(--bui-space-5)}.lg\:bui-pb-6{padding-bottom:var(--bui-space-6)}.lg\:bui-pb-7{padding-bottom:var(--bui-space-7)}.lg\:bui-pb-8{padding-bottom:var(--bui-space-8)}.lg\:bui-pb-9{padding-bottom:var(--bui-space-9)}.lg\:bui-pb-10{padding-bottom:var(--bui-space-10)}.lg\:bui-pb-11{padding-bottom:var(--bui-space-11)}.lg\:bui-pb-12{padding-bottom:var(--bui-space-12)}.lg\:bui-pb-13{padding-bottom:var(--bui-space-13)}.lg\:bui-pb-14{padding-bottom:var(--bui-space-14)}}@media (width>=1536px){.xl\:bui-pb{padding-bottom:var(--pb-xl)}.xl\:bui-pb-0\.5{padding-bottom:var(--bui-space-0_5)}.xl\:bui-pb-1{padding-bottom:var(--bui-space-1)}.xl\:bui-pb-1\.5{padding-bottom:var(--bui-space-1_5)}.xl\:bui-pb-2{padding-bottom:var(--bui-space-2)}.xl\:bui-pb-3{padding-bottom:var(--bui-space-3)}.xl\:bui-pb-4{padding-bottom:var(--bui-space-4)}.xl\:bui-pb-5{padding-bottom:var(--bui-space-5)}.xl\:bui-pb-6{padding-bottom:var(--bui-space-6)}.xl\:bui-pb-7{padding-bottom:var(--bui-space-7)}.xl\:bui-pb-8{padding-bottom:var(--bui-space-8)}.xl\:bui-pb-9{padding-bottom:var(--bui-space-9)}.xl\:bui-pb-10{padding-bottom:var(--bui-space-10)}.xl\:bui-pb-11{padding-bottom:var(--bui-space-11)}.xl\:bui-pb-12{padding-bottom:var(--bui-space-12)}.xl\:bui-pb-13{padding-bottom:var(--bui-space-13)}.xl\:bui-pb-14{padding-bottom:var(--bui-space-14)}}.bui-py{padding-top:var(--py);padding-bottom:var(--py)}.bui-py-0\.5{padding-top:var(--bui-space-0_5);padding-bottom:var(--bui-space-0_5)}.bui-py-1{padding-top:var(--bui-space-1);padding-bottom:var(--bui-space-1)}.bui-py-1\.5{padding-top:var(--bui-space-1_5);padding-bottom:var(--bui-space-1_5)}.bui-py-2{padding-top:var(--bui-space-2);padding-bottom:var(--bui-space-2)}.bui-py-3{padding-top:var(--bui-space-3);padding-bottom:var(--bui-space-3)}.bui-py-4{padding-top:var(--bui-space-4);padding-bottom:var(--bui-space-4)}.bui-py-5{padding-top:var(--bui-space-5);padding-bottom:var(--bui-space-5)}.bui-py-6{padding-top:var(--bui-space-6);padding-bottom:var(--bui-space-6)}.bui-py-7{padding-top:var(--bui-space-7);padding-bottom:var(--bui-space-7)}.bui-py-8{padding-top:var(--bui-space-8);padding-bottom:var(--bui-space-8)}.bui-py-9{padding-top:var(--bui-space-9);padding-bottom:var(--bui-space-9)}.bui-py-10{padding-top:var(--bui-space-10);padding-bottom:var(--bui-space-10)}.bui-py-11{padding-top:var(--bui-space-11);padding-bottom:var(--bui-space-11)}.bui-py-12{padding-top:var(--bui-space-12);padding-bottom:var(--bui-space-12)}.bui-py-13{padding-top:var(--bui-space-13);padding-bottom:var(--bui-space-13)}.bui-py-14{padding-top:var(--bui-space-14);padding-bottom:var(--bui-space-14)}@media (width>=640px){.xs\:bui-py{padding-top:var(--py-xs);padding-bottom:var(--py-xs)}.xs\:bui-py-0\.5{padding-top:var(--bui-space-0_5);padding-bottom:var(--bui-space-0_5)}.xs\:bui-py-1{padding-top:var(--bui-space-1);padding-bottom:var(--bui-space-1)}.xs\:bui-py-1\.5{padding-top:var(--bui-space-1_5);padding-bottom:var(--bui-space-1_5)}.xs\:bui-py-2{padding-top:var(--bui-space-2);padding-bottom:var(--bui-space-2)}.xs\:bui-py-3{padding-top:var(--bui-space-3);padding-bottom:var(--bui-space-3)}.xs\:bui-py-4{padding-top:var(--bui-space-4);padding-bottom:var(--bui-space-4)}.xs\:bui-py-5{padding-top:var(--bui-space-5);padding-bottom:var(--bui-space-5)}.xs\:bui-py-6{padding-top:var(--bui-space-6);padding-bottom:var(--bui-space-6)}.xs\:bui-py-7{padding-top:var(--bui-space-7);padding-bottom:var(--bui-space-7)}.xs\:bui-py-8{padding-top:var(--bui-space-8);padding-bottom:var(--bui-space-8)}.xs\:bui-py-9{padding-top:var(--bui-space-9);padding-bottom:var(--bui-space-9)}.xs\:bui-py-10{padding-top:var(--bui-space-10);padding-bottom:var(--bui-space-10)}.xs\:bui-py-11{padding-top:var(--bui-space-11);padding-bottom:var(--bui-space-11)}.xs\:bui-py-12{padding-top:var(--bui-space-12);padding-bottom:var(--bui-space-12)}.xs\:bui-py-13{padding-top:var(--bui-space-13);padding-bottom:var(--bui-space-13)}.xs\:bui-py-14{padding-top:var(--bui-space-14);padding-bottom:var(--bui-space-14)}}@media (width>=768px){.sm\:bui-py{padding-top:var(--py-sm);padding-bottom:var(--py-sm)}.sm\:bui-py-0\.5{padding-top:var(--bui-space-0_5);padding-bottom:var(--bui-space-0_5)}.sm\:bui-py-1{padding-top:var(--bui-space-1);padding-bottom:var(--bui-space-1)}.sm\:bui-py-1\.5{padding-top:var(--bui-space-1_5);padding-bottom:var(--bui-space-1_5)}.sm\:bui-py-2{padding-top:var(--bui-space-2);padding-bottom:var(--bui-space-2)}.sm\:bui-py-3{padding-top:var(--bui-space-3);padding-bottom:var(--bui-space-3)}.sm\:bui-py-4{padding-top:var(--bui-space-4);padding-bottom:var(--bui-space-4)}.sm\:bui-py-5{padding-top:var(--bui-space-5);padding-bottom:var(--bui-space-5)}.sm\:bui-py-6{padding-top:var(--bui-space-6);padding-bottom:var(--bui-space-6)}.sm\:bui-py-7{padding-top:var(--bui-space-7);padding-bottom:var(--bui-space-7)}.sm\:bui-py-8{padding-top:var(--bui-space-8);padding-bottom:var(--bui-space-8)}.sm\:bui-py-9{padding-top:var(--bui-space-9);padding-bottom:var(--bui-space-9)}.sm\:bui-py-10{padding-top:var(--bui-space-10);padding-bottom:var(--bui-space-10)}.sm\:bui-py-11{padding-top:var(--bui-space-11);padding-bottom:var(--bui-space-11)}.sm\:bui-py-12{padding-top:var(--bui-space-12);padding-bottom:var(--bui-space-12)}.sm\:bui-py-13{padding-top:var(--bui-space-13);padding-bottom:var(--bui-space-13)}.sm\:bui-py-14{padding-top:var(--bui-space-14);padding-bottom:var(--bui-space-14)}}@media (width>=1024px){.md\:bui-py{padding-top:var(--py-md);padding-bottom:var(--py-md)}.md\:bui-py-0\.5{padding-top:var(--bui-space-0_5);padding-bottom:var(--bui-space-0_5)}.md\:bui-py-1{padding-top:var(--bui-space-1);padding-bottom:var(--bui-space-1)}.md\:bui-py-1\.5{padding-top:var(--bui-space-1_5);padding-bottom:var(--bui-space-1_5)}.md\:bui-py-2{padding-top:var(--bui-space-2);padding-bottom:var(--bui-space-2)}.md\:bui-py-3{padding-top:var(--bui-space-3);padding-bottom:var(--bui-space-3)}.md\:bui-py-4{padding-top:var(--bui-space-4);padding-bottom:var(--bui-space-4)}.md\:bui-py-5{padding-top:var(--bui-space-5);padding-bottom:var(--bui-space-5)}.md\:bui-py-6{padding-top:var(--bui-space-6);padding-bottom:var(--bui-space-6)}.md\:bui-py-7{padding-top:var(--bui-space-7);padding-bottom:var(--bui-space-7)}.md\:bui-py-8{padding-top:var(--bui-space-8);padding-bottom:var(--bui-space-8)}.md\:bui-py-9{padding-top:var(--bui-space-9);padding-bottom:var(--bui-space-9)}.md\:bui-py-10{padding-top:var(--bui-space-10);padding-bottom:var(--bui-space-10)}.md\:bui-py-11{padding-top:var(--bui-space-11);padding-bottom:var(--bui-space-11)}.md\:bui-py-12{padding-top:var(--bui-space-12);padding-bottom:var(--bui-space-12)}.md\:bui-py-13{padding-top:var(--bui-space-13);padding-bottom:var(--bui-space-13)}.md\:bui-py-14{padding-top:var(--bui-space-14);padding-bottom:var(--bui-space-14)}}@media (width>=1280px){.lg\:bui-py{padding-top:var(--py-lg);padding-bottom:var(--py-lg)}.lg\:bui-py-0\.5{padding-top:var(--bui-space-0_5);padding-bottom:var(--bui-space-0_5)}.lg\:bui-py-1{padding-top:var(--bui-space-1);padding-bottom:var(--bui-space-1)}.lg\:bui-py-1\.5{padding-top:var(--bui-space-1_5);padding-bottom:var(--bui-space-1_5)}.lg\:bui-py-2{padding-top:var(--bui-space-2);padding-bottom:var(--bui-space-2)}.lg\:bui-py-3{padding-top:var(--bui-space-3);padding-bottom:var(--bui-space-3)}.lg\:bui-py-4{padding-top:var(--bui-space-4);padding-bottom:var(--bui-space-4)}.lg\:bui-py-5{padding-top:var(--bui-space-5);padding-bottom:var(--bui-space-5)}.lg\:bui-py-6{padding-top:var(--bui-space-6);padding-bottom:var(--bui-space-6)}.lg\:bui-py-7{padding-top:var(--bui-space-7);padding-bottom:var(--bui-space-7)}.lg\:bui-py-8{padding-top:var(--bui-space-8);padding-bottom:var(--bui-space-8)}.lg\:bui-py-9{padding-top:var(--bui-space-9);padding-bottom:var(--bui-space-9)}.lg\:bui-py-10{padding-top:var(--bui-space-10);padding-bottom:var(--bui-space-10)}.lg\:bui-py-11{padding-top:var(--bui-space-11);padding-bottom:var(--bui-space-11)}.lg\:bui-py-12{padding-top:var(--bui-space-12);padding-bottom:var(--bui-space-12)}.lg\:bui-py-13{padding-top:var(--bui-space-13);padding-bottom:var(--bui-space-13)}.lg\:bui-py-14{padding-top:var(--bui-space-14);padding-bottom:var(--bui-space-14)}}@media (width>=1536px){.xl\:bui-py{padding-top:var(--py-xl);padding-bottom:var(--py-xl)}.xl\:bui-py-0\.5{padding-top:var(--bui-space-0_5);padding-bottom:var(--bui-space-0_5)}.xl\:bui-py-1{padding-top:var(--bui-space-1);padding-bottom:var(--bui-space-1)}.xl\:bui-py-1\.5{padding-top:var(--bui-space-1_5);padding-bottom:var(--bui-space-1_5)}.xl\:bui-py-2{padding-top:var(--bui-space-2);padding-bottom:var(--bui-space-2)}.xl\:bui-py-3{padding-top:var(--bui-space-3);padding-bottom:var(--bui-space-3)}.xl\:bui-py-4{padding-top:var(--bui-space-4);padding-bottom:var(--bui-space-4)}.xl\:bui-py-5{padding-top:var(--bui-space-5);padding-bottom:var(--bui-space-5)}.xl\:bui-py-6{padding-top:var(--bui-space-6);padding-bottom:var(--bui-space-6)}.xl\:bui-py-7{padding-top:var(--bui-space-7);padding-bottom:var(--bui-space-7)}.xl\:bui-py-8{padding-top:var(--bui-space-8);padding-bottom:var(--bui-space-8)}.xl\:bui-py-9{padding-top:var(--bui-space-9);padding-bottom:var(--bui-space-9)}.xl\:bui-py-10{padding-top:var(--bui-space-10);padding-bottom:var(--bui-space-10)}.xl\:bui-py-11{padding-top:var(--bui-space-11);padding-bottom:var(--bui-space-11)}.xl\:bui-py-12{padding-top:var(--bui-space-12);padding-bottom:var(--bui-space-12)}.xl\:bui-py-13{padding-top:var(--bui-space-13);padding-bottom:var(--bui-space-13)}.xl\:bui-py-14{padding-top:var(--bui-space-14);padding-bottom:var(--bui-space-14)}}.bui-px{padding-left:var(--px);padding-right:var(--px)}.bui-px-0\.5{padding-left:var(--bui-space-0_5);padding-right:var(--bui-space-0_5)}.bui-px-1{padding-left:var(--bui-space-1);padding-right:var(--bui-space-1)}.bui-px-1\.5{padding-left:var(--bui-space-1_5);padding-right:var(--bui-space-1_5)}.bui-px-2{padding-left:var(--bui-space-2);padding-right:var(--bui-space-2)}.bui-px-3{padding-left:var(--bui-space-3);padding-right:var(--bui-space-3)}.bui-px-4{padding-left:var(--bui-space-4);padding-right:var(--bui-space-4)}.bui-px-5{padding-left:var(--bui-space-5);padding-right:var(--bui-space-5)}.bui-px-6{padding-left:var(--bui-space-6);padding-right:var(--bui-space-6)}.bui-px-7{padding-left:var(--bui-space-7);padding-right:var(--bui-space-7)}.bui-px-8{padding-left:var(--bui-space-8);padding-right:var(--bui-space-8)}.bui-px-9{padding-left:var(--bui-space-9);padding-right:var(--bui-space-9)}.bui-px-10{padding-left:var(--bui-space-10);padding-right:var(--bui-space-10)}.bui-px-11{padding-left:var(--bui-space-11);padding-right:var(--bui-space-11)}.bui-px-12{padding-left:var(--bui-space-12);padding-right:var(--bui-space-12)}.bui-px-13{padding-left:var(--bui-space-13);padding-right:var(--bui-space-13)}.bui-px-14{padding-left:var(--bui-space-14);padding-right:var(--bui-space-14)}@media (width>=640px){.xs\:bui-px{padding-left:var(--px-xs);padding-right:var(--px-xs)}.xs\:bui-px-0\.5{padding-left:var(--bui-space-0_5);padding-right:var(--bui-space-0_5)}.xs\:bui-px-1{padding-left:var(--bui-space-1);padding-right:var(--bui-space-1)}.xs\:bui-px-1\.5{padding-left:var(--bui-space-1_5);padding-right:var(--bui-space-1_5)}.xs\:bui-px-2{padding-left:var(--bui-space-2);padding-right:var(--bui-space-2)}.xs\:bui-px-3{padding-left:var(--bui-space-3);padding-right:var(--bui-space-3)}.xs\:bui-px-4{padding-left:var(--bui-space-4);padding-right:var(--bui-space-4)}.xs\:bui-px-5{padding-left:var(--bui-space-5);padding-right:var(--bui-space-5)}.xs\:bui-px-6{padding-left:var(--bui-space-6);padding-right:var(--bui-space-6)}.xs\:bui-px-7{padding-left:var(--bui-space-7);padding-right:var(--bui-space-7)}.xs\:bui-px-8{padding-left:var(--bui-space-8);padding-right:var(--bui-space-8)}.xs\:bui-px-9{padding-left:var(--bui-space-9);padding-right:var(--bui-space-9)}.xs\:bui-px-10{padding-left:var(--bui-space-10);padding-right:var(--bui-space-10)}.xs\:bui-px-11{padding-left:var(--bui-space-11);padding-right:var(--bui-space-11)}.xs\:bui-px-12{padding-left:var(--bui-space-12);padding-right:var(--bui-space-12)}.xs\:bui-px-13{padding-left:var(--bui-space-13);padding-right:var(--bui-space-13)}.xs\:bui-px-14{padding-left:var(--bui-space-14);padding-right:var(--bui-space-14)}}@media (width>=768px){.sm\:bui-px{padding-left:var(--px-sm);padding-right:var(--px-sm)}.sm\:bui-px-0\.5{padding-left:var(--bui-space-0_5);padding-right:var(--bui-space-0_5)}.sm\:bui-px-1{padding-left:var(--bui-space-1);padding-right:var(--bui-space-1)}.sm\:bui-px-1\.5{padding-left:var(--bui-space-1_5);padding-right:var(--bui-space-1_5)}.sm\:bui-px-2{padding-left:var(--bui-space-2);padding-right:var(--bui-space-2)}.sm\:bui-px-3{padding-left:var(--bui-space-3);padding-right:var(--bui-space-3)}.sm\:bui-px-4{padding-left:var(--bui-space-4);padding-right:var(--bui-space-4)}.sm\:bui-px-5{padding-left:var(--bui-space-5);padding-right:var(--bui-space-5)}.sm\:bui-px-6{padding-left:var(--bui-space-6);padding-right:var(--bui-space-6)}.sm\:bui-px-7{padding-left:var(--bui-space-7);padding-right:var(--bui-space-7)}.sm\:bui-px-8{padding-left:var(--bui-space-8);padding-right:var(--bui-space-8)}.sm\:bui-px-9{padding-left:var(--bui-space-9);padding-right:var(--bui-space-9)}.sm\:bui-px-10{padding-left:var(--bui-space-10);padding-right:var(--bui-space-10)}.sm\:bui-px-11{padding-left:var(--bui-space-11);padding-right:var(--bui-space-11)}.sm\:bui-px-12{padding-left:var(--bui-space-12);padding-right:var(--bui-space-12)}.sm\:bui-px-13{padding-left:var(--bui-space-13);padding-right:var(--bui-space-13)}.sm\:bui-px-14{padding-left:var(--bui-space-14);padding-right:var(--bui-space-14)}}@media (width>=1024px){.md\:bui-px{padding-left:var(--px-md);padding-right:var(--px-md)}.md\:bui-px-0\.5{padding-left:var(--bui-space-0_5);padding-right:var(--bui-space-0_5)}.md\:bui-px-1{padding-left:var(--bui-space-1);padding-right:var(--bui-space-1)}.md\:bui-px-1\.5{padding-left:var(--bui-space-1_5);padding-right:var(--bui-space-1_5)}.md\:bui-px-2{padding-left:var(--bui-space-2);padding-right:var(--bui-space-2)}.md\:bui-px-3{padding-left:var(--bui-space-3);padding-right:var(--bui-space-3)}.md\:bui-px-4{padding-left:var(--bui-space-4);padding-right:var(--bui-space-4)}.md\:bui-px-5{padding-left:var(--bui-space-5);padding-right:var(--bui-space-5)}.md\:bui-px-6{padding-left:var(--bui-space-6);padding-right:var(--bui-space-6)}.md\:bui-px-7{padding-left:var(--bui-space-7);padding-right:var(--bui-space-7)}.md\:bui-px-8{padding-left:var(--bui-space-8);padding-right:var(--bui-space-8)}.md\:bui-px-9{padding-left:var(--bui-space-9);padding-right:var(--bui-space-9)}.md\:bui-px-10{padding-left:var(--bui-space-10);padding-right:var(--bui-space-10)}.md\:bui-px-11{padding-left:var(--bui-space-11);padding-right:var(--bui-space-11)}.md\:bui-px-12{padding-left:var(--bui-space-12);padding-right:var(--bui-space-12)}.md\:bui-px-13{padding-left:var(--bui-space-13);padding-right:var(--bui-space-13)}.md\:bui-px-14{padding-left:var(--bui-space-14);padding-right:var(--bui-space-14)}}@media (width>=1280px){.lg\:bui-px{padding-left:var(--px-lg);padding-right:var(--px-lg)}.lg\:bui-px-0\.5{padding-left:var(--bui-space-0_5);padding-right:var(--bui-space-0_5)}.lg\:bui-px-1{padding-left:var(--bui-space-1);padding-right:var(--bui-space-1)}.lg\:bui-px-1\.5{padding-left:var(--bui-space-1_5);padding-right:var(--bui-space-1_5)}.lg\:bui-px-2{padding-left:var(--bui-space-2);padding-right:var(--bui-space-2)}.lg\:bui-px-3{padding-left:var(--bui-space-3);padding-right:var(--bui-space-3)}.lg\:bui-px-4{padding-left:var(--bui-space-4);padding-right:var(--bui-space-4)}.lg\:bui-px-5{padding-left:var(--bui-space-5);padding-right:var(--bui-space-5)}.lg\:bui-px-6{padding-left:var(--bui-space-6);padding-right:var(--bui-space-6)}.lg\:bui-px-7{padding-left:var(--bui-space-7);padding-right:var(--bui-space-7)}.lg\:bui-px-8{padding-left:var(--bui-space-8);padding-right:var(--bui-space-8)}.lg\:bui-px-9{padding-left:var(--bui-space-9);padding-right:var(--bui-space-9)}.lg\:bui-px-10{padding-left:var(--bui-space-10);padding-right:var(--bui-space-10)}.lg\:bui-px-11{padding-left:var(--bui-space-11);padding-right:var(--bui-space-11)}.lg\:bui-px-12{padding-left:var(--bui-space-12);padding-right:var(--bui-space-12)}.lg\:bui-px-13{padding-left:var(--bui-space-13);padding-right:var(--bui-space-13)}.lg\:bui-px-14{padding-left:var(--bui-space-14);padding-right:var(--bui-space-14)}}@media (width>=1536px){.xl\:bui-px{padding-left:var(--px-xl);padding-right:var(--px-xl)}.xl\:bui-px-0\.5{padding-left:var(--bui-space-0_5);padding-right:var(--bui-space-0_5)}.xl\:bui-px-1{padding-left:var(--bui-space-1);padding-right:var(--bui-space-1)}.xl\:bui-px-1\.5{padding-left:var(--bui-space-1_5);padding-right:var(--bui-space-1_5)}.xl\:bui-px-2{padding-left:var(--bui-space-2);padding-right:var(--bui-space-2)}.xl\:bui-px-3{padding-left:var(--bui-space-3);padding-right:var(--bui-space-3)}.xl\:bui-px-4{padding-left:var(--bui-space-4);padding-right:var(--bui-space-4)}.xl\:bui-px-5{padding-left:var(--bui-space-5);padding-right:var(--bui-space-5)}.xl\:bui-px-6{padding-left:var(--bui-space-6);padding-right:var(--bui-space-6)}.xl\:bui-px-7{padding-left:var(--bui-space-7);padding-right:var(--bui-space-7)}.xl\:bui-px-8{padding-left:var(--bui-space-8);padding-right:var(--bui-space-8)}.xl\:bui-px-9{padding-left:var(--bui-space-9);padding-right:var(--bui-space-9)}.xl\:bui-px-10{padding-left:var(--bui-space-10);padding-right:var(--bui-space-10)}.xl\:bui-px-11{padding-left:var(--bui-space-11);padding-right:var(--bui-space-11)}.xl\:bui-px-12{padding-left:var(--bui-space-12);padding-right:var(--bui-space-12)}.xl\:bui-px-13{padding-left:var(--bui-space-13);padding-right:var(--bui-space-13)}.xl\:bui-px-14{padding-left:var(--bui-space-14);padding-right:var(--bui-space-14)}}.bui-m{margin:var(--m)}.bui-m-0\.5{margin:var(--bui-space-0_5)}.bui-m-1{margin:var(--bui-space-1)}.bui-m-1\.5{margin:var(--bui-space-1_5)}.bui-m-2{margin:var(--bui-space-2)}.bui-m-3{margin:var(--bui-space-3)}.bui-m-4{margin:var(--bui-space-4)}.bui-m-5{margin:var(--bui-space-5)}.bui-m-6{margin:var(--bui-space-6)}.bui-m-7{margin:var(--bui-space-7)}.bui-m-8{margin:var(--bui-space-8)}.bui-m-9{margin:var(--bui-space-9)}.bui-m-10{margin:var(--bui-space-10)}.bui-m-11{margin:var(--bui-space-11)}.bui-m-12{margin:var(--bui-space-12)}.bui-m-13{margin:var(--bui-space-13)}.bui-m-14{margin:var(--bui-space-14)}@media (width>=640px){.xs\:bui-m{margin:var(--p-xs)}.xs\:bui-m-0\.5{margin:var(--bui-space-0_5)}.xs\:bui-m-1{margin:var(--bui-space-1)}.xs\:bui-m-1\.5{margin:var(--bui-space-1_5)}.xs\:bui-m-2{margin:var(--bui-space-2)}.xs\:bui-m-3{margin:var(--bui-space-3)}.xs\:bui-m-4{margin:var(--bui-space-4)}.xs\:bui-m-5{margin:var(--bui-space-5)}.xs\:bui-m-6{margin:var(--bui-space-6)}.xs\:bui-m-7{margin:var(--bui-space-7)}.xs\:bui-m-8{margin:var(--bui-space-8)}.xs\:bui-m-9{margin:var(--bui-space-9)}.xs\:bui-m-10{margin:var(--bui-space-10)}.xs\:bui-m-11{margin:var(--bui-space-11)}.xs\:bui-m-12{margin:var(--bui-space-12)}.xs\:bui-m-13{margin:var(--bui-space-13)}.xs\:bui-m-14{margin:var(--bui-space-14)}}@media (width>=768px){.sm\:bui-m{margin:var(--p-sm)}.sm\:bui-m-0\.5{margin:var(--bui-space-0_5)}.sm\:bui-m-1{margin:var(--bui-space-1)}.sm\:bui-m-1\.5{margin:var(--bui-space-1_5)}.sm\:bui-m-2{margin:var(--bui-space-2)}.sm\:bui-m-3{margin:var(--bui-space-3)}.sm\:bui-m-4{margin:var(--bui-space-4)}.sm\:bui-m-5{margin:var(--bui-space-5)}.sm\:bui-m-6{margin:var(--bui-space-6)}.sm\:bui-m-7{margin:var(--bui-space-7)}.sm\:bui-m-8{margin:var(--bui-space-8)}.sm\:bui-m-9{margin:var(--bui-space-9)}.sm\:bui-m-10{margin:var(--bui-space-10)}.sm\:bui-m-11{margin:var(--bui-space-11)}.sm\:bui-m-12{margin:var(--bui-space-12)}.sm\:bui-m-13{margin:var(--bui-space-13)}.sm\:bui-m-14{margin:var(--bui-space-14)}}@media (width>=1024px){.md\:bui-m{margin:var(--p-md)}.md\:bui-m-0\.5{margin:var(--bui-space-0_5)}.md\:bui-m-1{margin:var(--bui-space-1)}.md\:bui-m-1\.5{margin:var(--bui-space-1_5)}.md\:bui-m-2{margin:var(--bui-space-2)}.md\:bui-m-3{margin:var(--bui-space-3)}.md\:bui-m-4{margin:var(--bui-space-4)}.md\:bui-m-5{margin:var(--bui-space-5)}.md\:bui-m-6{margin:var(--bui-space-6)}.md\:bui-m-7{margin:var(--bui-space-7)}.md\:bui-m-8{margin:var(--bui-space-8)}.md\:bui-m-9{margin:var(--bui-space-9)}.md\:bui-m-10{margin:var(--bui-space-10)}.md\:bui-m-11{margin:var(--bui-space-11)}.md\:bui-m-12{margin:var(--bui-space-12)}.md\:bui-m-13{margin:var(--bui-space-13)}.md\:bui-m-14{margin:var(--bui-space-14)}}@media (width>=1280px){.lg\:bui-m{margin:var(--p-lg)}.lg\:bui-m-0\.5{margin:var(--bui-space-0_5)}.lg\:bui-m-1{margin:var(--bui-space-1)}.lg\:bui-m-1\.5{margin:var(--bui-space-1_5)}.lg\:bui-m-2{margin:var(--bui-space-2)}.lg\:bui-m-3{margin:var(--bui-space-3)}.lg\:bui-m-4{margin:var(--bui-space-4)}.lg\:bui-m-5{margin:var(--bui-space-5)}.lg\:bui-m-6{margin:var(--bui-space-6)}.lg\:bui-m-7{margin:var(--bui-space-7)}.lg\:bui-m-8{margin:var(--bui-space-8)}.lg\:bui-m-9{margin:var(--bui-space-9)}.lg\:bui-m-10{margin:var(--bui-space-10)}.lg\:bui-m-11{margin:var(--bui-space-11)}.lg\:bui-m-12{margin:var(--bui-space-12)}.lg\:bui-m-13{margin:var(--bui-space-13)}.lg\:bui-m-14{margin:var(--bui-space-14)}}@media (width>=1536px){.xl\:bui-m{margin:var(--p-xl)}.xl\:bui-m-0\.5{margin:var(--bui-space-0_5)}.xl\:bui-m-1{margin:var(--bui-space-1)}.xl\:bui-m-1\.5{margin:var(--bui-space-1_5)}.xl\:bui-m-2{margin:var(--bui-space-2)}.xl\:bui-m-3{margin:var(--bui-space-3)}.xl\:bui-m-4{margin:var(--bui-space-4)}.xl\:bui-m-5{margin:var(--bui-space-5)}.xl\:bui-m-6{margin:var(--bui-space-6)}.xl\:bui-m-7{margin:var(--bui-space-7)}.xl\:bui-m-8{margin:var(--bui-space-8)}.xl\:bui-m-9{margin:var(--bui-space-9)}.xl\:bui-m-10{margin:var(--bui-space-10)}.xl\:bui-m-11{margin:var(--bui-space-11)}.xl\:bui-m-12{margin:var(--bui-space-12)}.xl\:bui-m-13{margin:var(--bui-space-13)}.xl\:bui-m-14{margin:var(--bui-space-14)}}.bui-ml{margin-left:var(--ml)}.bui-ml-0\.5{margin-left:var(--bui-space-0_5)}.bui-ml-1{margin-left:var(--bui-space-1)}.bui-ml-1\.5{margin-left:var(--bui-space-1_5)}.bui-ml-2{margin-left:var(--bui-space-2)}.bui-ml-3{margin-left:var(--bui-space-3)}.bui-ml-4{margin-left:var(--bui-space-4)}.bui-ml-5{margin-left:var(--bui-space-5)}.bui-ml-6{margin-left:var(--bui-space-6)}.bui-ml-7{margin-left:var(--bui-space-7)}.bui-ml-8{margin-left:var(--bui-space-8)}.bui-ml-9{margin-left:var(--bui-space-9)}.bui-ml-10{margin-left:var(--bui-space-10)}.bui-ml-11{margin-left:var(--bui-space-11)}.bui-ml-12{margin-left:var(--bui-space-12)}.bui-ml-13{margin-left:var(--bui-space-13)}.bui-ml-14{margin-left:var(--bui-space-14)}@media (width>=640px){.xs\:bui-ml{margin-left:var(--pl-xs)}.xs\:bui-ml-0\.5{margin-left:var(--bui-space-0_5)}.xs\:bui-ml-1{margin-left:var(--bui-space-1)}.xs\:bui-ml-1\.5{margin-left:var(--bui-space-1_5)}.xs\:bui-ml-2{margin-left:var(--bui-space-2)}.xs\:bui-ml-3{margin-left:var(--bui-space-3)}.xs\:bui-ml-4{margin-left:var(--bui-space-4)}.xs\:bui-ml-5{margin-left:var(--bui-space-5)}.xs\:bui-ml-6{margin-left:var(--bui-space-6)}.xs\:bui-ml-7{margin-left:var(--bui-space-7)}.xs\:bui-ml-8{margin-left:var(--bui-space-8)}.xs\:bui-ml-9{margin-left:var(--bui-space-9)}.xs\:bui-ml-10{margin-left:var(--bui-space-10)}.xs\:bui-ml-11{margin-left:var(--bui-space-11)}.xs\:bui-ml-12{margin-left:var(--bui-space-12)}.xs\:bui-ml-13{margin-left:var(--bui-space-13)}.xs\:bui-ml-14{margin-left:var(--bui-space-14)}}@media (width>=768px){.sm\:bui-ml{margin-left:var(--pl-sm)}.sm\:bui-ml-0\.5{margin-left:var(--bui-space-0_5)}.sm\:bui-ml-1{margin-left:var(--bui-space-1)}.sm\:bui-ml-1\.5{margin-left:var(--bui-space-1_5)}.sm\:bui-ml-2{margin-left:var(--bui-space-2)}.sm\:bui-ml-3{margin-left:var(--bui-space-3)}.sm\:bui-ml-4{margin-left:var(--bui-space-4)}.sm\:bui-ml-5{margin-left:var(--bui-space-5)}.sm\:bui-ml-6{margin-left:var(--bui-space-6)}.sm\:bui-ml-7{margin-left:var(--bui-space-7)}.sm\:bui-ml-8{margin-left:var(--bui-space-8)}.sm\:bui-ml-9{margin-left:var(--bui-space-9)}.sm\:bui-ml-10{margin-left:var(--bui-space-10)}.sm\:bui-ml-11{margin-left:var(--bui-space-11)}.sm\:bui-ml-12{margin-left:var(--bui-space-12)}.sm\:bui-ml-13{margin-left:var(--bui-space-13)}.sm\:bui-ml-14{margin-left:var(--bui-space-14)}}@media (width>=1024px){.md\:bui-ml{margin-left:var(--pl-md)}.md\:bui-ml-0\.5{margin-left:var(--bui-space-0_5)}.md\:bui-ml-1{margin-left:var(--bui-space-1)}.md\:bui-ml-1\.5{margin-left:var(--bui-space-1_5)}.md\:bui-ml-2{margin-left:var(--bui-space-2)}.md\:bui-ml-3{margin-left:var(--bui-space-3)}.md\:bui-ml-4{margin-left:var(--bui-space-4)}.md\:bui-ml-5{margin-left:var(--bui-space-5)}.md\:bui-ml-6{margin-left:var(--bui-space-6)}.md\:bui-ml-7{margin-left:var(--bui-space-7)}.md\:bui-ml-8{margin-left:var(--bui-space-8)}.md\:bui-ml-9{margin-left:var(--bui-space-9)}.md\:bui-ml-10{margin-left:var(--bui-space-10)}.md\:bui-ml-11{margin-left:var(--bui-space-11)}.md\:bui-ml-12{margin-left:var(--bui-space-12)}.md\:bui-ml-13{margin-left:var(--bui-space-13)}.md\:bui-ml-14{margin-left:var(--bui-space-14)}}@media (width>=1280px){.lg\:bui-ml{margin-left:var(--pl-lg)}.lg\:bui-ml-0\.5{margin-left:var(--bui-space-0_5)}.lg\:bui-ml-1{margin-left:var(--bui-space-1)}.lg\:bui-ml-1\.5{margin-left:var(--bui-space-1_5)}.lg\:bui-ml-2{margin-left:var(--bui-space-2)}.lg\:bui-ml-3{margin-left:var(--bui-space-3)}.lg\:bui-ml-4{margin-left:var(--bui-space-4)}.lg\:bui-ml-5{margin-left:var(--bui-space-5)}.lg\:bui-ml-6{margin-left:var(--bui-space-6)}.lg\:bui-ml-7{margin-left:var(--bui-space-7)}.lg\:bui-ml-8{margin-left:var(--bui-space-8)}.lg\:bui-ml-9{margin-left:var(--bui-space-9)}.lg\:bui-ml-10{margin-left:var(--bui-space-10)}.lg\:bui-ml-11{margin-left:var(--bui-space-11)}.lg\:bui-ml-12{margin-left:var(--bui-space-12)}.lg\:bui-ml-13{margin-left:var(--bui-space-13)}.lg\:bui-ml-14{margin-left:var(--bui-space-14)}}@media (width>=1536px){.xl\:bui-ml{margin-left:var(--pl-xl)}.xl\:bui-ml-0\.5{margin-left:var(--bui-space-0_5)}.xl\:bui-ml-1{margin-left:var(--bui-space-1)}.xl\:bui-ml-1\.5{margin-left:var(--bui-space-1_5)}.xl\:bui-ml-2{margin-left:var(--bui-space-2)}.xl\:bui-ml-3{margin-left:var(--bui-space-3)}.xl\:bui-ml-4{margin-left:var(--bui-space-4)}.xl\:bui-ml-5{margin-left:var(--bui-space-5)}.xl\:bui-ml-6{margin-left:var(--bui-space-6)}.xl\:bui-ml-7{margin-left:var(--bui-space-7)}.xl\:bui-ml-8{margin-left:var(--bui-space-8)}.xl\:bui-ml-9{margin-left:var(--bui-space-9)}.xl\:bui-ml-10{margin-left:var(--bui-space-10)}.xl\:bui-ml-11{margin-left:var(--bui-space-11)}.xl\:bui-ml-12{margin-left:var(--bui-space-12)}.xl\:bui-ml-13{margin-left:var(--bui-space-13)}.xl\:bui-ml-14{margin-left:var(--bui-space-14)}}.bui-mr{margin-right:var(--mr)}.bui-mr-0\.5{margin-right:var(--bui-space-0_5)}.bui-mr-1{margin-right:var(--bui-space-1)}.bui-mr-1\.5{margin-right:var(--bui-space-1_5)}.bui-mr-2{margin-right:var(--bui-space-2)}.bui-mr-3{margin-right:var(--bui-space-3)}.bui-mr-4{margin-right:var(--bui-space-4)}.bui-mr-5{margin-right:var(--bui-space-5)}.bui-mr-6{margin-right:var(--bui-space-6)}.bui-mr-7{margin-right:var(--bui-space-7)}.bui-mr-8{margin-right:var(--bui-space-8)}.bui-mr-9{margin-right:var(--bui-space-9)}.bui-mr-10{margin-right:var(--bui-space-10)}.bui-mr-11{margin-right:var(--bui-space-11)}.bui-mr-12{margin-right:var(--bui-space-12)}.bui-mr-13{margin-right:var(--bui-space-13)}.bui-mr-14{margin-right:var(--bui-space-14)}@media (width>=640px){.xs\:bui-mr{margin-right:var(--pr-xs)}.xs\:bui-mr-0\.5{margin-right:var(--bui-space-0_5)}.xs\:bui-mr-1{margin-right:var(--bui-space-1)}.xs\:bui-mr-1\.5{margin-right:var(--bui-space-1_5)}.xs\:bui-mr-2{margin-right:var(--bui-space-2)}.xs\:bui-mr-3{margin-right:var(--bui-space-3)}.xs\:bui-mr-4{margin-right:var(--bui-space-4)}.xs\:bui-mr-5{margin-right:var(--bui-space-5)}.xs\:bui-mr-6{margin-right:var(--bui-space-6)}.xs\:bui-mr-7{margin-right:var(--bui-space-7)}.xs\:bui-mr-8{margin-right:var(--bui-space-8)}.xs\:bui-mr-9{margin-right:var(--bui-space-9)}.xs\:bui-mr-10{margin-right:var(--bui-space-10)}.xs\:bui-mr-11{margin-right:var(--bui-space-11)}.xs\:bui-mr-12{margin-right:var(--bui-space-12)}.xs\:bui-mr-13{margin-right:var(--bui-space-13)}.xs\:bui-mr-14{margin-right:var(--bui-space-14)}}@media (width>=768px){.sm\:bui-mr{margin-right:var(--pr-sm)}.sm\:bui-mr-0\.5{margin-right:var(--bui-space-0_5)}.sm\:bui-mr-1{margin-right:var(--bui-space-1)}.sm\:bui-mr-1\.5{margin-right:var(--bui-space-1_5)}.sm\:bui-mr-2{margin-right:var(--bui-space-2)}.sm\:bui-mr-3{margin-right:var(--bui-space-3)}.sm\:bui-mr-4{margin-right:var(--bui-space-4)}.sm\:bui-mr-5{margin-right:var(--bui-space-5)}.sm\:bui-mr-6{margin-right:var(--bui-space-6)}.sm\:bui-mr-7{margin-right:var(--bui-space-7)}.sm\:bui-mr-8{margin-right:var(--bui-space-8)}.sm\:bui-mr-9{margin-right:var(--bui-space-9)}.sm\:bui-mr-10{margin-right:var(--bui-space-10)}.sm\:bui-mr-11{margin-right:var(--bui-space-11)}.sm\:bui-mr-12{margin-right:var(--bui-space-12)}.sm\:bui-mr-13{margin-right:var(--bui-space-13)}.sm\:bui-mr-14{margin-right:var(--bui-space-14)}}@media (width>=1024px){.md\:bui-mr{margin-right:var(--pr-md)}.md\:bui-mr-0\.5{margin-right:var(--bui-space-0_5)}.md\:bui-mr-1{margin-right:var(--bui-space-1)}.md\:bui-mr-1\.5{margin-right:var(--bui-space-1_5)}.md\:bui-mr-2{margin-right:var(--bui-space-2)}.md\:bui-mr-3{margin-right:var(--bui-space-3)}.md\:bui-mr-4{margin-right:var(--bui-space-4)}.md\:bui-mr-5{margin-right:var(--bui-space-5)}.md\:bui-mr-6{margin-right:var(--bui-space-6)}.md\:bui-mr-7{margin-right:var(--bui-space-7)}.md\:bui-mr-8{margin-right:var(--bui-space-8)}.md\:bui-mr-9{margin-right:var(--bui-space-9)}.md\:bui-mr-10{margin-right:var(--bui-space-10)}.md\:bui-mr-11{margin-right:var(--bui-space-11)}.md\:bui-mr-12{margin-right:var(--bui-space-12)}.md\:bui-mr-13{margin-right:var(--bui-space-13)}.md\:bui-mr-14{margin-right:var(--bui-space-14)}}@media (width>=1280px){.lg\:bui-mr{margin-right:var(--pr-lg)}.lg\:bui-mr-0\.5{margin-right:var(--bui-space-0_5)}.lg\:bui-mr-1{margin-right:var(--bui-space-1)}.lg\:bui-mr-1\.5{margin-right:var(--bui-space-1_5)}.lg\:bui-mr-2{margin-right:var(--bui-space-2)}.lg\:bui-mr-3{margin-right:var(--bui-space-3)}.lg\:bui-mr-4{margin-right:var(--bui-space-4)}.lg\:bui-mr-5{margin-right:var(--bui-space-5)}.lg\:bui-mr-6{margin-right:var(--bui-space-6)}.lg\:bui-mr-7{margin-right:var(--bui-space-7)}.lg\:bui-mr-8{margin-right:var(--bui-space-8)}.lg\:bui-mr-9{margin-right:var(--bui-space-9)}.lg\:bui-mr-10{margin-right:var(--bui-space-10)}.lg\:bui-mr-11{margin-right:var(--bui-space-11)}.lg\:bui-mr-12{margin-right:var(--bui-space-12)}.lg\:bui-mr-13{margin-right:var(--bui-space-13)}.lg\:bui-mr-14{margin-right:var(--bui-space-14)}}@media (width>=1536px){.xl\:bui-mr{margin-right:var(--pr-xl)}.xl\:bui-mr-0\.5{margin-right:var(--bui-space-0_5)}.xl\:bui-mr-1{margin-right:var(--bui-space-1)}.xl\:bui-mr-1\.5{margin-right:var(--bui-space-1_5)}.xl\:bui-mr-2{margin-right:var(--bui-space-2)}.xl\:bui-mr-3{margin-right:var(--bui-space-3)}.xl\:bui-mr-4{margin-right:var(--bui-space-4)}.xl\:bui-mr-5{margin-right:var(--bui-space-5)}.xl\:bui-mr-6{margin-right:var(--bui-space-6)}.xl\:bui-mr-7{margin-right:var(--bui-space-7)}.xl\:bui-mr-8{margin-right:var(--bui-space-8)}.xl\:bui-mr-9{margin-right:var(--bui-space-9)}.xl\:bui-mr-10{margin-right:var(--bui-space-10)}.xl\:bui-mr-11{margin-right:var(--bui-space-11)}.xl\:bui-mr-12{margin-right:var(--bui-space-12)}.xl\:bui-mr-13{margin-right:var(--bui-space-13)}.xl\:bui-mr-14{margin-right:var(--bui-space-14)}}.bui-mt{margin-top:var(--mt)}.bui-mt-0\.5{margin-top:var(--bui-space-0_5)}.bui-mt-1{margin-top:var(--bui-space-1)}.bui-mt-1\.5{margin-top:var(--bui-space-1_5)}.bui-mt-2{margin-top:var(--bui-space-2)}.bui-mt-3{margin-top:var(--bui-space-3)}.bui-mt-4{margin-top:var(--bui-space-4)}.bui-mt-5{margin-top:var(--bui-space-5)}.bui-mt-6{margin-top:var(--bui-space-6)}.bui-mt-7{margin-top:var(--bui-space-7)}.bui-mt-8{margin-top:var(--bui-space-8)}.bui-mt-9{margin-top:var(--bui-space-9)}.bui-mt-10{margin-top:var(--bui-space-10)}.bui-mt-11{margin-top:var(--bui-space-11)}.bui-mt-12{margin-top:var(--bui-space-12)}.bui-mt-13{margin-top:var(--bui-space-13)}.bui-mt-14{margin-top:var(--bui-space-14)}@media (width>=640px){.xs\:bui-mt{margin-top:var(--pt-xs)}.xs\:bui-mt-0\.5{margin-top:var(--bui-space-0_5)}.xs\:bui-mt-1{margin-top:var(--bui-space-1)}.xs\:bui-mt-1\.5{margin-top:var(--bui-space-1_5)}.xs\:bui-mt-2{margin-top:var(--bui-space-2)}.xs\:bui-mt-3{margin-top:var(--bui-space-3)}.xs\:bui-mt-4{margin-top:var(--bui-space-4)}.xs\:bui-mt-5{margin-top:var(--bui-space-5)}.xs\:bui-mt-6{margin-top:var(--bui-space-6)}.xs\:bui-mt-7{margin-top:var(--bui-space-7)}.xs\:bui-mt-8{margin-top:var(--bui-space-8)}.xs\:bui-mt-9{margin-top:var(--bui-space-9)}.xs\:bui-mt-10{margin-top:var(--bui-space-10)}.xs\:bui-mt-11{margin-top:var(--bui-space-11)}.xs\:bui-mt-12{margin-top:var(--bui-space-12)}.xs\:bui-mt-13{margin-top:var(--bui-space-13)}.xs\:bui-mt-14{margin-top:var(--bui-space-14)}}@media (width>=768px){.sm\:bui-mt{margin-top:var(--pt-sm)}.sm\:bui-mt-0\.5{margin-top:var(--bui-space-0_5)}.sm\:bui-mt-1{margin-top:var(--bui-space-1)}.sm\:bui-mt-1\.5{margin-top:var(--bui-space-1_5)}.sm\:bui-mt-2{margin-top:var(--bui-space-2)}.sm\:bui-mt-3{margin-top:var(--bui-space-3)}.sm\:bui-mt-4{margin-top:var(--bui-space-4)}.sm\:bui-mt-5{margin-top:var(--bui-space-5)}.sm\:bui-mt-6{margin-top:var(--bui-space-6)}.sm\:bui-mt-7{margin-top:var(--bui-space-7)}.sm\:bui-mt-8{margin-top:var(--bui-space-8)}.sm\:bui-mt-9{margin-top:var(--bui-space-9)}.sm\:bui-mt-10{margin-top:var(--bui-space-10)}.sm\:bui-mt-11{margin-top:var(--bui-space-11)}.sm\:bui-mt-12{margin-top:var(--bui-space-12)}.sm\:bui-mt-13{margin-top:var(--bui-space-13)}.sm\:bui-mt-14{margin-top:var(--bui-space-14)}}@media (width>=1024px){.md\:bui-mt{margin-top:var(--pt-md)}.md\:bui-mt-0\.5{margin-top:var(--bui-space-0_5)}.md\:bui-mt-1{margin-top:var(--bui-space-1)}.md\:bui-mt-1\.5{margin-top:var(--bui-space-1_5)}.md\:bui-mt-2{margin-top:var(--bui-space-2)}.md\:bui-mt-3{margin-top:var(--bui-space-3)}.md\:bui-mt-4{margin-top:var(--bui-space-4)}.md\:bui-mt-5{margin-top:var(--bui-space-5)}.md\:bui-mt-6{margin-top:var(--bui-space-6)}.md\:bui-mt-7{margin-top:var(--bui-space-7)}.md\:bui-mt-8{margin-top:var(--bui-space-8)}.md\:bui-mt-9{margin-top:var(--bui-space-9)}.md\:bui-mt-10{margin-top:var(--bui-space-10)}.md\:bui-mt-11{margin-top:var(--bui-space-11)}.md\:bui-mt-12{margin-top:var(--bui-space-12)}.md\:bui-mt-13{margin-top:var(--bui-space-13)}.md\:bui-mt-14{margin-top:var(--bui-space-14)}}@media (width>=1280px){.lg\:bui-mt{margin-top:var(--pt-lg)}.lg\:bui-mt-0\.5{margin-top:var(--bui-space-0_5)}.lg\:bui-mt-1{margin-top:var(--bui-space-1)}.lg\:bui-mt-1\.5{margin-top:var(--bui-space-1_5)}.lg\:bui-mt-2{margin-top:var(--bui-space-2)}.lg\:bui-mt-3{margin-top:var(--bui-space-3)}.lg\:bui-mt-4{margin-top:var(--bui-space-4)}.lg\:bui-mt-5{margin-top:var(--bui-space-5)}.lg\:bui-mt-6{margin-top:var(--bui-space-6)}.lg\:bui-mt-7{margin-top:var(--bui-space-7)}.lg\:bui-mt-8{margin-top:var(--bui-space-8)}.lg\:bui-mt-9{margin-top:var(--bui-space-9)}.lg\:bui-mt-10{margin-top:var(--bui-space-10)}.lg\:bui-mt-11{margin-top:var(--bui-space-11)}.lg\:bui-mt-12{margin-top:var(--bui-space-12)}.lg\:bui-mt-13{margin-top:var(--bui-space-13)}.lg\:bui-mt-14{margin-top:var(--bui-space-14)}}@media (width>=1536px){.xl\:bui-mt{margin-top:var(--pt-xl)}.xl\:bui-mt-0\.5{margin-top:var(--bui-space-0_5)}.xl\:bui-mt-1{margin-top:var(--bui-space-1)}.xl\:bui-mt-1\.5{margin-top:var(--bui-space-1_5)}.xl\:bui-mt-2{margin-top:var(--bui-space-2)}.xl\:bui-mt-3{margin-top:var(--bui-space-3)}.xl\:bui-mt-4{margin-top:var(--bui-space-4)}.xl\:bui-mt-5{margin-top:var(--bui-space-5)}.xl\:bui-mt-6{margin-top:var(--bui-space-6)}.xl\:bui-mt-7{margin-top:var(--bui-space-7)}.xl\:bui-mt-8{margin-top:var(--bui-space-8)}.xl\:bui-mt-9{margin-top:var(--bui-space-9)}.xl\:bui-mt-10{margin-top:var(--bui-space-10)}.xl\:bui-mt-11{margin-top:var(--bui-space-11)}.xl\:bui-mt-12{margin-top:var(--bui-space-12)}.xl\:bui-mt-13{margin-top:var(--bui-space-13)}.xl\:bui-mt-14{margin-top:var(--bui-space-14)}}.bui-mb{margin-bottom:var(--mb)}.bui-mb-0\.5{margin-bottom:var(--bui-space-0_5)}.bui-mb-1{margin-bottom:var(--bui-space-1)}.bui-mb-1\.5{margin-bottom:var(--bui-space-1_5)}.bui-mb-2{margin-bottom:var(--bui-space-2)}.bui-mb-3{margin-bottom:var(--bui-space-3)}.bui-mb-4{margin-bottom:var(--bui-space-4)}.bui-mb-5{margin-bottom:var(--bui-space-5)}.bui-mb-6{margin-bottom:var(--bui-space-6)}.bui-mb-7{margin-bottom:var(--bui-space-7)}.bui-mb-8{margin-bottom:var(--bui-space-8)}.bui-mb-9{margin-bottom:var(--bui-space-9)}.bui-mb-10{margin-bottom:var(--bui-space-10)}.bui-mb-11{margin-bottom:var(--bui-space-11)}.bui-mb-12{margin-bottom:var(--bui-space-12)}.bui-mb-13{margin-bottom:var(--bui-space-13)}.bui-mb-14{margin-bottom:var(--bui-space-14)}@media (width>=640px){.xs\:bui-mb{margin-bottom:var(--pb-xs)}.xs\:bui-mb-0\.5{margin-bottom:var(--bui-space-0_5)}.xs\:bui-mb-1{margin-bottom:var(--bui-space-1)}.xs\:bui-mb-1\.5{margin-bottom:var(--bui-space-1_5)}.xs\:bui-mb-2{margin-bottom:var(--bui-space-2)}.xs\:bui-mb-3{margin-bottom:var(--bui-space-3)}.xs\:bui-mb-4{margin-bottom:var(--bui-space-4)}.xs\:bui-mb-5{margin-bottom:var(--bui-space-5)}.xs\:bui-mb-6{margin-bottom:var(--bui-space-6)}.xs\:bui-mb-7{margin-bottom:var(--bui-space-7)}.xs\:bui-mb-8{margin-bottom:var(--bui-space-8)}.xs\:bui-mb-9{margin-bottom:var(--bui-space-9)}.xs\:bui-mb-10{margin-bottom:var(--bui-space-10)}.xs\:bui-mb-11{margin-bottom:var(--bui-space-11)}.xs\:bui-mb-12{margin-bottom:var(--bui-space-12)}.xs\:bui-mb-13{margin-bottom:var(--bui-space-13)}.xs\:bui-mb-14{margin-bottom:var(--bui-space-14)}}@media (width>=768px){.sm\:bui-mb{margin-bottom:var(--pb-sm)}.sm\:bui-mb-0\.5{margin-bottom:var(--bui-space-0_5)}.sm\:bui-mb-1{margin-bottom:var(--bui-space-1)}.sm\:bui-mb-1\.5{margin-bottom:var(--bui-space-1_5)}.sm\:bui-mb-2{margin-bottom:var(--bui-space-2)}.sm\:bui-mb-3{margin-bottom:var(--bui-space-3)}.sm\:bui-mb-4{margin-bottom:var(--bui-space-4)}.sm\:bui-mb-5{margin-bottom:var(--bui-space-5)}.sm\:bui-mb-6{margin-bottom:var(--bui-space-6)}.sm\:bui-mb-7{margin-bottom:var(--bui-space-7)}.sm\:bui-mb-8{margin-bottom:var(--bui-space-8)}.sm\:bui-mb-9{margin-bottom:var(--bui-space-9)}.sm\:bui-mb-10{margin-bottom:var(--bui-space-10)}.sm\:bui-mb-11{margin-bottom:var(--bui-space-11)}.sm\:bui-mb-12{margin-bottom:var(--bui-space-12)}.sm\:bui-mb-13{margin-bottom:var(--bui-space-13)}.sm\:bui-mb-14{margin-bottom:var(--bui-space-14)}}@media (width>=1024px){.md\:bui-mb{margin-bottom:var(--pb-md)}.md\:bui-mb-0\.5{margin-bottom:var(--bui-space-0_5)}.md\:bui-mb-1{margin-bottom:var(--bui-space-1)}.md\:bui-mb-1\.5{margin-bottom:var(--bui-space-1_5)}.md\:bui-mb-2{margin-bottom:var(--bui-space-2)}.md\:bui-mb-3{margin-bottom:var(--bui-space-3)}.md\:bui-mb-4{margin-bottom:var(--bui-space-4)}.md\:bui-mb-5{margin-bottom:var(--bui-space-5)}.md\:bui-mb-6{margin-bottom:var(--bui-space-6)}.md\:bui-mb-7{margin-bottom:var(--bui-space-7)}.md\:bui-mb-8{margin-bottom:var(--bui-space-8)}.md\:bui-mb-9{margin-bottom:var(--bui-space-9)}.md\:bui-mb-10{margin-bottom:var(--bui-space-10)}.md\:bui-mb-11{margin-bottom:var(--bui-space-11)}.md\:bui-mb-12{margin-bottom:var(--bui-space-12)}.md\:bui-mb-13{margin-bottom:var(--bui-space-13)}.md\:bui-mb-14{margin-bottom:var(--bui-space-14)}}@media (width>=1280px){.lg\:bui-mb{margin-bottom:var(--pb-lg)}.lg\:bui-mb-0\.5{margin-bottom:var(--bui-space-0_5)}.lg\:bui-mb-1{margin-bottom:var(--bui-space-1)}.lg\:bui-mb-1\.5{margin-bottom:var(--bui-space-1_5)}.lg\:bui-mb-2{margin-bottom:var(--bui-space-2)}.lg\:bui-mb-3{margin-bottom:var(--bui-space-3)}.lg\:bui-mb-4{margin-bottom:var(--bui-space-4)}.lg\:bui-mb-5{margin-bottom:var(--bui-space-5)}.lg\:bui-mb-6{margin-bottom:var(--bui-space-6)}.lg\:bui-mb-7{margin-bottom:var(--bui-space-7)}.lg\:bui-mb-8{margin-bottom:var(--bui-space-8)}.lg\:bui-mb-9{margin-bottom:var(--bui-space-9)}.lg\:bui-mb-10{margin-bottom:var(--bui-space-10)}.lg\:bui-mb-11{margin-bottom:var(--bui-space-11)}.lg\:bui-mb-12{margin-bottom:var(--bui-space-12)}.lg\:bui-mb-13{margin-bottom:var(--bui-space-13)}.lg\:bui-mb-14{margin-bottom:var(--bui-space-14)}}@media (width>=1536px){.xl\:bui-mb{margin-bottom:var(--pb-xl)}.xl\:bui-mb-0\.5{margin-bottom:var(--bui-space-0_5)}.xl\:bui-mb-1{margin-bottom:var(--bui-space-1)}.xl\:bui-mb-1\.5{margin-bottom:var(--bui-space-1_5)}.xl\:bui-mb-2{margin-bottom:var(--bui-space-2)}.xl\:bui-mb-3{margin-bottom:var(--bui-space-3)}.xl\:bui-mb-4{margin-bottom:var(--bui-space-4)}.xl\:bui-mb-5{margin-bottom:var(--bui-space-5)}.xl\:bui-mb-6{margin-bottom:var(--bui-space-6)}.xl\:bui-mb-7{margin-bottom:var(--bui-space-7)}.xl\:bui-mb-8{margin-bottom:var(--bui-space-8)}.xl\:bui-mb-9{margin-bottom:var(--bui-space-9)}.xl\:bui-mb-10{margin-bottom:var(--bui-space-10)}.xl\:bui-mb-11{margin-bottom:var(--bui-space-11)}.xl\:bui-mb-12{margin-bottom:var(--bui-space-12)}.xl\:bui-mb-13{margin-bottom:var(--bui-space-13)}.xl\:bui-mb-14{margin-bottom:var(--bui-space-14)}}.bui-my{margin-top:var(--my);margin-bottom:var(--my)}.bui-my-0\.5{margin-top:var(--bui-space-0_5);margin-bottom:var(--bui-space-0_5)}.bui-my-1{margin-top:var(--bui-space-1);margin-bottom:var(--bui-space-1)}.bui-my-1\.5{margin-top:var(--bui-space-1_5);margin-bottom:var(--bui-space-1_5)}.bui-my-2{margin-top:var(--bui-space-2);margin-bottom:var(--bui-space-2)}.bui-my-3{margin-top:var(--bui-space-3);margin-bottom:var(--bui-space-3)}.bui-my-4{margin-top:var(--bui-space-4);margin-bottom:var(--bui-space-4)}.bui-my-5{margin-top:var(--bui-space-5);margin-bottom:var(--bui-space-5)}.bui-my-6{margin-top:var(--bui-space-6);margin-bottom:var(--bui-space-6)}.bui-my-7{margin-top:var(--bui-space-7);margin-bottom:var(--bui-space-7)}.bui-my-8{margin-top:var(--bui-space-8);margin-bottom:var(--bui-space-8)}.bui-my-9{margin-top:var(--bui-space-9);margin-bottom:var(--bui-space-9)}.bui-my-10{margin-top:var(--bui-space-10);margin-bottom:var(--bui-space-10)}.bui-my-11{margin-top:var(--bui-space-11);margin-bottom:var(--bui-space-11)}.bui-my-12{margin-top:var(--bui-space-12);margin-bottom:var(--bui-space-12)}.bui-my-13{margin-top:var(--bui-space-13);margin-bottom:var(--bui-space-13)}.bui-my-14{margin-top:var(--bui-space-14);margin-bottom:var(--bui-space-14)}@media (width>=640px){.xs\:bui-my{margin-top:var(--py-xs);margin-bottom:var(--py-xs)}.xs\:bui-my-0\.5{margin-top:var(--bui-space-0_5);margin-bottom:var(--bui-space-0_5)}.xs\:bui-my-1{margin-top:var(--bui-space-1);margin-bottom:var(--bui-space-1)}.xs\:bui-my-1\.5{margin-top:var(--bui-space-1_5);margin-bottom:var(--bui-space-1_5)}.xs\:bui-my-2{margin-top:var(--bui-space-2);margin-bottom:var(--bui-space-2)}.xs\:bui-my-3{margin-top:var(--bui-space-3);margin-bottom:var(--bui-space-3)}.xs\:bui-my-4{margin-top:var(--bui-space-4);margin-bottom:var(--bui-space-4)}.xs\:bui-my-5{margin-top:var(--bui-space-5);margin-bottom:var(--bui-space-5)}.xs\:bui-my-6{margin-top:var(--bui-space-6);margin-bottom:var(--bui-space-6)}.xs\:bui-my-7{margin-top:var(--bui-space-7);margin-bottom:var(--bui-space-7)}.xs\:bui-my-8{margin-top:var(--bui-space-8);margin-bottom:var(--bui-space-8)}.xs\:bui-my-9{margin-top:var(--bui-space-9);margin-bottom:var(--bui-space-9)}.xs\:bui-my-10{margin-top:var(--bui-space-10);margin-bottom:var(--bui-space-10)}.xs\:bui-my-11{margin-top:var(--bui-space-11);margin-bottom:var(--bui-space-11)}.xs\:bui-my-12{margin-top:var(--bui-space-12);margin-bottom:var(--bui-space-12)}.xs\:bui-my-13{margin-top:var(--bui-space-13);margin-bottom:var(--bui-space-13)}.xs\:bui-my-14{margin-top:var(--bui-space-14);margin-bottom:var(--bui-space-14)}}@media (width>=768px){.sm\:bui-my{margin-top:var(--py-sm);margin-bottom:var(--py-sm)}.sm\:bui-my-0\.5{margin-top:var(--bui-space-0_5);margin-bottom:var(--bui-space-0_5)}.sm\:bui-my-1{margin-top:var(--bui-space-1);margin-bottom:var(--bui-space-1)}.sm\:bui-my-1\.5{margin-top:var(--bui-space-1_5);margin-bottom:var(--bui-space-1_5)}.sm\:bui-my-2{margin-top:var(--bui-space-2);margin-bottom:var(--bui-space-2)}.sm\:bui-my-3{margin-top:var(--bui-space-3);margin-bottom:var(--bui-space-3)}.sm\:bui-my-4{margin-top:var(--bui-space-4);margin-bottom:var(--bui-space-4)}.sm\:bui-my-5{margin-top:var(--bui-space-5);margin-bottom:var(--bui-space-5)}.sm\:bui-my-6{margin-top:var(--bui-space-6);margin-bottom:var(--bui-space-6)}.sm\:bui-my-7{margin-top:var(--bui-space-7);margin-bottom:var(--bui-space-7)}.sm\:bui-my-8{margin-top:var(--bui-space-8);margin-bottom:var(--bui-space-8)}.sm\:bui-my-9{margin-top:var(--bui-space-9);margin-bottom:var(--bui-space-9)}.sm\:bui-my-10{margin-top:var(--bui-space-10);margin-bottom:var(--bui-space-10)}.sm\:bui-my-11{margin-top:var(--bui-space-11);margin-bottom:var(--bui-space-11)}.sm\:bui-my-12{margin-top:var(--bui-space-12);margin-bottom:var(--bui-space-12)}.sm\:bui-my-13{margin-top:var(--bui-space-13);margin-bottom:var(--bui-space-13)}.sm\:bui-my-14{margin-top:var(--bui-space-14);margin-bottom:var(--bui-space-14)}}@media (width>=1024px){.md\:bui-my{margin-top:var(--py-md);margin-bottom:var(--py-md)}.md\:bui-my-0\.5{margin-top:var(--bui-space-0_5);margin-bottom:var(--bui-space-0_5)}.md\:bui-my-1{margin-top:var(--bui-space-1);margin-bottom:var(--bui-space-1)}.md\:bui-my-1\.5{margin-top:var(--bui-space-1_5);margin-bottom:var(--bui-space-1_5)}.md\:bui-my-2{margin-top:var(--bui-space-2);margin-bottom:var(--bui-space-2)}.md\:bui-my-3{margin-top:var(--bui-space-3);margin-bottom:var(--bui-space-3)}.md\:bui-my-4{margin-top:var(--bui-space-4);margin-bottom:var(--bui-space-4)}.md\:bui-my-5{margin-top:var(--bui-space-5);margin-bottom:var(--bui-space-5)}.md\:bui-my-6{margin-top:var(--bui-space-6);margin-bottom:var(--bui-space-6)}.md\:bui-my-7{margin-top:var(--bui-space-7);margin-bottom:var(--bui-space-7)}.md\:bui-my-8{margin-top:var(--bui-space-8);margin-bottom:var(--bui-space-8)}.md\:bui-my-9{margin-top:var(--bui-space-9);margin-bottom:var(--bui-space-9)}.md\:bui-my-10{margin-top:var(--bui-space-10);margin-bottom:var(--bui-space-10)}.md\:bui-my-11{margin-top:var(--bui-space-11);margin-bottom:var(--bui-space-11)}.md\:bui-my-12{margin-top:var(--bui-space-12);margin-bottom:var(--bui-space-12)}.md\:bui-my-13{margin-top:var(--bui-space-13);margin-bottom:var(--bui-space-13)}.md\:bui-my-14{margin-top:var(--bui-space-14);margin-bottom:var(--bui-space-14)}}@media (width>=1280px){.lg\:bui-my{margin-top:var(--py-lg);margin-bottom:var(--py-lg)}.lg\:bui-my-0\.5{margin-top:var(--bui-space-0_5);margin-bottom:var(--bui-space-0_5)}.lg\:bui-my-1{margin-top:var(--bui-space-1);margin-bottom:var(--bui-space-1)}.lg\:bui-my-1\.5{margin-top:var(--bui-space-1_5);margin-bottom:var(--bui-space-1_5)}.lg\:bui-my-2{margin-top:var(--bui-space-2);margin-bottom:var(--bui-space-2)}.lg\:bui-my-3{margin-top:var(--bui-space-3);margin-bottom:var(--bui-space-3)}.lg\:bui-my-4{margin-top:var(--bui-space-4);margin-bottom:var(--bui-space-4)}.lg\:bui-my-5{margin-top:var(--bui-space-5);margin-bottom:var(--bui-space-5)}.lg\:bui-my-6{margin-top:var(--bui-space-6);margin-bottom:var(--bui-space-6)}.lg\:bui-my-7{margin-top:var(--bui-space-7);margin-bottom:var(--bui-space-7)}.lg\:bui-my-8{margin-top:var(--bui-space-8);margin-bottom:var(--bui-space-8)}.lg\:bui-my-9{margin-top:var(--bui-space-9);margin-bottom:var(--bui-space-9)}.lg\:bui-my-10{margin-top:var(--bui-space-10);margin-bottom:var(--bui-space-10)}.lg\:bui-my-11{margin-top:var(--bui-space-11);margin-bottom:var(--bui-space-11)}.lg\:bui-my-12{margin-top:var(--bui-space-12);margin-bottom:var(--bui-space-12)}.lg\:bui-my-13{margin-top:var(--bui-space-13);margin-bottom:var(--bui-space-13)}.lg\:bui-my-14{margin-top:var(--bui-space-14);margin-bottom:var(--bui-space-14)}}@media (width>=1536px){.xl\:bui-my{margin-top:var(--py-xl);margin-bottom:var(--py-xl)}.xl\:bui-my-0\.5{margin-top:var(--bui-space-0_5);margin-bottom:var(--bui-space-0_5)}.xl\:bui-my-1{margin-top:var(--bui-space-1);margin-bottom:var(--bui-space-1)}.xl\:bui-my-1\.5{margin-top:var(--bui-space-1_5);margin-bottom:var(--bui-space-1_5)}.xl\:bui-my-2{margin-top:var(--bui-space-2);margin-bottom:var(--bui-space-2)}.xl\:bui-my-3{margin-top:var(--bui-space-3);margin-bottom:var(--bui-space-3)}.xl\:bui-my-4{margin-top:var(--bui-space-4);margin-bottom:var(--bui-space-4)}.xl\:bui-my-5{margin-top:var(--bui-space-5);margin-bottom:var(--bui-space-5)}.xl\:bui-my-6{margin-top:var(--bui-space-6);margin-bottom:var(--bui-space-6)}.xl\:bui-my-7{margin-top:var(--bui-space-7);margin-bottom:var(--bui-space-7)}.xl\:bui-my-8{margin-top:var(--bui-space-8);margin-bottom:var(--bui-space-8)}.xl\:bui-my-9{margin-top:var(--bui-space-9);margin-bottom:var(--bui-space-9)}.xl\:bui-my-10{margin-top:var(--bui-space-10);margin-bottom:var(--bui-space-10)}.xl\:bui-my-11{margin-top:var(--bui-space-11);margin-bottom:var(--bui-space-11)}.xl\:bui-my-12{margin-top:var(--bui-space-12);margin-bottom:var(--bui-space-12)}.xl\:bui-my-13{margin-top:var(--bui-space-13);margin-bottom:var(--bui-space-13)}.xl\:bui-my-14{margin-top:var(--bui-space-14);margin-bottom:var(--bui-space-14)}}.bui-mx{margin-left:var(--mx);margin-right:var(--mx)}.bui-mx-0\.5{margin-left:var(--bui-space-0_5);margin-right:var(--bui-space-0_5)}.bui-mx-1{margin-left:var(--bui-space-1);margin-right:var(--bui-space-1)}.bui-mx-1\.5{margin-left:var(--bui-space-1_5);margin-right:var(--bui-space-1_5)}.bui-mx-2{margin-left:var(--bui-space-2);margin-right:var(--bui-space-2)}.bui-mx-3{margin-left:var(--bui-space-3);margin-right:var(--bui-space-3)}.bui-mx-4{margin-left:var(--bui-space-4);margin-right:var(--bui-space-4)}.bui-mx-5{margin-left:var(--bui-space-5);margin-right:var(--bui-space-5)}.bui-mx-6{margin-left:var(--bui-space-6);margin-right:var(--bui-space-6)}.bui-mx-7{margin-left:var(--bui-space-7);margin-right:var(--bui-space-7)}.bui-mx-8{margin-left:var(--bui-space-8);margin-right:var(--bui-space-8)}.bui-mx-9{margin-left:var(--bui-space-9);margin-right:var(--bui-space-9)}.bui-mx-10{margin-left:var(--bui-space-10);margin-right:var(--bui-space-10)}.bui-mx-11{margin-left:var(--bui-space-11);margin-right:var(--bui-space-11)}.bui-mx-12{margin-left:var(--bui-space-12);margin-right:var(--bui-space-12)}.bui-mx-13{margin-left:var(--bui-space-13);margin-right:var(--bui-space-13)}.bui-mx-14{margin-left:var(--bui-space-14);margin-right:var(--bui-space-14)}@media (width>=640px){.xs\:bui-mx{margin-left:var(--px-xs);margin-right:var(--px-xs)}.xs\:bui-mx-0\.5{margin-left:var(--bui-space-0_5);margin-right:var(--bui-space-0_5)}.xs\:bui-mx-1{margin-left:var(--bui-space-1);margin-right:var(--bui-space-1)}.xs\:bui-mx-1\.5{margin-left:var(--bui-space-1_5);margin-right:var(--bui-space-1_5)}.xs\:bui-mx-2{margin-left:var(--bui-space-2);margin-right:var(--bui-space-2)}.xs\:bui-mx-3{margin-left:var(--bui-space-3);margin-right:var(--bui-space-3)}.xs\:bui-mx-4{margin-left:var(--bui-space-4);margin-right:var(--bui-space-4)}.xs\:bui-mx-5{margin-left:var(--bui-space-5);margin-right:var(--bui-space-5)}.xs\:bui-mx-6{margin-left:var(--bui-space-6);margin-right:var(--bui-space-6)}.xs\:bui-mx-7{margin-left:var(--bui-space-7);margin-right:var(--bui-space-7)}.xs\:bui-mx-8{margin-left:var(--bui-space-8);margin-right:var(--bui-space-8)}.xs\:bui-mx-9{margin-left:var(--bui-space-9);margin-right:var(--bui-space-9)}.xs\:bui-mx-10{margin-left:var(--bui-space-10);margin-right:var(--bui-space-10)}.xs\:bui-mx-11{margin-left:var(--bui-space-11);margin-right:var(--bui-space-11)}.xs\:bui-mx-12{margin-left:var(--bui-space-12);margin-right:var(--bui-space-12)}.xs\:bui-mx-13{margin-left:var(--bui-space-13);margin-right:var(--bui-space-13)}.xs\:bui-mx-14{margin-left:var(--bui-space-14);margin-right:var(--bui-space-14)}}@media (width>=768px){.sm\:bui-mx{margin-left:var(--px-sm);margin-right:var(--px-sm)}.sm\:bui-mx-0\.5{margin-left:var(--bui-space-0_5);margin-right:var(--bui-space-0_5)}.sm\:bui-mx-1{margin-left:var(--bui-space-1);margin-right:var(--bui-space-1)}.sm\:bui-mx-1\.5{margin-left:var(--bui-space-1_5);margin-right:var(--bui-space-1_5)}.sm\:bui-mx-2{margin-left:var(--bui-space-2);margin-right:var(--bui-space-2)}.sm\:bui-mx-3{margin-left:var(--bui-space-3);margin-right:var(--bui-space-3)}.sm\:bui-mx-4{margin-left:var(--bui-space-4);margin-right:var(--bui-space-4)}.sm\:bui-mx-5{margin-left:var(--bui-space-5);margin-right:var(--bui-space-5)}.sm\:bui-mx-6{margin-left:var(--bui-space-6);margin-right:var(--bui-space-6)}.sm\:bui-mx-7{margin-left:var(--bui-space-7);margin-right:var(--bui-space-7)}.sm\:bui-mx-8{margin-left:var(--bui-space-8);margin-right:var(--bui-space-8)}.sm\:bui-mx-9{margin-left:var(--bui-space-9);margin-right:var(--bui-space-9)}.sm\:bui-mx-10{margin-left:var(--bui-space-10);margin-right:var(--bui-space-10)}.sm\:bui-mx-11{margin-left:var(--bui-space-11);margin-right:var(--bui-space-11)}.sm\:bui-mx-12{margin-left:var(--bui-space-12);margin-right:var(--bui-space-12)}.sm\:bui-mx-13{margin-left:var(--bui-space-13);margin-right:var(--bui-space-13)}.sm\:bui-mx-14{margin-left:var(--bui-space-14);margin-right:var(--bui-space-14)}}@media (width>=1024px){.md\:bui-mx{margin-left:var(--px-md);margin-right:var(--px-md)}.md\:bui-mx-0\.5{margin-left:var(--bui-space-0_5);margin-right:var(--bui-space-0_5)}.md\:bui-mx-1{margin-left:var(--bui-space-1);margin-right:var(--bui-space-1)}.md\:bui-mx-1\.5{margin-left:var(--bui-space-1_5);margin-right:var(--bui-space-1_5)}.md\:bui-mx-2{margin-left:var(--bui-space-2);margin-right:var(--bui-space-2)}.md\:bui-mx-3{margin-left:var(--bui-space-3);margin-right:var(--bui-space-3)}.md\:bui-mx-4{margin-left:var(--bui-space-4);margin-right:var(--bui-space-4)}.md\:bui-mx-5{margin-left:var(--bui-space-5);margin-right:var(--bui-space-5)}.md\:bui-mx-6{margin-left:var(--bui-space-6);margin-right:var(--bui-space-6)}.md\:bui-mx-7{margin-left:var(--bui-space-7);margin-right:var(--bui-space-7)}.md\:bui-mx-8{margin-left:var(--bui-space-8);margin-right:var(--bui-space-8)}.md\:bui-mx-9{margin-left:var(--bui-space-9);margin-right:var(--bui-space-9)}.md\:bui-mx-10{margin-left:var(--bui-space-10);margin-right:var(--bui-space-10)}.md\:bui-mx-11{margin-left:var(--bui-space-11);margin-right:var(--bui-space-11)}.md\:bui-mx-12{margin-left:var(--bui-space-12);margin-right:var(--bui-space-12)}.md\:bui-mx-13{margin-left:var(--bui-space-13);margin-right:var(--bui-space-13)}.md\:bui-mx-14{margin-left:var(--bui-space-14);margin-right:var(--bui-space-14)}}@media (width>=1280px){.lg\:bui-mx{margin-left:var(--px-lg);margin-right:var(--px-lg)}.lg\:bui-mx-0\.5{margin-left:var(--bui-space-0_5);margin-right:var(--bui-space-0_5)}.lg\:bui-mx-1{margin-left:var(--bui-space-1);margin-right:var(--bui-space-1)}.lg\:bui-mx-1\.5{margin-left:var(--bui-space-1_5);margin-right:var(--bui-space-1_5)}.lg\:bui-mx-2{margin-left:var(--bui-space-2);margin-right:var(--bui-space-2)}.lg\:bui-mx-3{margin-left:var(--bui-space-3);margin-right:var(--bui-space-3)}.lg\:bui-mx-4{margin-left:var(--bui-space-4);margin-right:var(--bui-space-4)}.lg\:bui-mx-5{margin-left:var(--bui-space-5);margin-right:var(--bui-space-5)}.lg\:bui-mx-6{margin-left:var(--bui-space-6);margin-right:var(--bui-space-6)}.lg\:bui-mx-7{margin-left:var(--bui-space-7);margin-right:var(--bui-space-7)}.lg\:bui-mx-8{margin-left:var(--bui-space-8);margin-right:var(--bui-space-8)}.lg\:bui-mx-9{margin-left:var(--bui-space-9);margin-right:var(--bui-space-9)}.lg\:bui-mx-10{margin-left:var(--bui-space-10);margin-right:var(--bui-space-10)}.lg\:bui-mx-11{margin-left:var(--bui-space-11);margin-right:var(--bui-space-11)}.lg\:bui-mx-12{margin-left:var(--bui-space-12);margin-right:var(--bui-space-12)}.lg\:bui-mx-13{margin-left:var(--bui-space-13);margin-right:var(--bui-space-13)}.lg\:bui-mx-14{margin-left:var(--bui-space-14);margin-right:var(--bui-space-14)}}@media (width>=1536px){.xl\:bui-mx{margin-left:var(--px-xl);margin-right:var(--px-xl)}.xl\:bui-mx-0\.5{margin-left:var(--bui-space-0_5);margin-right:var(--bui-space-0_5)}.xl\:bui-mx-1{margin-left:var(--bui-space-1);margin-right:var(--bui-space-1)}.xl\:bui-mx-1\.5{margin-left:var(--bui-space-1_5);margin-right:var(--bui-space-1_5)}.xl\:bui-mx-2{margin-left:var(--bui-space-2);margin-right:var(--bui-space-2)}.xl\:bui-mx-3{margin-left:var(--bui-space-3);margin-right:var(--bui-space-3)}.xl\:bui-mx-4{margin-left:var(--bui-space-4);margin-right:var(--bui-space-4)}.xl\:bui-mx-5{margin-left:var(--bui-space-5);margin-right:var(--bui-space-5)}.xl\:bui-mx-6{margin-left:var(--bui-space-6);margin-right:var(--bui-space-6)}.xl\:bui-mx-7{margin-left:var(--bui-space-7);margin-right:var(--bui-space-7)}.xl\:bui-mx-8{margin-left:var(--bui-space-8);margin-right:var(--bui-space-8)}.xl\:bui-mx-9{margin-left:var(--bui-space-9);margin-right:var(--bui-space-9)}.xl\:bui-mx-10{margin-left:var(--bui-space-10);margin-right:var(--bui-space-10)}.xl\:bui-mx-11{margin-left:var(--bui-space-11);margin-right:var(--bui-space-11)}.xl\:bui-mx-12{margin-left:var(--bui-space-12);margin-right:var(--bui-space-12)}.xl\:bui-mx-13{margin-left:var(--bui-space-13);margin-right:var(--bui-space-13)}.xl\:bui-mx-14{margin-left:var(--bui-space-14);margin-right:var(--bui-space-14)}}.bui-display-none{display:none}.bui-display-inline{display:inline}.bui-display-inline-block{display:inline-block}.bui-display-block{display:block}@media (width>=640px){.xs\:bui-display-none{display:none}.xs\:bui-display-inline{display:inline}.xs\:bui-display-inline-block{display:inline-block}.xs\:bui-display-block{display:block}}@media (width>=768px){.sm\:bui-display-none{display:none}.sm\:bui-display-inline{display:inline}.sm\:bui-display-inline-block{display:inline-block}.sm\:bui-display-block{display:block}}@media (width>=1024px){.md\:bui-display-none{display:none}.md\:bui-display-inline{display:inline}.md\:bui-display-inline-block{display:inline-block}.md\:bui-display-block{display:block}}@media (width>=1280px){.lg\:bui-display-none{display:none}.lg\:bui-display-inline{display:inline}.lg\:bui-display-inline-block{display:inline-block}.lg\:bui-display-block{display:block}}@media (width>=1536px){.xl\:bui-display-none{display:none}.xl\:bui-display-inline{display:inline}.xl\:bui-display-inline-block{display:inline-block}.xl\:bui-display-block{display:block}}.bui-w{width:var(--width)}.bui-min-w{min-width:var(--min-width)}.bui-max-w{max-width:var(--max-width)}@media (width>=640px){.xs\:bui-w{width:var(--width)}.xs\:bui-min-w{min-width:var(--min-width)}.xs\:bui-max-w{max-width:var(--max-width)}}@media (width>=768px){.sm\:bui-w{width:var(--width)}.sm\:bui-min-w{min-width:var(--min-width)}.sm\:bui-max-w{max-width:var(--max-width)}}@media (width>=1024px){.md\:bui-w{width:var(--width)}.md\:bui-min-w{min-width:var(--min-width)}.md\:bui-max-w{max-width:var(--max-width)}}@media (width>=1280px){.lg\:bui-w{width:var(--width)}.lg\:bui-min-w{min-width:var(--min-width)}.lg\:bui-max-w{max-width:var(--max-width)}}@media (width>=1536px){.xl\:bui-w{width:var(--width)}.xl\:bui-min-w{min-width:var(--min-width)}.xl\:bui-max-w{max-width:var(--max-width)}}.bui-h{height:var(--height)}.bui-min-h{min-height:var(--min-height)}.bui-max-h{max-height:var(--max-height)}@media (width>=640px){.xs\:bui-h{height:var(--height)}.xs\:bui-min-h{min-height:var(--min-height)}.xs\:bui-max-h{max-height:var(--max-height)}}@media (width>=768px){.sm\:bui-h{height:var(--height)}.sm\:bui-min-h{min-height:var(--min-height)}.sm\:bui-max-h{max-height:var(--max-height)}}@media (width>=1024px){.md\:bui-h{height:var(--height)}.md\:bui-min-h{min-height:var(--min-height)}.md\:bui-max-h{max-height:var(--max-height)}}@media (width>=1280px){.lg\:bui-h{height:var(--height)}.lg\:bui-min-h{min-height:var(--min-height)}.lg\:bui-max-h{max-height:var(--max-height)}}@media (width>=1536px){.xl\:bui-h{height:var(--height)}.xl\:bui-min-h{min-height:var(--min-height)}.xl\:bui-max-h{max-height:var(--max-height)}}.bui-position-absolute{position:absolute}.bui-position-fixed{position:fixed}.bui-position-sticky{position:sticky}.bui-position-relative{position:relative}.bui-position-static{position:static}@media (width>=640px){.xs\:bui-position-absolute{position:absolute}.xs\:bui-position-fixed{position:fixed}.xs\:bui-position-sticky{position:sticky}.xs\:bui-position-relative{position:relative}.xs\:bui-position-static{position:static}}@media (width>=768px){.sm\:bui-position-absolute{position:absolute}.sm\:bui-position-fixed{position:fixed}.sm\:bui-position-sticky{position:sticky}.sm\:bui-position-relative{position:relative}.sm\:bui-position-static{position:static}}@media (width>=1024px){.md\:bui-position-absolute{position:absolute}.md\:bui-position-fixed{position:fixed}.md\:bui-position-sticky{position:sticky}.md\:bui-position-relative{position:relative}.md\:bui-position-static{position:static}}@media (width>=1280px){.lg\:bui-position-absolute{position:absolute}.lg\:bui-position-fixed{position:fixed}.lg\:bui-position-sticky{position:sticky}.lg\:bui-position-relative{position:relative}.lg\:bui-position-static{position:static}}@media (width>=1536px){.xl\:bui-position-absolute{position:absolute}.xl\:bui-position-fixed{position:fixed}.xl\:bui-position-sticky{position:sticky}.xl\:bui-position-relative{position:relative}.xl\:bui-position-static{position:static}}.bui-columns-1{grid-template-columns:repeat(1,minmax(0,1fr))}.bui-columns-2{grid-template-columns:repeat(2,minmax(0,1fr))}.bui-columns-3{grid-template-columns:repeat(3,minmax(0,1fr))}.bui-columns-4{grid-template-columns:repeat(4,minmax(0,1fr))}.bui-columns-5{grid-template-columns:repeat(5,minmax(0,1fr))}.bui-columns-6{grid-template-columns:repeat(6,minmax(0,1fr))}.bui-columns-7{grid-template-columns:repeat(7,minmax(0,1fr))}.bui-columns-8{grid-template-columns:repeat(8,minmax(0,1fr))}.bui-columns-9{grid-template-columns:repeat(9,minmax(0,1fr))}.bui-columns-10{grid-template-columns:repeat(10,minmax(0,1fr))}.bui-columns-11{grid-template-columns:repeat(11,minmax(0,1fr))}.bui-columns-12{grid-template-columns:repeat(12,minmax(0,1fr))}.bui-columns-auto{grid-template-columns:repeat(auto-fit,minmax(0,1fr))}.bui-col-span-1{grid-column:span 1/span 1}.bui-col-span-2{grid-column:span 2/span 2}.bui-col-span-3{grid-column:span 3/span 3}.bui-col-span-4{grid-column:span 4/span 4}.bui-col-span-5{grid-column:span 5/span 5}.bui-col-span-6{grid-column:span 6/span 6}.bui-col-span-7{grid-column:span 7/span 7}.bui-col-span-8{grid-column:span 8/span 8}.bui-col-span-9{grid-column:span 9/span 9}.bui-col-span-10{grid-column:span 10/span 10}.bui-col-span-11{grid-column:span 11/span 11}.bui-col-span-12{grid-column:span 12/span 12}.bui-col-span-auto{grid-column:span auto/span auto}.bui-col-start-1{grid-column-start:1}.bui-col-start-2{grid-column-start:2}.bui-col-start-3{grid-column-start:3}.bui-col-start-4{grid-column-start:4}.bui-col-start-5{grid-column-start:5}.bui-col-start-6{grid-column-start:6}.bui-col-start-7{grid-column-start:7}.bui-col-start-8{grid-column-start:8}.bui-col-start-9{grid-column-start:9}.bui-col-start-10{grid-column-start:10}.bui-col-start-11{grid-column-start:11}.bui-col-start-12{grid-column-start:12}.bui-col-start-13{grid-column-start:13}.bui-col-start-auto{grid-column-start:auto}.bui-col-end-1{grid-column-end:1}.bui-col-end-2{grid-column-end:2}.bui-col-end-3{grid-column-end:3}.bui-col-end-4{grid-column-end:4}.bui-col-end-5{grid-column-end:5}.bui-col-end-6{grid-column-end:6}.bui-col-end-7{grid-column-end:7}.bui-col-end-8{grid-column-end:8}.bui-col-end-9{grid-column-end:9}.bui-col-end-10{grid-column-end:10}.bui-col-end-11{grid-column-end:11}.bui-col-end-12{grid-column-end:12}.bui-col-end-13{grid-column-end:13}.bui-col-end-auto{grid-column-end:auto}.bui-row-span-1{grid-row:span 1/span 1}.bui-row-span-2{grid-row:span 2/span 2}.bui-row-span-3{grid-row:span 3/span 3}.bui-row-span-4{grid-row:span 4/span 4}.bui-row-span-5{grid-row:span 5/span 5}.bui-row-span-6{grid-row:span 6/span 6}.bui-row-span-7{grid-row:span 7/span 7}.bui-row-span-8{grid-row:span 8/span 8}.bui-row-span-9{grid-row:span 9/span 9}.bui-row-span-10{grid-row:span 10/span 10}.bui-row-span-11{grid-row:span 11/span 11}.bui-row-span-12{grid-row:span 12/span 12}.bui-row-span-auto{grid-row:span auto/span auto}@media (width>=640px){.xs\:bui-columns-1{grid-template-columns:repeat(1,minmax(0,1fr))}.xs\:bui-columns-2{grid-template-columns:repeat(2,minmax(0,1fr))}.xs\:bui-columns-3{grid-template-columns:repeat(3,minmax(0,1fr))}.xs\:bui-columns-4{grid-template-columns:repeat(4,minmax(0,1fr))}.xs\:bui-columns-5{grid-template-columns:repeat(5,minmax(0,1fr))}.xs\:bui-columns-6{grid-template-columns:repeat(6,minmax(0,1fr))}.xs\:bui-columns-7{grid-template-columns:repeat(7,minmax(0,1fr))}.xs\:bui-columns-8{grid-template-columns:repeat(8,minmax(0,1fr))}.xs\:bui-columns-9{grid-template-columns:repeat(9,minmax(0,1fr))}.xs\:bui-columns-10{grid-template-columns:repeat(10,minmax(0,1fr))}.xs\:bui-columns-11{grid-template-columns:repeat(11,minmax(0,1fr))}.xs\:bui-columns-12{grid-template-columns:repeat(12,minmax(0,1fr))}.xs\:bui-columns-auto{grid-template-columns:repeat(auto-fit,minmax(0,1fr))}.xs\:bui-col-span-1{grid-column:span 1/span 1}.xs\:bui-col-span-2{grid-column:span 2/span 2}.xs\:bui-col-span-3{grid-column:span 3/span 3}.xs\:bui-col-span-4{grid-column:span 4/span 4}.xs\:bui-col-span-5{grid-column:span 5/span 5}.xs\:bui-col-span-6{grid-column:span 6/span 6}.xs\:bui-col-span-7{grid-column:span 7/span 7}.xs\:bui-col-span-8{grid-column:span 8/span 8}.xs\:bui-col-span-9{grid-column:span 9/span 9}.xs\:bui-col-span-10{grid-column:span 10/span 10}.xs\:bui-col-span-11{grid-column:span 11/span 11}.xs\:bui-col-span-12{grid-column:span 12/span 12}.xs\:bui-col-span-auto{grid-column:span auto/span auto}.xs\:bui-col-start-1{grid-column-start:1}.xs\:bui-col-start-2{grid-column-start:2}.xs\:bui-col-start-3{grid-column-start:3}.xs\:bui-col-start-4{grid-column-start:4}.xs\:bui-col-start-5{grid-column-start:5}.xs\:bui-col-start-6{grid-column-start:6}.xs\:bui-col-start-7{grid-column-start:7}.xs\:bui-col-start-8{grid-column-start:8}.xs\:bui-col-start-9{grid-column-start:9}.xs\:bui-col-start-10{grid-column-start:10}.xs\:bui-col-start-11{grid-column-start:11}.xs\:bui-col-start-12{grid-column-start:12}.xs\:bui-col-start-13{grid-column-start:13}.xs\:bui-col-start-auto{grid-column-start:auto}.xs\:bui-col-end-1{grid-column-end:1}.xs\:bui-col-end-2{grid-column-end:2}.xs\:bui-col-end-3{grid-column-end:3}.xs\:bui-col-end-4{grid-column-end:4}.xs\:bui-col-end-5{grid-column-end:5}.xs\:bui-col-end-6{grid-column-end:6}.xs\:bui-col-end-7{grid-column-end:7}.xs\:bui-col-end-8{grid-column-end:8}.xs\:bui-col-end-9{grid-column-end:9}.xs\:bui-col-end-10{grid-column-end:10}.xs\:bui-col-end-11{grid-column-end:11}.xs\:bui-col-end-12{grid-column-end:12}.xs\:bui-col-end-13{grid-column-end:13}.xs\:bui-col-end-auto{grid-column-end:auto}.xs\:bui-row-span-1{grid-row:span 1/span 1}.xs\:bui-row-span-2{grid-row:span 2/span 2}.xs\:bui-row-span-3{grid-row:span 3/span 3}.xs\:bui-row-span-4{grid-row:span 4/span 4}.xs\:bui-row-span-5{grid-row:span 5/span 5}.xs\:bui-row-span-6{grid-row:span 6/span 6}.xs\:bui-row-span-7{grid-row:span 7/span 7}.xs\:bui-row-span-8{grid-row:span 8/span 8}.xs\:bui-row-span-9{grid-row:span 9/span 9}.xs\:bui-row-span-10{grid-row:span 10/span 10}.xs\:bui-row-span-11{grid-row:span 11/span 11}.xs\:bui-row-span-12{grid-row:span 12/span 12}.xs\:bui-row-span-auto{grid-row:span auto/span auto}}@media (width>=768px){.sm\:bui-columns-1{grid-template-columns:repeat(1,minmax(0,1fr))}.sm\:bui-columns-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:bui-columns-3{grid-template-columns:repeat(3,minmax(0,1fr))}.sm\:bui-columns-4{grid-template-columns:repeat(4,minmax(0,1fr))}.sm\:bui-columns-5{grid-template-columns:repeat(5,minmax(0,1fr))}.sm\:bui-columns-6{grid-template-columns:repeat(6,minmax(0,1fr))}.sm\:bui-columns-7{grid-template-columns:repeat(7,minmax(0,1fr))}.sm\:bui-columns-8{grid-template-columns:repeat(8,minmax(0,1fr))}.sm\:bui-columns-9{grid-template-columns:repeat(9,minmax(0,1fr))}.sm\:bui-columns-10{grid-template-columns:repeat(10,minmax(0,1fr))}.sm\:bui-columns-11{grid-template-columns:repeat(11,minmax(0,1fr))}.sm\:bui-columns-12{grid-template-columns:repeat(12,minmax(0,1fr))}.sm\:bui-columns-auto{grid-template-columns:repeat(auto-fit,minmax(0,1fr))}.sm\:bui-col-span-1{grid-column:span 1/span 1}.sm\:bui-col-span-2{grid-column:span 2/span 2}.sm\:bui-col-span-3{grid-column:span 3/span 3}.sm\:bui-col-span-4{grid-column:span 4/span 4}.sm\:bui-col-span-5{grid-column:span 5/span 5}.sm\:bui-col-span-6{grid-column:span 6/span 6}.sm\:bui-col-span-7{grid-column:span 7/span 7}.sm\:bui-col-span-8{grid-column:span 8/span 8}.sm\:bui-col-span-9{grid-column:span 9/span 9}.sm\:bui-col-span-10{grid-column:span 10/span 10}.sm\:bui-col-span-11{grid-column:span 11/span 11}.sm\:bui-col-span-12{grid-column:span 12/span 12}.sm\:bui-col-span-auto{grid-column:span auto/span auto}.sm\:bui-col-start-1{grid-column-start:1}.sm\:bui-col-start-2{grid-column-start:2}.sm\:bui-col-start-3{grid-column-start:3}.sm\:bui-col-start-4{grid-column-start:4}.sm\:bui-col-start-5{grid-column-start:5}.sm\:bui-col-start-6{grid-column-start:6}.sm\:bui-col-start-7{grid-column-start:7}.sm\:bui-col-start-8{grid-column-start:8}.sm\:bui-col-start-9{grid-column-start:9}.sm\:bui-col-start-10{grid-column-start:10}.sm\:bui-col-start-11{grid-column-start:11}.sm\:bui-col-start-12{grid-column-start:12}.sm\:bui-col-start-13{grid-column-start:13}.sm\:bui-col-start-auto{grid-column-start:auto}.sm\:bui-col-end-1{grid-column-end:1}.sm\:bui-col-end-2{grid-column-end:2}.sm\:bui-col-end-3{grid-column-end:3}.sm\:bui-col-end-4{grid-column-end:4}.sm\:bui-col-end-5{grid-column-end:5}.sm\:bui-col-end-6{grid-column-end:6}.sm\:bui-col-end-7{grid-column-end:7}.sm\:bui-col-end-8{grid-column-end:8}.sm\:bui-col-end-9{grid-column-end:9}.sm\:bui-col-end-10{grid-column-end:10}.sm\:bui-col-end-11{grid-column-end:11}.sm\:bui-col-end-12{grid-column-end:12}.sm\:bui-col-end-13{grid-column-end:13}.sm\:bui-col-end-auto{grid-column-end:auto}.sm\:bui-row-span-1{grid-row:span 1/span 1}.sm\:bui-row-span-2{grid-row:span 2/span 2}.sm\:bui-row-span-3{grid-row:span 3/span 3}.sm\:bui-row-span-4{grid-row:span 4/span 4}.sm\:bui-row-span-5{grid-row:span 5/span 5}.sm\:bui-row-span-6{grid-row:span 6/span 6}.sm\:bui-row-span-7{grid-row:span 7/span 7}.sm\:bui-row-span-8{grid-row:span 8/span 8}.sm\:bui-row-span-9{grid-row:span 9/span 9}.sm\:bui-row-span-10{grid-row:span 10/span 10}.sm\:bui-row-span-11{grid-row:span 11/span 11}.sm\:bui-row-span-12{grid-row:span 12/span 12}.sm\:bui-row-span-auto{grid-row:span auto/span auto}}@media (width>=1024px){.md\:bui-columns-1{grid-template-columns:repeat(1,minmax(0,1fr))}.md\:bui-columns-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:bui-columns-3{grid-template-columns:repeat(3,minmax(0,1fr))}.md\:bui-columns-4{grid-template-columns:repeat(4,minmax(0,1fr))}.md\:bui-columns-5{grid-template-columns:repeat(5,minmax(0,1fr))}.md\:bui-columns-6{grid-template-columns:repeat(6,minmax(0,1fr))}.md\:bui-columns-7{grid-template-columns:repeat(7,minmax(0,1fr))}.md\:bui-columns-8{grid-template-columns:repeat(8,minmax(0,1fr))}.md\:bui-columns-9{grid-template-columns:repeat(9,minmax(0,1fr))}.md\:bui-columns-10{grid-template-columns:repeat(10,minmax(0,1fr))}.md\:bui-columns-11{grid-template-columns:repeat(11,minmax(0,1fr))}.md\:bui-columns-12{grid-template-columns:repeat(12,minmax(0,1fr))}.md\:bui-columns-auto{grid-template-columns:repeat(auto-fit,minmax(0,1fr))}.md\:bui-col-span-1{grid-column:span 1/span 1}.md\:bui-col-span-2{grid-column:span 2/span 2}.md\:bui-col-span-3{grid-column:span 3/span 3}.md\:bui-col-span-4{grid-column:span 4/span 4}.md\:bui-col-span-5{grid-column:span 5/span 5}.md\:bui-col-span-6{grid-column:span 6/span 6}.md\:bui-col-span-7{grid-column:span 7/span 7}.md\:bui-col-span-8{grid-column:span 8/span 8}.md\:bui-col-span-9{grid-column:span 9/span 9}.md\:bui-col-span-10{grid-column:span 10/span 10}.md\:bui-col-span-11{grid-column:span 11/span 11}.md\:bui-col-span-12{grid-column:span 12/span 12}.md\:bui-col-span-auto{grid-column:span auto/span auto}.md\:bui-col-start-1{grid-column-start:1}.md\:bui-col-start-2{grid-column-start:2}.md\:bui-col-start-3{grid-column-start:3}.md\:bui-col-start-4{grid-column-start:4}.md\:bui-col-start-5{grid-column-start:5}.md\:bui-col-start-6{grid-column-start:6}.md\:bui-col-start-7{grid-column-start:7}.md\:bui-col-start-8{grid-column-start:8}.md\:bui-col-start-9{grid-column-start:9}.md\:bui-col-start-10{grid-column-start:10}.md\:bui-col-start-11{grid-column-start:11}.md\:bui-col-start-12{grid-column-start:12}.md\:bui-col-start-13{grid-column-start:13}.md\:bui-col-start-auto{grid-column-start:auto}.md\:bui-col-end-1{grid-column-end:1}.md\:bui-col-end-2{grid-column-end:2}.md\:bui-col-end-3{grid-column-end:3}.md\:bui-col-end-4{grid-column-end:4}.md\:bui-col-end-5{grid-column-end:5}.md\:bui-col-end-6{grid-column-end:6}.md\:bui-col-end-7{grid-column-end:7}.md\:bui-col-end-8{grid-column-end:8}.md\:bui-col-end-9{grid-column-end:9}.md\:bui-col-end-10{grid-column-end:10}.md\:bui-col-end-11{grid-column-end:11}.md\:bui-col-end-12{grid-column-end:12}.md\:bui-col-end-13{grid-column-end:13}.md\:bui-col-end-auto{grid-column-end:auto}.md\:bui-row-span-1{grid-row:span 1/span 1}.md\:bui-row-span-2{grid-row:span 2/span 2}.md\:bui-row-span-3{grid-row:span 3/span 3}.md\:bui-row-span-4{grid-row:span 4/span 4}.md\:bui-row-span-5{grid-row:span 5/span 5}.md\:bui-row-span-6{grid-row:span 6/span 6}.md\:bui-row-span-7{grid-row:span 7/span 7}.md\:bui-row-span-8{grid-row:span 8/span 8}.md\:bui-row-span-9{grid-row:span 9/span 9}.md\:bui-row-span-10{grid-row:span 10/span 10}.md\:bui-row-span-11{grid-row:span 11/span 11}.md\:bui-row-span-12{grid-row:span 12/span 12}.md\:bui-row-span-auto{grid-row:span auto/span auto}}@media (width>=1280px){.lg\:bui-columns-1{grid-template-columns:repeat(1,minmax(0,1fr))}.lg\:bui-columns-2{grid-template-columns:repeat(2,minmax(0,1fr))}.lg\:bui-columns-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:bui-columns-4{grid-template-columns:repeat(4,minmax(0,1fr))}.lg\:bui-columns-5{grid-template-columns:repeat(5,minmax(0,1fr))}.lg\:bui-columns-6{grid-template-columns:repeat(6,minmax(0,1fr))}.lg\:bui-columns-7{grid-template-columns:repeat(7,minmax(0,1fr))}.lg\:bui-columns-8{grid-template-columns:repeat(8,minmax(0,1fr))}.lg\:bui-columns-9{grid-template-columns:repeat(9,minmax(0,1fr))}.lg\:bui-columns-10{grid-template-columns:repeat(10,minmax(0,1fr))}.lg\:bui-columns-11{grid-template-columns:repeat(11,minmax(0,1fr))}.lg\:bui-columns-12{grid-template-columns:repeat(12,minmax(0,1fr))}.lg\:bui-columns-auto{grid-template-columns:repeat(auto-fit,minmax(0,1fr))}.lg\:bui-col-span-1{grid-column:span 1/span 1}.lg\:bui-col-span-2{grid-column:span 2/span 2}.lg\:bui-col-span-3{grid-column:span 3/span 3}.lg\:bui-col-span-4{grid-column:span 4/span 4}.lg\:bui-col-span-5{grid-column:span 5/span 5}.lg\:bui-col-span-6{grid-column:span 6/span 6}.lg\:bui-col-span-7{grid-column:span 7/span 7}.lg\:bui-col-span-8{grid-column:span 8/span 8}.lg\:bui-col-span-9{grid-column:span 9/span 9}.lg\:bui-col-span-10{grid-column:span 10/span 10}.lg\:bui-col-span-11{grid-column:span 11/span 11}.lg\:bui-col-span-12{grid-column:span 12/span 12}.lg\:bui-col-span-auto{grid-column:span auto/span auto}.lg\:bui-col-start-1{grid-column-start:1}.lg\:bui-col-start-2{grid-column-start:2}.lg\:bui-col-start-3{grid-column-start:3}.lg\:bui-col-start-4{grid-column-start:4}.lg\:bui-col-start-5{grid-column-start:5}.lg\:bui-col-start-6{grid-column-start:6}.lg\:bui-col-start-7{grid-column-start:7}.lg\:bui-col-start-8{grid-column-start:8}.lg\:bui-col-start-9{grid-column-start:9}.lg\:bui-col-start-10{grid-column-start:10}.lg\:bui-col-start-11{grid-column-start:11}.lg\:bui-col-start-12{grid-column-start:12}.lg\:bui-col-start-13{grid-column-start:13}.lg\:bui-col-start-auto{grid-column-start:auto}.lg\:bui-col-end-1{grid-column-end:1}.lg\:bui-col-end-2{grid-column-end:2}.lg\:bui-col-end-3{grid-column-end:3}.lg\:bui-col-end-4{grid-column-end:4}.lg\:bui-col-end-5{grid-column-end:5}.lg\:bui-col-end-6{grid-column-end:6}.lg\:bui-col-end-7{grid-column-end:7}.lg\:bui-col-end-8{grid-column-end:8}.lg\:bui-col-end-9{grid-column-end:9}.lg\:bui-col-end-10{grid-column-end:10}.lg\:bui-col-end-11{grid-column-end:11}.lg\:bui-col-end-12{grid-column-end:12}.lg\:bui-col-end-13{grid-column-end:13}.lg\:bui-col-end-auto{grid-column-end:auto}.lg\:bui-row-span-1{grid-row:span 1/span 1}.lg\:bui-row-span-2{grid-row:span 2/span 2}.lg\:bui-row-span-3{grid-row:span 3/span 3}.lg\:bui-row-span-4{grid-row:span 4/span 4}.lg\:bui-row-span-5{grid-row:span 5/span 5}.lg\:bui-row-span-6{grid-row:span 6/span 6}.lg\:bui-row-span-7{grid-row:span 7/span 7}.lg\:bui-row-span-8{grid-row:span 8/span 8}.lg\:bui-row-span-9{grid-row:span 9/span 9}.lg\:bui-row-span-10{grid-row:span 10/span 10}.lg\:bui-row-span-11{grid-row:span 11/span 11}.lg\:bui-row-span-12{grid-row:span 12/span 12}.lg\:bui-row-span-auto{grid-row:span auto/span auto}}@media (width>=1536px){.xl\:bui-columns-1{grid-template-columns:repeat(1,minmax(0,1fr))}.xl\:bui-columns-2{grid-template-columns:repeat(2,minmax(0,1fr))}.xl\:bui-columns-3{grid-template-columns:repeat(3,minmax(0,1fr))}.xl\:bui-columns-4{grid-template-columns:repeat(4,minmax(0,1fr))}.xl\:bui-columns-5{grid-template-columns:repeat(5,minmax(0,1fr))}.xl\:bui-columns-6{grid-template-columns:repeat(6,minmax(0,1fr))}.xl\:bui-columns-7{grid-template-columns:repeat(7,minmax(0,1fr))}.xl\:bui-columns-8{grid-template-columns:repeat(8,minmax(0,1fr))}.xl\:bui-columns-9{grid-template-columns:repeat(9,minmax(0,1fr))}.xl\:bui-columns-10{grid-template-columns:repeat(10,minmax(0,1fr))}.xl\:bui-columns-11{grid-template-columns:repeat(11,minmax(0,1fr))}.xl\:bui-columns-12{grid-template-columns:repeat(12,minmax(0,1fr))}.xl\:bui-columns-auto{grid-template-columns:repeat(auto-fit,minmax(0,1fr))}.xl\:bui-col-span-1{grid-column:span 1/span 1}.xl\:bui-col-span-2{grid-column:span 2/span 2}.xl\:bui-col-span-3{grid-column:span 3/span 3}.xl\:bui-col-span-4{grid-column:span 4/span 4}.xl\:bui-col-span-5{grid-column:span 5/span 5}.xl\:bui-col-span-6{grid-column:span 6/span 6}.xl\:bui-col-span-7{grid-column:span 7/span 7}.xl\:bui-col-span-8{grid-column:span 8/span 8}.xl\:bui-col-span-9{grid-column:span 9/span 9}.xl\:bui-col-span-10{grid-column:span 10/span 10}.xl\:bui-col-span-11{grid-column:span 11/span 11}.xl\:bui-col-span-12{grid-column:span 12/span 12}.xl\:bui-col-span-auto{grid-column:span auto/span auto}.xl\:bui-col-start-1{grid-column-start:1}.xl\:bui-col-start-2{grid-column-start:2}.xl\:bui-col-start-3{grid-column-start:3}.xl\:bui-col-start-4{grid-column-start:4}.xl\:bui-col-start-5{grid-column-start:5}.xl\:bui-col-start-6{grid-column-start:6}.xl\:bui-col-start-7{grid-column-start:7}.xl\:bui-col-start-8{grid-column-start:8}.xl\:bui-col-start-9{grid-column-start:9}.xl\:bui-col-start-10{grid-column-start:10}.xl\:bui-col-start-11{grid-column-start:11}.xl\:bui-col-start-12{grid-column-start:12}.xl\:bui-col-start-13{grid-column-start:13}.xl\:bui-col-start-auto{grid-column-start:auto}.xl\:bui-col-end-1{grid-column-end:1}.xl\:bui-col-end-2{grid-column-end:2}.xl\:bui-col-end-3{grid-column-end:3}.xl\:bui-col-end-4{grid-column-end:4}.xl\:bui-col-end-5{grid-column-end:5}.xl\:bui-col-end-6{grid-column-end:6}.xl\:bui-col-end-7{grid-column-end:7}.xl\:bui-col-end-8{grid-column-end:8}.xl\:bui-col-end-9{grid-column-end:9}.xl\:bui-col-end-10{grid-column-end:10}.xl\:bui-col-end-11{grid-column-end:11}.xl\:bui-col-end-12{grid-column-end:12}.xl\:bui-col-end-13{grid-column-end:13}.xl\:bui-col-end-auto{grid-column-end:auto}.xl\:bui-row-span-1{grid-row:span 1/span 1}.xl\:bui-row-span-2{grid-row:span 2/span 2}.xl\:bui-row-span-3{grid-row:span 3/span 3}.xl\:bui-row-span-4{grid-row:span 4/span 4}.xl\:bui-row-span-5{grid-row:span 5/span 5}.xl\:bui-row-span-6{grid-row:span 6/span 6}.xl\:bui-row-span-7{grid-row:span 7/span 7}.xl\:bui-row-span-8{grid-row:span 8/span 8}.xl\:bui-row-span-9{grid-row:span 9/span 9}.xl\:bui-row-span-10{grid-row:span 10/span 10}.xl\:bui-row-span-11{grid-row:span 11/span 11}.xl\:bui-row-span-12{grid-row:span 12/span 12}.xl\:bui-row-span-auto{grid-row:span auto/span auto}}.bui-gap{gap:var(--gap)}.bui-gap-0\.5{gap:var(--bui-space-0_5)}.bui-gap-1{gap:var(--bui-space-1)}.bui-gap-1\.5{gap:var(--bui-space-1_5)}.bui-gap-2{gap:var(--bui-space-2)}.bui-gap-3{gap:var(--bui-space-3)}.bui-gap-4{gap:var(--bui-space-4)}.bui-gap-5{gap:var(--bui-space-5)}.bui-gap-6{gap:var(--bui-space-6)}.bui-gap-7{gap:var(--bui-space-7)}.bui-gap-8{gap:var(--bui-space-8)}.bui-gap-9{gap:var(--bui-space-9)}.bui-gap-10{gap:var(--bui-space-10)}.bui-gap-11{gap:var(--bui-space-11)}.bui-gap-12{gap:var(--bui-space-12)}.bui-gap-13{gap:var(--bui-space-13)}.bui-gap-14{gap:var(--bui-space-14)}@media (width>=640px){.xs\:bui-gap{gap:var(--gap-xs)}.xs\:bui-gap-0\.5{gap:var(--bui-space-0_5)}.xs\:bui-gap-1{gap:var(--bui-space-1)}.xs\:bui-gap-1\.5{gap:var(--bui-space-1_5)}.xs\:bui-gap-2{gap:var(--bui-space-2)}.xs\:bui-gap-3{gap:var(--bui-space-3)}.xs\:bui-gap-4{gap:var(--bui-space-4)}.xs\:bui-gap-5{gap:var(--bui-space-5)}.xs\:bui-gap-6{gap:var(--bui-space-6)}.xs\:bui-gap-7{gap:var(--bui-space-7)}.xs\:bui-gap-8{gap:var(--bui-space-8)}.xs\:bui-gap-9{gap:var(--bui-space-9)}.xs\:bui-gap-10{gap:var(--bui-space-10)}.xs\:bui-gap-11{gap:var(--bui-space-11)}.xs\:bui-gap-12{gap:var(--bui-space-12)}.xs\:bui-gap-13{gap:var(--bui-space-13)}.xs\:bui-gap-14{gap:var(--bui-space-14)}}@media (width>=768px){.sm\:bui-gap{gap:var(--gap-sm)}.sm\:bui-gap-0\.5{gap:var(--bui-space-0_5)}.sm\:bui-gap-1{gap:var(--bui-space-1)}.sm\:bui-gap-1\.5{gap:var(--bui-space-1_5)}.sm\:bui-gap-2{gap:var(--bui-space-2)}.sm\:bui-gap-3{gap:var(--bui-space-3)}.sm\:bui-gap-4{gap:var(--bui-space-4)}.sm\:bui-gap-5{gap:var(--bui-space-5)}.sm\:bui-gap-6{gap:var(--bui-space-6)}.sm\:bui-gap-7{gap:var(--bui-space-7)}.sm\:bui-gap-8{gap:var(--bui-space-8)}.sm\:bui-gap-9{gap:var(--bui-space-9)}.sm\:bui-gap-10{gap:var(--bui-space-10)}.sm\:bui-gap-11{gap:var(--bui-space-11)}.sm\:bui-gap-12{gap:var(--bui-space-12)}.sm\:bui-gap-13{gap:var(--bui-space-13)}.sm\:bui-gap-14{gap:var(--bui-space-14)}}@media (width>=1024px){.md\:bui-gap{gap:var(--gap-md)}.md\:bui-gap-0\.5{gap:var(--bui-space-0_5)}.md\:bui-gap-1{gap:var(--bui-space-1)}.md\:bui-gap-1\.5{gap:var(--bui-space-1_5)}.md\:bui-gap-2{gap:var(--bui-space-2)}.md\:bui-gap-3{gap:var(--bui-space-3)}.md\:bui-gap-4{gap:var(--bui-space-4)}.md\:bui-gap-5{gap:var(--bui-space-5)}.md\:bui-gap-6{gap:var(--bui-space-6)}.md\:bui-gap-7{gap:var(--bui-space-7)}.md\:bui-gap-8{gap:var(--bui-space-8)}.md\:bui-gap-9{gap:var(--bui-space-9)}.md\:bui-gap-10{gap:var(--bui-space-10)}.md\:bui-gap-11{gap:var(--bui-space-11)}.md\:bui-gap-12{gap:var(--bui-space-12)}.md\:bui-gap-13{gap:var(--bui-space-13)}.md\:bui-gap-14{gap:var(--bui-space-14)}}@media (width>=1280px){.lg\:bui-gap{gap:var(--gap-lg)}.lg\:bui-gap-0\.5{gap:var(--bui-space-0_5)}.lg\:bui-gap-1{gap:var(--bui-space-1)}.lg\:bui-gap-1\.5{gap:var(--bui-space-1_5)}.lg\:bui-gap-2{gap:var(--bui-space-2)}.lg\:bui-gap-3{gap:var(--bui-space-3)}.lg\:bui-gap-4{gap:var(--bui-space-4)}.lg\:bui-gap-5{gap:var(--bui-space-5)}.lg\:bui-gap-6{gap:var(--bui-space-6)}.lg\:bui-gap-7{gap:var(--bui-space-7)}.lg\:bui-gap-8{gap:var(--bui-space-8)}.lg\:bui-gap-9{gap:var(--bui-space-9)}.lg\:bui-gap-10{gap:var(--bui-space-10)}.lg\:bui-gap-11{gap:var(--bui-space-11)}.lg\:bui-gap-12{gap:var(--bui-space-12)}.lg\:bui-gap-13{gap:var(--bui-space-13)}.lg\:bui-gap-14{gap:var(--bui-space-14)}}@media (width>=1536px){.xl\:bui-gap{gap:var(--gap-xl)}.xl\:bui-gap-0\.5{gap:var(--bui-space-0_5)}.xl\:bui-gap-1{gap:var(--bui-space-1)}.xl\:bui-gap-1\.5{gap:var(--bui-space-1_5)}.xl\:bui-gap-2{gap:var(--bui-space-2)}.xl\:bui-gap-3{gap:var(--bui-space-3)}.xl\:bui-gap-4{gap:var(--bui-space-4)}.xl\:bui-gap-5{gap:var(--bui-space-5)}.xl\:bui-gap-6{gap:var(--bui-space-6)}.xl\:bui-gap-7{gap:var(--bui-space-7)}.xl\:bui-gap-8{gap:var(--bui-space-8)}.xl\:bui-gap-9{gap:var(--bui-space-9)}.xl\:bui-gap-10{gap:var(--bui-space-10)}.xl\:bui-gap-11{gap:var(--bui-space-11)}.xl\:bui-gap-12{gap:var(--bui-space-12)}.xl\:bui-gap-13{gap:var(--bui-space-13)}.xl\:bui-gap-14{gap:var(--bui-space-14)}}.bui-align-start{align-items:start}.bui-align-center{align-items:center}.bui-align-end{align-items:end}.bui-align-stretch{align-items:stretch}.bui-jc-start{justify-content:start}.bui-jc-center{justify-content:center}.bui-jc-end{justify-content:end}.bui-jc-between{justify-content:space-between}.bui-fd-row{flex-direction:row}.bui-fd-column{flex-direction:column}.bui-fd-row-reverse{flex-direction:row-reverse}.bui-fd-column-reverse{flex-direction:column-reverse}@media (width>=640px){.xs\:bui-align-start{align-items:start}.xs\:bui-align-center{align-items:center}.xs\:bui-align-end{align-items:end}.xs\:bui-align-stretch{align-items:stretch}.xs\:bui-jc-start{justify-content:start}.xs\:bui-jc-center{justify-content:center}.xs\:bui-jc-end{justify-content:end}.xs\:bui-jc-between{justify-content:space-between}.xs\:bui-fd-row{flex-direction:row}.xs\:bui-fd-column{flex-direction:column}.xs\:bui-fd-row-reverse{flex-direction:row-reverse}.xs\:bui-fd-column-reverse{flex-direction:column-reverse}}@media (width>=768px){.sm\:bui-align-start{align-items:start}.sm\:bui-align-center{align-items:center}.sm\:bui-align-end{align-items:end}.sm\:bui-align-stretch{align-items:stretch}.sm\:bui-jc-start{justify-content:start}.sm\:bui-jc-center{justify-content:center}.sm\:bui-jc-end{justify-content:end}.sm\:bui-jc-between{justify-content:space-between}.sm\:bui-fd-row{flex-direction:row}.sm\:bui-fd-column{flex-direction:column}.sm\:bui-fd-row-reverse{flex-direction:row-reverse}.sm\:bui-fd-column-reverse{flex-direction:column-reverse}}@media (width>=1024px){.md\:bui-align-start{align-items:start}.md\:bui-align-center{align-items:center}.md\:bui-align-end{align-items:end}.md\:bui-align-stretch{align-items:stretch}.md\:bui-jc-start{justify-content:start}.md\:bui-jc-center{justify-content:center}.md\:bui-jc-end{justify-content:end}.md\:bui-jc-between{justify-content:space-between}.md\:bui-fd-row{flex-direction:row}.md\:bui-fd-column{flex-direction:column}.md\:bui-fd-row-reverse{flex-direction:row-reverse}.md\:bui-fd-column-reverse{flex-direction:column-reverse}}@media (width>=1280px){.lg\:bui-align-start{align-items:start}.lg\:bui-align-center{align-items:center}.lg\:bui-align-end{align-items:end}.lg\:bui-align-stretch{align-items:stretch}.lg\:bui-jc-start{justify-content:start}.lg\:bui-jc-center{justify-content:center}.lg\:bui-jc-end{justify-content:end}.lg\:bui-jc-between{justify-content:space-between}.lg\:bui-fd-row{flex-direction:row}.lg\:bui-fd-column{flex-direction:column}.lg\:bui-fd-row-reverse{flex-direction:row-reverse}.lg\:bui-fd-column-reverse{flex-direction:column-reverse}}@media (width>=1536px){.xl\:bui-align-start{align-items:start}.xl\:bui-align-center{align-items:center}.xl\:bui-align-end{align-items:end}.xl\:bui-align-stretch{align-items:stretch}.xl\:bui-jc-start{justify-content:start}.xl\:bui-jc-center{justify-content:center}.xl\:bui-jc-end{justify-content:end}.xl\:bui-jc-between{justify-content:space-between}.xl\:bui-fd-row{flex-direction:row}.xl\:bui-fd-column{flex-direction:column}.xl\:bui-fd-row-reverse{flex-direction:row-reverse}.xl\:bui-fd-column-reverse{flex-direction:column-reverse}}:where(a){color:inherit;text-decoration:none}@keyframes pulse{50%{opacity:.5}}:root{--bui-font-regular:system-ui;--bui-font-monospace:ui-monospace,"Menlo","Monaco","Consolas","Liberation Mono","Courier New",monospace;--bui-font-weight-regular:400;--bui-font-weight-bold:600;--bui-font-size-1:.625rem;--bui-font-size-2:.75rem;--bui-font-size-3:.875rem;--bui-font-size-4:1rem;--bui-font-size-5:1.25rem;--bui-font-size-6:1.5rem;--bui-font-size-7:2rem;--bui-font-size-8:3rem;--bui-font-size-9:4rem;--bui-font-size-10:5.75rem;--bui-space:.25rem;--bui-space-0_5:calc(var(--bui-space)*.5);--bui-space-1:var(--bui-space);--bui-space-1_5:calc(var(--bui-space)*1.5);--bui-space-2:calc(var(--bui-space)*2);--bui-space-3:calc(var(--bui-space)*3);--bui-space-4:calc(var(--bui-space)*4);--bui-space-5:calc(var(--bui-space)*5);--bui-space-6:calc(var(--bui-space)*6);--bui-space-7:calc(var(--bui-space)*7);--bui-space-8:calc(var(--bui-space)*8);--bui-space-9:calc(var(--bui-space)*9);--bui-space-10:calc(var(--bui-space)*10);--bui-space-11:calc(var(--bui-space)*11);--bui-space-12:calc(var(--bui-space)*12);--bui-space-13:calc(var(--bui-space)*13);--bui-space-14:calc(var(--bui-space)*14);--bui-radius-1:calc(.125rem);--bui-radius-2:calc(.25rem);--bui-radius-3:calc(.5rem);--bui-radius-4:calc(.75rem);--bui-radius-5:calc(1rem);--bui-radius-6:calc(1.25rem);--bui-radius-full:9999px;--bui-black:#000;--bui-white:#fff;--bui-gray-1:#f8f8f8;--bui-gray-2:#ececec;--bui-gray-3:#d9d9d9;--bui-gray-4:#c1c1c1;--bui-gray-5:#9e9e9e;--bui-gray-6:#8c8c8c;--bui-gray-7:#757575;--bui-gray-8:#595959;--bui-bg:var(--bui-gray-1);--bui-bg-surface-1:var(--bui-white);--bui-bg-surface-2:var(--bui-gray-2);--bui-bg-solid:#1f5493;--bui-bg-solid-hover:#163a66;--bui-bg-solid-pressed:#0f2b4e;--bui-bg-solid-disabled:#ebebeb;--bui-bg-tint:transparent;--bui-bg-tint-hover:#1f549366;--bui-bg-tint-pressed:#1f549399;--bui-bg-tint-disabled:#ebebeb;--bui-bg-danger:#feebe7;--bui-bg-warning:#fff2b2;--bui-bg-success:#e6f6eb;--bui-fg-primary:var(--bui-black);--bui-fg-secondary:var(--bui-gray-7);--bui-fg-link:#1f5493;--bui-fg-link-hover:#1f2d5c;--bui-fg-disabled:#9e9e9e;--bui-fg-solid:var(--bui-white);--bui-fg-solid-disabled:#9c9c9c;--bui-fg-tint:#1f5493;--bui-fg-tint-disabled:var(--bui-gray-5);--bui-fg-danger:#e22b2b;--bui-fg-warning:#e36d05;--bui-fg-success:#1db954;--bui-border:#0000001a;--bui-border-hover:#0003;--bui-border-pressed:#0006;--bui-border-disabled:#0000001a;--bui-border-danger:#f87a7a;--bui-border-warning:#e36d05;--bui-border-success:#53db83;--bui-ring:#1f5493;--bui-scrollbar:#a0a0a03b;--bui-scrollbar-thumb:#a0a0a0;--bui-animate-pulse:pulse 2s cubic-bezier(.4,0,.6,1)infinite}[data-theme-mode=dark]{--bui-gray-1:#191919;--bui-gray-2:#242424;--bui-gray-3:#373737;--bui-gray-4:#464646;--bui-gray-5:#575757;--bui-gray-6:#7b7b7b;--bui-gray-7:#9e9e9e;--bui-gray-8:#b4b4b4;--bui-bg:#333;--bui-bg-surface-1:#424242;--bui-bg-surface-2:var(--bui-gray-2);--bui-bg-solid:#9cc9ff;--bui-bg-solid-hover:#83b9fd;--bui-bg-solid-pressed:#83b9fd;--bui-bg-solid-disabled:#222;--bui-bg-tint:transparent;--bui-bg-tint-hover:#9cc9ff1f;--bui-bg-tint-pressed:#9cc9ff29;--bui-bg-tint-disabled:transparent;--bui-bg-danger:#3b1219;--bui-bg-warning:#302008;--bui-bg-success:#132d21;--bui-fg-primary:var(--bui-white);--bui-fg-secondary:var(--bui-gray-7);--bui-fg-link:#9cc9ff;--bui-fg-link-hover:#7eb5f7;--bui-fg-disabled:var(--bui-gray-7);--bui-fg-solid:#101821;--bui-fg-solid-disabled:var(--bui-gray-5);--bui-fg-tint:#9cc9ff;--bui-fg-tint-disabled:var(--bui-gray-5);--bui-fg-danger:#e22b2b;--bui-fg-warning:#e36d05;--bui-fg-success:#1db954;--bui-border:#ffffff1f;--bui-border-hover:#fff6;--bui-border-pressed:#ffffff80;--bui-border-disabled:#fff3;--bui-border-danger:#f87a7a;--bui-border-warning:#e36d05;--bui-border-success:#53db83;--bui-ring:#1f5493;--bui-scrollbar:#3636363a;--bui-scrollbar-thumb:#575757}.bui-AvatarRoot{vertical-align:middle;user-select:none;color:var(--bui-fg-primary);background-color:var(--bui-bg-surface-2);border-radius:100%;justify-content:center;align-items:center;width:2rem;height:2rem;font-size:1rem;font-weight:500;line-height:1;display:inline-flex;overflow:hidden}.bui-AvatarRoot[data-size=small]{width:1.5rem;height:1.5rem}.bui-AvatarRoot[data-size=medium]{width:2rem;height:2rem}.bui-AvatarRoot[data-size=large]{width:3rem;height:3rem}.bui-AvatarImage{object-fit:cover;width:100%;height:100%}.bui-AvatarFallback{width:100%;height:100%;font-size:var(--bui-font-size-3);font-weight:var(--bui-font-weight-regular);box-shadow:inset 0 0 0 1px var(--bui-border);border-radius:var(--bui-radius-full);justify-content:center;align-items:center;display:flex}.bui-Box{font-family:var(--bui-font-regular);font-weight:var(--bui-font-weight-regular);color:var(--bui-fg-primary)}.bui-Button{user-select:none;font-family:var(--bui-font-regular);font-weight:var(--bui-font-weight-bold);cursor:pointer;border-radius:var(--bui-radius-2);justify-content:center;align-items:center;gap:var(--bui-space-1_5);border:none;flex-shrink:0;padding:0;display:inline-flex;&[data-disabled=true]{cursor:not-allowed}}.bui-Button[data-variant=primary]{background-color:var(--bui-bg-solid);color:var(--bui-fg-solid);&:hover{background-color:var(--bui-bg-solid-hover);transition:background-color .15s}&:active{background-color:var(--bui-bg-solid-pressed)}&:focus-visible{outline:2px solid var(--bui-ring);outline-offset:2px}&[data-disabled=true]{background-color:var(--bui-bg-solid-disabled);color:var(--bui-fg-solid-disabled)}}.bui-Button[data-variant=secondary]{background-color:var(--bui-bg-surface-1);box-shadow:inset 0 0 0 1px var(--bui-border);color:var(--bui-fg-primary);&:hover{box-shadow:inset 0 0 0 1px var(--bui-border-hover);transition:box-shadow .15s}&:active{box-shadow:inset 0 0 0 1px var(--bui-border-pressed)}&:focus-visible{box-shadow:inset 0 0 0 2px var(--bui-ring);outline:none;transition:none}&[data-disabled=true]{box-shadow:inset 0 0 0 1px var(--bui-border-disabled);color:var(--bui-fg-disabled)}}.bui-Button[data-variant=tertiary]{color:var(--bui-fg-primary);background-color:#0000;&:hover{background-color:var(--bui-bg-surface-1);transition:background-color .2s}&:active{background-color:var(--bui-bg-surface-2)}&:focus-visible{box-shadow:inset 0 0 0 2px var(--bui-ring);outline:none;transition:none}&[data-disabled=true]{color:var(--bui-fg-disabled);background-color:#0000}}.bui-Button[data-size=medium]{font-size:var(--bui-font-size-4);padding:0 var(--bui-space-3);height:2.5rem}.bui-Button[data-size=small]{font-size:var(--bui-font-size-3);padding:0 var(--bui-space-2);height:2rem}.bui-Button[data-size=small] svg{width:1rem;height:1rem}.bui-Button[data-size=medium] svg{width:1.25rem;height:1.25rem}.bui-ButtonIcon{justify-content:center;align-items:center}.bui-ButtonIcon[data-size=small]{width:2rem;padding:0}.bui-ButtonIcon[data-size=medium]{width:2.5rem;padding:0}.bui-Card{gap:var(--bui-space-3);background-color:var(--bui-bg-surface-1);border-radius:var(--bui-radius-3);padding-block:var(--bui-space-3);color:var(--bui-fg-primary);border:1px solid var(--bui-border);flex-direction:column;width:100%;min-height:0;display:flex;overflow:hidden}.bui-CardBody{flex:1;min-height:0;overflow:auto}.bui-CardHeader,.bui-CardFooter{padding-inline:var(--bui-space-3)}.bui-CheckboxRoot{width:1rem;height:1rem;box-shadow:inset 0 0 0 1px var(--bui-border);cursor:pointer;background-color:var(--bui-bg-surface-1);border:none;border-radius:2px;flex-shrink:0;justify-content:center;align-items:center;padding:0;transition:background-color .2s ease-in-out;display:flex}.bui-CheckboxRoot:focus-visible{outline:2px solid var(--bui-ring);outline-offset:2px;transition:none}.bui-CheckboxRoot[data-checked]{background-color:var(--bui-bg-solid);box-shadow:none;color:var(--bui-fg-solid)}.bui-CheckboxLabel{align-items:center;gap:var(--bui-space-2);font-size:var(--bui-font-size-3);font-family:var(--bui-font-regular);font-weight:var(--bui-font-weight-regular);color:var(--bui-fg-primary);user-select:none;flex-direction:row;display:flex;&:hover{& .bui-CheckboxRoot:not([data-checked]){box-shadow:inset 0 0 0 1px var(--bui-border-hover)}}}.bui-CheckboxIndicator{color:var(--bui-fg-solid);justify-content:center;align-items:center;display:flex}.bui-CollapsiblePanel{height:var(--collapsible-panel-height);transition:all .15s ease-out;display:flex;overflow:hidden;&[data-starting-style],&[data-ending-style]{height:0}}.bui-Container{max-width:120rem;padding-inline:var(--bui-space-4);margin-inline:auto;transition:padding .2s ease-in-out}@media (width>=640px){.bui-Container{padding-inline:var(--bui-space-5)}}.bui-FieldError{color:var(--bui-fg-danger);font-size:var(--bui-font-size-2);font-weight:var(--bui-font-weight-regular);margin-top:var(--bui-space-2);display:inline-block}.bui-FieldLabelWrapper{margin-bottom:var(--bui-space-3);gap:var(--bui-space-1);flex-direction:column;display:flex}.bui-FieldLabel{color:var(--bui-fg-primary);cursor:pointer;font-weight:var(--bui-font-weight-regular);font-size:var(--bui-font-size-2);margin-right:auto}.bui-FieldSecondaryLabel{color:var(--bui-fg-secondary);font-weight:var(--bui-font-weight-regular);margin-left:var(--bui-space-1)}.bui-FieldDescription{font-weight:var(--bui-font-weight-regular);font-size:var(--bui-font-size-2);color:var(--bui-fg-secondary);margin:0}.bui-Flex{min-width:0;display:flex}.bui-Grid{display:grid}.bui-HeaderToolbar{margin-bottom:var(--bui-space-6);&:before{content:"";background-color:var(--bui-bg);z-index:0;height:16px;position:absolute;top:0;left:0;right:0}&[data-has-tabs=true]{margin-bottom:0}}.bui-HeaderToolbarWrapper{z-index:1;background-color:var(--bui-bg-surface-1);padding-inline:var(--bui-space-5);border-bottom:1px solid var(--bui-border);color:var(--bui-fg-primary);flex-direction:row;justify-content:space-between;align-items:center;height:52px;display:flex;position:relative}.bui-HeaderToolbarContent{align-items:center;gap:var(--bui-space-2);flex-direction:row;display:flex}.bui-HeaderToolbarName{align-items:center;gap:var(--bui-space-2);font-size:var(--bui-font-size-3);font-weight:var(--bui-font-weight-regular);flex-direction:row;flex-shrink:0;display:flex}.bui-HeaderToolbarIcon{width:16px;height:16px;color:var(--bui-fg-primary);& svg{width:100%;height:100%}}.bui-HeaderToolbarControls{right:var(--bui-space-5);align-items:center;gap:var(--bui-space-2);flex-direction:row;display:flex;position:absolute;top:50%;transform:translateY(-50%)}.bui-HeaderTabsWrapper{margin-bottom:var(--bui-space-4);padding-inline:var(--bui-space-3);border-bottom:1px solid var(--bui-border);background-color:var(--bui-bg-surface-1)}.bui-HeaderPage{gap:var(--bui-space-1);margin-top:var(--bui-space-6);margin-bottom:var(--bui-space-6);flex-direction:column;display:flex}.bui-HeaderPageContent{flex-direction:row;justify-content:space-between;display:flex}.bui-HeaderPageTabsWrapper{margin-left:-8px}.bui-HeaderPageControls,.bui-HeaderPageBreadcrumbs{align-items:center;gap:var(--bui-space-2);flex-direction:row;display:flex}.bui-Icon{width:1rem;height:1rem}.bui-Link{font-family:var(--bui-font-regular);cursor:pointer;margin:0;padding:0;text-decoration-line:none;display:inline-block;&:hover{text-underline-offset:calc(.025em + 2px);text-decoration-line:underline;text-decoration-style:solid;text-decoration-thickness:min(2px,max(1px,.05em));text-decoration-color:color-mix(in srgb,currentColor 30%,transparent)}}.bui-MenuPopover{border:1px solid var(--bui-border);border-radius:var(--bui-radius-2);background:var(--bui-bg-surface-1);color:var(--bui-fg-primary);outline:none;flex-direction:column;min-height:0;transition:transform .2s,opacity .2s;display:flex;overflow:hidden;&[data-entering],&[data-exiting]{transform:var(--origin);opacity:0}&[data-placement=top]{--origin:translateY(8px)}&[data-placement=bottom]{--origin:translateY(-8px)}&[data-placement=right]{--origin:translateX(-8px)}&[data-placement=left]{--origin:translateX(8px)}}.bui-MenuContent{max-height:inherit;box-sizing:border-box;padding:var(--bui-space-1);outline:none;min-width:150px;overflow:auto}.bui-MenuPopover .bui-ScrollAreaRoot{flex-direction:column;flex:1;height:100%;min-height:0;display:flex}.bui-MenuPopover .bui-ScrollAreaScrollbar{margin-inline:var(--bui-space-1_5)}.bui-MenuItem{height:2rem;padding-inline:var(--bui-space-2);border-radius:var(--bui-radius-2);cursor:default;color:var(--bui-fg-primary);font-size:var(--bui-font-size-3);justify-content:space-between;align-items:center;gap:var(--bui-space-6);outline:none;display:flex;&[data-focused],&[data-open]{background:var(--bui-bg-surface-2);color:var(--bui-fg-primary)}&[data-color=danger]{color:var(--bui-fg-danger)}&[data-color=danger][data-focused]{background:var(--bui-bg-danger);color:var(--bui-fg-danger)}&[data-has-submenu]{&>.bui-MenuItemArrow{display:block}}}.bui-MenuItemListBox{height:2rem;padding-inline:var(--bui-space-2);border-radius:var(--bui-radius-2);cursor:default;color:var(--bui-fg-primary);font-size:var(--bui-font-size-3);justify-content:space-between;align-items:center;gap:var(--bui-space-6);outline:none;display:flex;&:hover{background:var(--bui-bg-surface-2);color:var(--bui-fg-primary)}&[data-selected] .bui-MenuItemListBoxCheck{&>svg{opacity:1;color:var(--bui-fg-primary)}}}.bui-MenuItemListBoxCheck{justify-content:center;align-items:center;width:1rem;height:1rem;display:flex;&>svg{opacity:0;width:1rem;height:1rem}}.bui-MenuItemContent{align-items:center;gap:var(--bui-space-2);display:flex;&>svg{width:1rem;height:1rem}}.bui-MenuItemArrow{width:1rem;height:1rem;display:none;&>svg{width:1rem;height:1rem}}.bui-MenuSection{&:first-child .bui-MenuSectionHeader{padding-top:0}}.bui-MenuSectionHeader{height:2rem;padding-top:var(--bui-space-3);padding-left:var(--bui-space-2);color:var(--bui-fg-primary);font-size:var(--bui-font-size-1);letter-spacing:.05rem;text-transform:uppercase;align-items:center;font-weight:700;display:flex}.bui-MenuSeparator{background:var(--bui-border);height:1px;margin-inline:var(--bui-space-1_5);margin-block:var(--bui-space-1)}.bui-MenuSearchField{font-family:var(--bui-font-regular);flex-shrink:0;width:100%;position:relative;&[data-empty]{& .bui-MenuSearchFieldClear{display:none}}}.bui-MenuSearchFieldInput{padding:0 var(--bui-space-3);border:none;border-bottom:1px solid var(--bui-border);background-color:var(--bui-bg-surface-1);font-size:var(--bui-font-size-3);font-family:var(--bui-font-regular);font-weight:var(--bui-font-weight-regular);color:var(--bui-fg-primary);width:100%;height:2rem;cursor:inherit;outline:none;align-items:center;display:flex;&::-webkit-search-cancel-button,&::-webkit-search-decoration{-webkit-appearance:none}}.bui-MenuSearchFieldClear{right:var(--bui-space-2);cursor:pointer;color:var(--bui-fg-secondary);background-color:#0000;border:none;justify-content:center;align-items:center;margin:0;padding:0;transition:color .2s ease-in-out;display:flex;position:absolute;top:0;bottom:0;&>svg{width:1rem;height:1rem}}.bui-MenuEmptyState{padding:var(--bui-space-1);color:var(--bui-fg-secondary);font-size:var(--bui-font-size-3);font-family:var(--bui-font-regular);font-weight:var(--bui-font-weight-regular)}.bui-Popover{background-color:var(--bui-bg-surface-1);border:1px solid var(--bui-border);border-radius:var(--bui-radius-3);padding-block:var(--bui-space-1);margin-right:12px;overflow:scroll;box-shadow:0 4px 12px #0000001a}.bui-RadioGroup{color:var(--bui-fg-primary);flex-direction:column;display:flex}.bui-RadioGroup[data-orientation=horizontal] .bui-RadioGroupContent{gap:var(--bui-space-4);flex-direction:row}.bui-RadioGroupContent{gap:var(--bui-space-2);flex-direction:column;display:flex}.bui-Radio{align-items:center;gap:var(--bui-space-2);font-size:var(--bui-font-size-2);color:var(--bui-fg-primary);forced-color-adjust:none;display:flex;position:relative;&:before{content:"";box-sizing:border-box;border:.125rem solid var(--bui-border);background:var(--bui-gray-1);border-radius:var(--bui-radius-full);width:1rem;height:1rem;transition:all .2s;display:block}&[data-pressed]:before{border-color:var(--bui-border)}&[data-selected]{&:before{border-color:var(--bui-bg-solid);border-width:.25rem}&[data-pressed]:before{border-color:var(--bui-bg-solid)}}&[data-focus-visible]:before{outline:2px solid var(--bui-ring);outline-offset:2px}&[data-disabled]{cursor:not-allowed;color:var(--bui-fg-disabled);&:before{border-color:var(--bui-border-disabled);background:var(--bui-bg-disabled)}&[data-selected]:before{border-color:var(--bui-border-disabled)}}&[data-invalid]:before,&[data-invalid][data-selected]:before{border-color:var(--bui-border-danger)}&[data-disabled][data-invalid]{color:var(--bui-fg-disabled);&:before{border-color:var(--bui-border-disabled);background:var(--bui-bg-disabled)}&[data-selected]:before{border-color:var(--bui-border-disabled)}}}.bui-Table{caption-side:bottom;border-collapse:collapse;width:100%}.bui-TableHeader{border-bottom:1px solid var(--bui-border);transition:color .2s ease-in-out}.bui-TableHead{text-align:left;padding:var(--bui-space-3);font-size:var(--bui-font-size-3);color:var(--bui-fg-primary)}.bui-TableHeadSortButton{cursor:pointer;user-select:none;align-items:center;gap:var(--bui-space-1);display:inline-flex;&:hover svg{opacity:.5}& svg{opacity:0;transition:opacity .1s ease-in-out,transform .1s ease-in-out}&[data-sort-order=asc] svg{opacity:1;transform:rotate(0)}&[data-sort-order=desc] svg{opacity:1;transform:rotate(180deg)}}.bui-TableBody{color:var(--bui-fg-primary)}.bui-TableRow{border-bottom:1px solid var(--bui-border);transition:color .2s ease-in-out;&[data-react-aria-pressable=true]{cursor:pointer}}.bui-TableBody .bui-TableRow:hover{background-color:var(--bui-gray-2)}.bui-TableCell{padding:var(--bui-space-3);font-size:var(--bui-font-size-3);padding:var(--bui-space-3);font-size:var(--bui-font-size-3)}.bui-TableCellContentWrapper{align-items:center;gap:var(--bui-space-2);flex-direction:row;display:inline-flex}.bui-TableCellIcon,.bui-TableCellIcon svg{color:var(--bui-fg-primary);align-items:center;display:inline-flex}.bui-TableCellContent{gap:var(--bui-space-0_5);flex-direction:column;display:flex}.bui-TableCellProfile{gap:var(--bui-space-2);flex-direction:row;align-items:center;display:flex}.bui-TableCellProfileAvatar{vertical-align:middle;user-select:none;color:var(--bui-fg-primary);background-color:var(--bui-bg-surface-2);border-radius:100%;justify-content:center;align-items:center;width:1.25rem;height:1.25rem;font-size:1rem;font-weight:500;line-height:1;display:inline-flex;overflow:hidden}.bui-TableCellProfileAvatarImage{object-fit:cover;width:100%;height:100%}.bui-TableCellProfileAvatarFallback{width:100%;height:100%;font-size:var(--bui-font-size-2);font-weight:var(--bui-font-weight-regular);box-shadow:inset 0 0 0 1px var(--bui-border);border-radius:var(--bui-radius-full);justify-content:center;align-items:center;display:flex}.bui-DataTablePagination{padding-top:var(--bui-space-5);justify-content:space-between;align-items:center;display:flex}.bui-DataTablePagination--left{justify-content:space-between;align-items:center;display:flex}.bui-DataTablePagination--right{justify-content:space-between;align-items:center;gap:var(--bui-space-2);display:flex}.bui-DataTablePagination--select{min-width:10.5rem}.bui-Tabs{--active-tab-left:0px;--active-tab-right:0px;--active-tab-top:0px;--active-tab-bottom:0px;--active-tab-width:0px;--active-tab-height:0px;--active-transition-duration:0s;--hovered-tab-left:0px;--hovered-tab-right:0px;--hovered-tab-top:0px;--hovered-tab-bottom:0px;--hovered-tab-width:0px;--hovered-tab-height:0px;--hovered-tab-opacity:0;--hovered-transition-duration:0s}.bui-TabList{flex-direction:row;display:flex}.bui-TabListWrapper{position:relative}.bui-Tab{font-size:var(--bui-font-size-3);font-family:var(--bui-font-regular);font-weight:var(--bui-font-weight-regular);color:var(--bui-fg-secondary);cursor:pointer;z-index:2;height:36px;padding-inline:var(--bui-space-2);justify-content:center;align-items:center;display:flex;position:relative;&[data-selected=true]{color:var(--bui-fg-primary)}}.bui-TabActive{content:"";left:calc(var(--active-tab-left) + var(--bui-space-2));width:calc(var(--active-tab-width) - var(--bui-space-4));background-color:var(--bui-fg-primary);height:1px;transition:left var(--active-transition-duration)ease-out,opacity .15s ease-out,width var(--active-transition-duration)ease-out;opacity:1;border-radius:4px;position:absolute;bottom:-1px}.bui-TabHovered{content:"";left:var(--hovered-tab-left);top:calc(var(--hovered-tab-top) + 4px);width:var(--hovered-tab-width);height:calc(var(--hovered-tab-height) - 8px);background-color:var(--bui-gray-2);opacity:var(--hovered-tab-opacity);transition:left var(--hovered-transition-duration)ease-out,top var(--hovered-transition-duration)ease-out,width var(--hovered-transition-duration)ease-out,height var(--hovered-transition-duration)ease-out,opacity .15s ease-out;border-radius:4px;position:absolute}.bui-TabPanel{padding-inline:var(--bui-space-2);padding-top:var(--bui-space-4)}.bui-TagList{gap:var(--bui-space-2);flex-wrap:wrap;display:flex}.bui-Tag{color:var(--bui-fg-primary);background-color:var(--bui-gray-2);border-radius:var(--bui-radius-2);font-weight:var(--bui-font-weight-regular);justify-content:center;align-items:center;gap:var(--bui-space-1);box-sizing:border-box;transition-property:background-color,box-shadow,color;transition-duration:.2s;transition-timing-function:ease-in-out;display:flex}.bui-Tag[data-size=small]{height:26px;padding:0 var(--bui-space-2);font-size:var(--bui-font-size-1)}.bui-Tag[data-size=medium]{height:32px;padding:0 var(--bui-space-2);font-size:var(--bui-font-size-2)}.bui-Tag[data-hovered]{background-color:var(--bui-gray-3);cursor:pointer}.bui-Tag[data-focus-visible]{outline:2px solid var(--bui-ring);outline-offset:1px}.bui-Tag[data-selected]{box-shadow:inset 0 0 0 1px var(--bui-gray-8)}.bui-Tag[data-disabled]{color:var(--bui-fg-disabled);cursor:not-allowed}.bui-TagRemoveButton{cursor:pointer;color:var(--bui-fg-primary);background-color:#0000;border:none;width:1rem;height:1rem;margin:0;padding:0}.bui-TagIcon{justify-content:center;align-items:center;transition:color .2s ease-in-out;display:flex;& svg{width:1rem;height:1rem}}.bui-Text{font-family:var(--bui-font-regular);margin:0;padding:0}.bui-Text[data-variant=title-large]{font-size:var(--bui-font-size-8);line-height:140%}.bui-Text[data-variant=title-medium]{font-size:var(--bui-font-size-7);line-height:140%}.bui-Text[data-variant=title-small]{font-size:var(--bui-font-size-6);line-height:140%}.bui-Text[data-variant=title-x-small]{font-size:var(--bui-font-size-5);line-height:140%}.bui-Text[data-variant=body-large]{font-size:var(--bui-font-size-4);line-height:140%}.bui-Text[data-variant=body-medium]{font-size:var(--bui-font-size-3);line-height:140%}.bui-Text[data-variant=body-small]{font-size:var(--bui-font-size-2);line-height:140%}.bui-Text[data-variant=body-x-small]{font-size:var(--bui-font-size-1);line-height:140%}.bui-Text[data-weight=regular]{font-weight:var(--bui-font-weight-regular)}.bui-Text[data-weight=bold]{font-weight:var(--bui-font-weight-bold)}.bui-Text[data-color=primary]{color:var(--bui-fg-primary)}.bui-Text[data-color=secondary]{color:var(--bui-fg-secondary)}.bui-Text[data-color=danger]{color:var(--bui-fg-danger)}.bui-Text[data-color=warning]{color:var(--bui-fg-warning)}.bui-Text[data-color=success]{color:var(--bui-fg-success)}.bui-Text[data-truncate]{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.bui-Text[data-as=span],.bui-Text[data-as=label],.bui-Text[data-as=strong],.bui-Text[data-as=em],.bui-Text[data-as=small]{display:inline-block}.bui-TextField{font-family:var(--bui-font-regular);flex-direction:column;flex-shrink:0;width:100%;display:flex}.bui-InputWrapper{position:relative;&[data-size=small] .bui-Input{height:2rem}&[data-size=medium] .bui-Input{height:2.5rem}&[data-size=small] .bui-Input[data-icon]{padding-left:var(--bui-space-8)}&[data-size=medium] .bui-Input[data-icon]{padding-left:var(--bui-space-9)}}.bui-InputIcon{left:var(--bui-space-3);margin-right:var(--bui-space-1);color:var(--bui-fg-primary);pointer-events:none;flex-shrink:0;transition:left .2s ease-in-out;position:absolute;top:50%;transform:translateY(-50%);&[data-size=small],&[data-size=small] svg{width:1rem;height:1rem}&[data-size=medium],&[data-size=medium] svg{width:1.25rem;height:1.25rem}}.bui-Input{padding:0 var(--bui-space-3);border-radius:var(--bui-radius-2);border:1px solid var(--bui-border);background-color:var(--bui-bg-surface-1);font-size:var(--bui-font-size-3);font-family:var(--bui-font-regular);font-weight:var(--bui-font-weight-regular);color:var(--bui-fg-primary);width:100%;height:100%;cursor:inherit;align-items:center;transition:border-color .2s ease-in-out,outline-color .2s ease-in-out;display:flex;&::-webkit-search-cancel-button,&::-webkit-search-decoration{-webkit-appearance:none}&::placeholder{color:var(--bui-fg-secondary)}&[data-focused]{outline-color:var(--bui-border-pressed);outline-width:0}&[data-hovered]{border-color:var(--bui-border-hover)}&[data-focused]{border-color:var(--bui-border-pressed);outline-width:0}&[data-invalid]{border-color:var(--bui-fg-danger)}&[data-disabled]{opacity:.5;cursor:not-allowed;border:1px solid var(--bui-border-disabled)}}.bui-SearchField{&[data-empty]{& .bui-InputClear{display:none}}&[data-start-collapsed=true]{padding:0;transition:width .3s ease-in-out;&[data-collapsed=false]{cursor:pointer;&[data-size=medium]{width:2.5rem;height:2.5rem}&[data-size=small]{width:2rem;height:2rem}&[data-size=medium] .bui-Input{padding-left:0;&::placeholder{opacity:0}}&[data-size=small] .bui-Input{padding-left:0;&::placeholder{opacity:0}}&[data-size=small] .bui-InputIcon{left:var(--bui-space-2)}&[data-size=medium] .bui-InputIcon{left:10px}}}}.bui-InputClear{cursor:pointer;color:var(--bui-fg-secondary);background-color:#0000;border:none;justify-content:center;align-items:center;margin:0;padding:0;transition:color .2s ease-in-out;display:flex;position:absolute;top:0;bottom:0;right:0}.bui-InputClear:hover{color:var(--bui-fg-primary)}.bui-InputClear[data-size=small]{width:2rem;height:2rem}.bui-InputClear[data-size=medium]{width:2.5rem;height:2.5rem}.bui-InputClear svg{width:1rem;height:1rem}.bui-Skeleton{animation:var(--bui-animate-pulse);background-color:var(--bui-bg-surface-2);border-radius:var(--bui-radius-2)}.bui-Skeleton[data-rounded=true]{border-radius:var(--bui-radius-full)}.bui-Tooltip{background:var(--bui-bg-surface-1);border:1px solid var(--bui-gray-3);forced-color-adjust:none;padding:var(--bui-space-2)var(--bui-space-3);max-width:240px;font-size:var(--bui-font-size-3);font-family:var(--bui-font-regular);color:var(--bui-fg-primary);border-radius:4px;outline:none;transition:transform .2s,opacity .2s;transform:translate(0,0);box-shadow:0 10px 15px -3px #0000001a,0 4px 6px -4px #0000001a;&[data-entering],&[data-exiting]{transform:var(--origin);opacity:0}--tooltip-offset:var(--bui-space-3)&[data-placement=top]{margin-bottom:var(--tooltip-offset);--origin:translateY(4px)}&[data-placement=right]{margin-left:var(--tooltip-offset);--origin:translateX(-4px)}&[data-placement=bottom]{margin-top:var(--tooltip-offset);--origin:translateY(-4px)}&[data-placement=left]{margin-right:var(--tooltip-offset);--origin:translateX(4px)}}.bui-TooltipArrow{& svg{display:block;& path:first-child{fill:var(--bui-bg-surface-1)}& path:nth-child(2){fill:var(--bui-gray-3)}--tooltip-arrow-overlap:-2px}&[data-placement=top] svg{margin-top:var(--tooltip-arrow-overlap)}&[data-placement=bottom] svg{margin-bottom:var(--tooltip-arrow-overlap);transform:rotate(180deg)}&[data-placement=right] svg{margin-right:var(--tooltip-arrow-overlap);transform:rotate(90deg)}&[data-placement=left] svg{margin-left:var(--tooltip-arrow-overlap);transform:rotate(-90deg)}}[data-theme=dark]{& .bui-Tooltip{background:var(--bui-bg-surface-2);box-shadow:none;border:1px solid var(--bui-gray-4)}& .bui-TooltipArrow{& svg path:first-child{fill:var(--bui-bg-surface-2)}& svg path:nth-child(2){fill:var(--bui-gray-4)}}}.bui-ScrollAreaRoot{box-sizing:border-box;width:100%}.bui-ScrollAreaViewport{overscroll-behavior:contain;height:100%}.bui-ScrollAreaContent{padding-block:.75rem;flex-direction:column;gap:1rem;padding-left:1rem;padding-right:1.5rem;display:flex}.bui-ScrollAreaScrollbar{background-color:var(--bui-scrollbar);opacity:0;border-radius:.375rem;justify-content:center;width:.25rem;margin:.5rem;transition:opacity .15s .3s;display:flex;&[data-hovering],&[data-scrolling]{opacity:1;transition-duration:75ms;transition-delay:0s}&:before{content:"";width:1.25rem;height:100%;position:absolute}}.bui-ScrollAreaThumb{border-radius:inherit;background-color:var(--bui-scrollbar-thumb);width:100%}.bui-Select[data-invalid]{& .bui-SelectTrigger{border-color:var(--bui-fg-danger)}}.bui-SelectTrigger{box-sizing:border-box;border-radius:var(--bui-radius-3);border:1px solid var(--bui-border);background-color:var(--bui-bg-surface-1);cursor:pointer;justify-content:space-between;align-items:center;gap:var(--bui-space-2);width:100%;display:flex;& svg{color:var(--bui-fg-secondary);flex-shrink:0}&[data-size=small]{height:2rem;padding-inline:var(--bui-space-3)}&[data-size=medium]{height:3rem;padding-inline:var(--bui-space-4)}&[data-size=small] svg{width:1rem;height:1rem}&[data-size=medium] svg{width:1.25rem;height:1.25rem}&::placeholder{color:var(--bui-fg-secondary)}&:hover{border-color:var(--bui-border-hover);transition:border-color .2s ease-in-out,outline-color .2s ease-in-out}&:focus-visible{border-color:var(--bui-border-pressed);outline:0}&[data-invalid]{border-color:var(--bui-fg-danger)}&[data-invalid]:hover,&[data-invalid]:focus-visible{border-width:2px}&[disabled]{cursor:not-allowed;border-color:var(--bui-border-disabled);color:var(--bui-fg-disabled)}&[disabled] .bui-SelectValue{color:var(--bui-fg-disabled)}&[data-popup-open] .bui-SelectIcon{transform:rotate(180deg)}}.bui-SelectValue{text-overflow:ellipsis;white-space:nowrap;width:100%;font-size:var(--bui-font-size-3);font-family:var(--bui-font-regular);font-weight:var(--bui-font-weight-regular);color:var(--bui-fg-primary);text-align:left;overflow:hidden;& .bui-SelectItemIndicator{display:none}&[disabled]{color:var(--bui-fg-disabled)}}.bui-SelectItem{width:var(--anchor-width);padding-block:var(--bui-space-2);padding-left:var(--bui-space-3);padding-right:var(--bui-space-4);color:var(--bui-fg-primary);border-radius:var(--bui-radius-3);cursor:pointer;user-select:none;font-size:var(--bui-font-size-3);align-items:center;gap:var(--bui-space-1);outline:none;grid-template-columns:1rem 1fr;grid-template-areas:"icon text";display:grid;position:relative;&[data-focused]{z-index:0;color:var(--bui-fg-primary);position:relative}&[data-focused]:before{content:"";z-index:-1;background-color:var(--bui-bg-tint-hover);border-radius:.25rem;position:absolute;inset-block:0;inset-inline:.25rem}&[data-disabled]{cursor:not-allowed;color:var(--bui-fg-disabled)}&[data-selected] .bui-SelectItemIndicator{opacity:1}}.bui-SelectItemIndicator{opacity:0;grid-area:icon;justify-content:center;align-items:center;transition:opacity .2s ease-in-out;display:flex}.bui-SelectItemLabel{flex:1;grid-area:text}.bui-Switch{align-items:center;gap:var(--bui-space-3);font-size:var(--bui-font-size-3);color:var(--bui-fg-primary);cursor:pointer;display:flex;position:relative;&[data-pressed] .bui-SwitchIndicator{&:before{background:var(--bui-fg-solid)}}&[data-selected]{& .bui-SwitchIndicator{background:var(--bui-bg-solid);&:before{background:var(--bui-fg-solid);transform:translate(100%)}}&[data-pressed]{& .indicator{background:var(--bui-gray-3)}}}&[data-focus-visible] .bui-SwitchIndicator{outline-offset:2px;outline:2px solid}}.bui-SwitchIndicator{background:var(--bui-gray-3);border:2px;border-radius:1.143rem;width:2rem;height:1.143rem;transition:all .2s;&:before{content:"";background:var(--bui-fg-solid);border-radius:16px;width:.857rem;height:.857rem;margin:.143rem;transition:all .2s;display:block}} \ No newline at end of file +@layer tokens{:root{--bui-font-regular:system-ui;--bui-font-monospace:ui-monospace,"Menlo","Monaco","Consolas","Liberation Mono","Courier New",monospace;--bui-font-weight-regular:400;--bui-font-weight-bold:600;--bui-font-size-1:.625rem;--bui-font-size-2:.75rem;--bui-font-size-3:.875rem;--bui-font-size-4:1rem;--bui-font-size-5:1.25rem;--bui-font-size-6:1.5rem;--bui-font-size-7:2rem;--bui-font-size-8:3rem;--bui-font-size-9:4rem;--bui-font-size-10:5.75rem;--bui-space:.25rem;--bui-space-0_5:calc(var(--bui-space)*.5);--bui-space-1:var(--bui-space);--bui-space-1_5:calc(var(--bui-space)*1.5);--bui-space-2:calc(var(--bui-space)*2);--bui-space-3:calc(var(--bui-space)*3);--bui-space-4:calc(var(--bui-space)*4);--bui-space-5:calc(var(--bui-space)*5);--bui-space-6:calc(var(--bui-space)*6);--bui-space-7:calc(var(--bui-space)*7);--bui-space-8:calc(var(--bui-space)*8);--bui-space-9:calc(var(--bui-space)*9);--bui-space-10:calc(var(--bui-space)*10);--bui-space-11:calc(var(--bui-space)*11);--bui-space-12:calc(var(--bui-space)*12);--bui-space-13:calc(var(--bui-space)*13);--bui-space-14:calc(var(--bui-space)*14);--bui-radius-1:calc(.125rem);--bui-radius-2:calc(.25rem);--bui-radius-3:calc(.5rem);--bui-radius-4:calc(.75rem);--bui-radius-5:calc(1rem);--bui-radius-6:calc(1.25rem);--bui-radius-full:9999px;--bui-black:#000;--bui-white:#fff;--bui-gray-1:#f8f8f8;--bui-gray-2:#ececec;--bui-gray-3:#d9d9d9;--bui-gray-4:#c1c1c1;--bui-gray-5:#9e9e9e;--bui-gray-6:#8c8c8c;--bui-gray-7:#757575;--bui-gray-8:#595959;--bui-bg:var(--bui-gray-1);--bui-bg-surface-1:var(--bui-white);--bui-bg-surface-2:var(--bui-gray-2);--bui-bg-solid:#1f5493;--bui-bg-solid-hover:#163a66;--bui-bg-solid-pressed:#0f2b4e;--bui-bg-solid-disabled:#ebebeb;--bui-bg-tint:transparent;--bui-bg-tint-hover:#1f549366;--bui-bg-tint-pressed:#1f549399;--bui-bg-tint-disabled:#ebebeb;--bui-bg-danger:#feebe7;--bui-bg-warning:#fff2b2;--bui-bg-success:#e6f6eb;--bui-fg-primary:var(--bui-black);--bui-fg-secondary:var(--bui-gray-7);--bui-fg-link:#1f5493;--bui-fg-link-hover:#1f2d5c;--bui-fg-disabled:#9e9e9e;--bui-fg-solid:var(--bui-white);--bui-fg-solid-disabled:#9c9c9c;--bui-fg-tint:#1f5493;--bui-fg-tint-disabled:var(--bui-gray-5);--bui-fg-danger:#e22b2b;--bui-fg-warning:#e36d05;--bui-fg-success:#1db954;--bui-border:#0000001a;--bui-border-hover:#0003;--bui-border-pressed:#0006;--bui-border-disabled:#0000001a;--bui-border-danger:#f87a7a;--bui-border-warning:#e36d05;--bui-border-success:#53db83;--bui-ring:#1f5493;--bui-scrollbar:#a0a0a03b;--bui-scrollbar-thumb:#a0a0a0;--bui-animate-pulse:pulse 2s cubic-bezier(.4,0,.6,1)infinite}[data-theme-mode=dark]{--bui-gray-1:#191919;--bui-gray-2:#242424;--bui-gray-3:#373737;--bui-gray-4:#464646;--bui-gray-5:#575757;--bui-gray-6:#7b7b7b;--bui-gray-7:#9e9e9e;--bui-gray-8:#b4b4b4;--bui-bg:#333;--bui-bg-surface-1:#424242;--bui-bg-surface-2:var(--bui-gray-2);--bui-bg-solid:#9cc9ff;--bui-bg-solid-hover:#83b9fd;--bui-bg-solid-pressed:#83b9fd;--bui-bg-solid-disabled:#222;--bui-bg-tint:transparent;--bui-bg-tint-hover:#9cc9ff1f;--bui-bg-tint-pressed:#9cc9ff29;--bui-bg-tint-disabled:transparent;--bui-bg-danger:#3b1219;--bui-bg-warning:#302008;--bui-bg-success:#132d21;--bui-fg-primary:var(--bui-white);--bui-fg-secondary:var(--bui-gray-7);--bui-fg-link:#9cc9ff;--bui-fg-link-hover:#7eb5f7;--bui-fg-disabled:var(--bui-gray-7);--bui-fg-solid:#101821;--bui-fg-solid-disabled:var(--bui-gray-5);--bui-fg-tint:#9cc9ff;--bui-fg-tint-disabled:var(--bui-gray-5);--bui-fg-danger:#e22b2b;--bui-fg-warning:#e36d05;--bui-fg-success:#1db954;--bui-border:#ffffff1f;--bui-border-hover:#fff6;--bui-border-pressed:#ffffff80;--bui-border-disabled:#fff3;--bui-border-danger:#f87a7a;--bui-border-warning:#e36d05;--bui-border-success:#53db83;--bui-ring:#1f5493;--bui-scrollbar:#3636363a;--bui-scrollbar-thumb:#575757}}@layer base{*,:before,:after{box-sizing:border-box}html{-webkit-text-size-adjust:100%;tab-size:4;font-family:system-ui,Segoe UI,Roboto,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji;line-height:1.15}body{margin:0}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Consolas,Liberation Mono,Menlo,monospace;font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{border-color:currentColor}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:100%;line-height:1.15}button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button}legend{padding:0}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}:where(a){color:inherit;text-decoration:none}@keyframes pulse{50%{opacity:.5}}body{background-color:var(--bui-bg);color:var(--bui-fg-primary);font-family:var(--bui-font-regular);font-weight:var(--bui-font-weight-regular);font-size:var(--bui-font-size-3);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}}@layer components;@layer utilities{.bui-p{padding:var(--p)}.bui-p-0\.5{padding:var(--bui-space-0_5)}.bui-p-1{padding:var(--bui-space-1)}.bui-p-1\.5{padding:var(--bui-space-1_5)}.bui-p-2{padding:var(--bui-space-2)}.bui-p-3{padding:var(--bui-space-3)}.bui-p-4{padding:var(--bui-space-4)}.bui-p-5{padding:var(--bui-space-5)}.bui-p-6{padding:var(--bui-space-6)}.bui-p-7{padding:var(--bui-space-7)}.bui-p-8{padding:var(--bui-space-8)}.bui-p-9{padding:var(--bui-space-9)}.bui-p-10{padding:var(--bui-space-10)}.bui-p-11{padding:var(--bui-space-11)}.bui-p-12{padding:var(--bui-space-12)}.bui-p-13{padding:var(--bui-space-13)}.bui-p-14{padding:var(--bui-space-14)}@media (width>=640px){.xs\:bui-p{padding:var(--p-xs)}.xs\:bui-p-0\.5{padding:var(--bui-space-0_5)}.xs\:bui-p-1{padding:var(--bui-space-1)}.xs\:bui-p-1\.5{padding:var(--bui-space-1_5)}.xs\:bui-p-2{padding:var(--bui-space-2)}.xs\:bui-p-3{padding:var(--bui-space-3)}.xs\:bui-p-4{padding:var(--bui-space-4)}.xs\:bui-p-5{padding:var(--bui-space-5)}.xs\:bui-p-6{padding:var(--bui-space-6)}.xs\:bui-p-7{padding:var(--bui-space-7)}.xs\:bui-p-8{padding:var(--bui-space-8)}.xs\:bui-p-9{padding:var(--bui-space-9)}.xs\:bui-p-10{padding:var(--bui-space-10)}.xs\:bui-p-11{padding:var(--bui-space-11)}.xs\:bui-p-12{padding:var(--bui-space-12)}.xs\:bui-p-13{padding:var(--bui-space-13)}.xs\:bui-p-14{padding:var(--bui-space-14)}}@media (width>=768px){.sm\:bui-p{padding:var(--p-sm)}.sm\:bui-p-0\.5{padding:var(--bui-space-0_5)}.sm\:bui-p-1{padding:var(--bui-space-1)}.sm\:bui-p-1\.5{padding:var(--bui-space-1_5)}.sm\:bui-p-2{padding:var(--bui-space-2)}.sm\:bui-p-3{padding:var(--bui-space-3)}.sm\:bui-p-4{padding:var(--bui-space-4)}.sm\:bui-p-5{padding:var(--bui-space-5)}.sm\:bui-p-6{padding:var(--bui-space-6)}.sm\:bui-p-7{padding:var(--bui-space-7)}.sm\:bui-p-8{padding:var(--bui-space-8)}.sm\:bui-p-9{padding:var(--bui-space-9)}.sm\:bui-p-10{padding:var(--bui-space-10)}.sm\:bui-p-11{padding:var(--bui-space-11)}.sm\:bui-p-12{padding:var(--bui-space-12)}.sm\:bui-p-13{padding:var(--bui-space-13)}.sm\:bui-p-14{padding:var(--bui-space-14)}}@media (width>=1024px){.md\:bui-p{padding:var(--p-md)}.md\:bui-p-0\.5{padding:var(--bui-space-0_5)}.md\:bui-p-1{padding:var(--bui-space-1)}.md\:bui-p-1\.5{padding:var(--bui-space-1_5)}.md\:bui-p-2{padding:var(--bui-space-2)}.md\:bui-p-3{padding:var(--bui-space-3)}.md\:bui-p-4{padding:var(--bui-space-4)}.md\:bui-p-5{padding:var(--bui-space-5)}.md\:bui-p-6{padding:var(--bui-space-6)}.md\:bui-p-7{padding:var(--bui-space-7)}.md\:bui-p-8{padding:var(--bui-space-8)}.md\:bui-p-9{padding:var(--bui-space-9)}.md\:bui-p-10{padding:var(--bui-space-10)}.md\:bui-p-11{padding:var(--bui-space-11)}.md\:bui-p-12{padding:var(--bui-space-12)}.md\:bui-p-13{padding:var(--bui-space-13)}.md\:bui-p-14{padding:var(--bui-space-14)}}@media (width>=1280px){.lg\:bui-p{padding:var(--p-lg)}.lg\:bui-p-0\.5{padding:var(--bui-space-0_5)}.lg\:bui-p-1{padding:var(--bui-space-1)}.lg\:bui-p-1\.5{padding:var(--bui-space-1_5)}.lg\:bui-p-2{padding:var(--bui-space-2)}.lg\:bui-p-3{padding:var(--bui-space-3)}.lg\:bui-p-4{padding:var(--bui-space-4)}.lg\:bui-p-5{padding:var(--bui-space-5)}.lg\:bui-p-6{padding:var(--bui-space-6)}.lg\:bui-p-7{padding:var(--bui-space-7)}.lg\:bui-p-8{padding:var(--bui-space-8)}.lg\:bui-p-9{padding:var(--bui-space-9)}.lg\:bui-p-10{padding:var(--bui-space-10)}.lg\:bui-p-11{padding:var(--bui-space-11)}.lg\:bui-p-12{padding:var(--bui-space-12)}.lg\:bui-p-13{padding:var(--bui-space-13)}.lg\:bui-p-14{padding:var(--bui-space-14)}}@media (width>=1536px){.xl\:bui-p{padding:var(--p-xl)}.xl\:bui-p-0\.5{padding:var(--bui-space-0_5)}.xl\:bui-p-1{padding:var(--bui-space-1)}.xl\:bui-p-1\.5{padding:var(--bui-space-1_5)}.xl\:bui-p-2{padding:var(--bui-space-2)}.xl\:bui-p-3{padding:var(--bui-space-3)}.xl\:bui-p-4{padding:var(--bui-space-4)}.xl\:bui-p-5{padding:var(--bui-space-5)}.xl\:bui-p-6{padding:var(--bui-space-6)}.xl\:bui-p-7{padding:var(--bui-space-7)}.xl\:bui-p-8{padding:var(--bui-space-8)}.xl\:bui-p-9{padding:var(--bui-space-9)}.xl\:bui-p-10{padding:var(--bui-space-10)}.xl\:bui-p-11{padding:var(--bui-space-11)}.xl\:bui-p-12{padding:var(--bui-space-12)}.xl\:bui-p-13{padding:var(--bui-space-13)}.xl\:bui-p-14{padding:var(--bui-space-14)}}.bui-pl{padding-left:var(--pl)}.bui-pl-0\.5{padding-left:var(--bui-space-0_5)}.bui-pl-1{padding-left:var(--bui-space-1)}.bui-pl-1\.5{padding-left:var(--bui-space-1_5)}.bui-pl-2{padding-left:var(--bui-space-2)}.bui-pl-3{padding-left:var(--bui-space-3)}.bui-pl-4{padding-left:var(--bui-space-4)}.bui-pl-5{padding-left:var(--bui-space-5)}.bui-pl-6{padding-left:var(--bui-space-6)}.bui-pl-7{padding-left:var(--bui-space-7)}.bui-pl-8{padding-left:var(--bui-space-8)}.bui-pl-9{padding-left:var(--bui-space-9)}.bui-pl-10{padding-left:var(--bui-space-10)}.bui-pl-11{padding-left:var(--bui-space-11)}.bui-pl-12{padding-left:var(--bui-space-12)}.bui-pl-13{padding-left:var(--bui-space-13)}.bui-pl-14{padding-left:var(--bui-space-14)}@media (width>=640px){.xs\:bui-pl{padding-left:var(--pl-xs)}.xs\:bui-pl-0\.5{padding-left:var(--bui-space-0_5)}.xs\:bui-pl-1{padding-left:var(--bui-space-1)}.xs\:bui-pl-1\.5{padding-left:var(--bui-space-1_5)}.xs\:bui-pl-2{padding-left:var(--bui-space-2)}.xs\:bui-pl-3{padding-left:var(--bui-space-3)}.xs\:bui-pl-4{padding-left:var(--bui-space-4)}.xs\:bui-pl-5{padding-left:var(--bui-space-5)}.xs\:bui-pl-6{padding-left:var(--bui-space-6)}.xs\:bui-pl-7{padding-left:var(--bui-space-7)}.xs\:bui-pl-8{padding-left:var(--bui-space-8)}.xs\:bui-pl-9{padding-left:var(--bui-space-9)}.xs\:bui-pl-10{padding-left:var(--bui-space-10)}.xs\:bui-pl-11{padding-left:var(--bui-space-11)}.xs\:bui-pl-12{padding-left:var(--bui-space-12)}.xs\:bui-pl-13{padding-left:var(--bui-space-13)}.xs\:bui-pl-14{padding-left:var(--bui-space-14)}}@media (width>=768px){.sm\:bui-pl{padding-left:var(--pl-sm)}.sm\:bui-pl-0\.5{padding-left:var(--bui-space-0_5)}.sm\:bui-pl-1{padding-left:var(--bui-space-1)}.sm\:bui-pl-1\.5{padding-left:var(--bui-space-1_5)}.sm\:bui-pl-2{padding-left:var(--bui-space-2)}.sm\:bui-pl-3{padding-left:var(--bui-space-3)}.sm\:bui-pl-4{padding-left:var(--bui-space-4)}.sm\:bui-pl-5{padding-left:var(--bui-space-5)}.sm\:bui-pl-6{padding-left:var(--bui-space-6)}.sm\:bui-pl-7{padding-left:var(--bui-space-7)}.sm\:bui-pl-8{padding-left:var(--bui-space-8)}.sm\:bui-pl-9{padding-left:var(--bui-space-9)}.sm\:bui-pl-10{padding-left:var(--bui-space-10)}.sm\:bui-pl-11{padding-left:var(--bui-space-11)}.sm\:bui-pl-12{padding-left:var(--bui-space-12)}.sm\:bui-pl-13{padding-left:var(--bui-space-13)}.sm\:bui-pl-14{padding-left:var(--bui-space-14)}}@media (width>=1024px){.md\:bui-pl{padding-left:var(--pl-md)}.md\:bui-pl-0\.5{padding-left:var(--bui-space-0_5)}.md\:bui-pl-1{padding-left:var(--bui-space-1)}.md\:bui-pl-1\.5{padding-left:var(--bui-space-1_5)}.md\:bui-pl-2{padding-left:var(--bui-space-2)}.md\:bui-pl-3{padding-left:var(--bui-space-3)}.md\:bui-pl-4{padding-left:var(--bui-space-4)}.md\:bui-pl-5{padding-left:var(--bui-space-5)}.md\:bui-pl-6{padding-left:var(--bui-space-6)}.md\:bui-pl-7{padding-left:var(--bui-space-7)}.md\:bui-pl-8{padding-left:var(--bui-space-8)}.md\:bui-pl-9{padding-left:var(--bui-space-9)}.md\:bui-pl-10{padding-left:var(--bui-space-10)}.md\:bui-pl-11{padding-left:var(--bui-space-11)}.md\:bui-pl-12{padding-left:var(--bui-space-12)}.md\:bui-pl-13{padding-left:var(--bui-space-13)}.md\:bui-pl-14{padding-left:var(--bui-space-14)}}@media (width>=1280px){.lg\:bui-pl{padding-left:var(--pl-lg)}.lg\:bui-pl-0\.5{padding-left:var(--bui-space-0_5)}.lg\:bui-pl-1{padding-left:var(--bui-space-1)}.lg\:bui-pl-1\.5{padding-left:var(--bui-space-1_5)}.lg\:bui-pl-2{padding-left:var(--bui-space-2)}.lg\:bui-pl-3{padding-left:var(--bui-space-3)}.lg\:bui-pl-4{padding-left:var(--bui-space-4)}.lg\:bui-pl-5{padding-left:var(--bui-space-5)}.lg\:bui-pl-6{padding-left:var(--bui-space-6)}.lg\:bui-pl-7{padding-left:var(--bui-space-7)}.lg\:bui-pl-8{padding-left:var(--bui-space-8)}.lg\:bui-pl-9{padding-left:var(--bui-space-9)}.lg\:bui-pl-10{padding-left:var(--bui-space-10)}.lg\:bui-pl-11{padding-left:var(--bui-space-11)}.lg\:bui-pl-12{padding-left:var(--bui-space-12)}.lg\:bui-pl-13{padding-left:var(--bui-space-13)}.lg\:bui-pl-14{padding-left:var(--bui-space-14)}}@media (width>=1536px){.xl\:bui-pl{padding-left:var(--pl-xl)}.xl\:bui-pl-0\.5{padding-left:var(--bui-space-0_5)}.xl\:bui-pl-1{padding-left:var(--bui-space-1)}.xl\:bui-pl-1\.5{padding-left:var(--bui-space-1_5)}.xl\:bui-pl-2{padding-left:var(--bui-space-2)}.xl\:bui-pl-3{padding-left:var(--bui-space-3)}.xl\:bui-pl-4{padding-left:var(--bui-space-4)}.xl\:bui-pl-5{padding-left:var(--bui-space-5)}.xl\:bui-pl-6{padding-left:var(--bui-space-6)}.xl\:bui-pl-7{padding-left:var(--bui-space-7)}.xl\:bui-pl-8{padding-left:var(--bui-space-8)}.xl\:bui-pl-9{padding-left:var(--bui-space-9)}.xl\:bui-pl-10{padding-left:var(--bui-space-10)}.xl\:bui-pl-11{padding-left:var(--bui-space-11)}.xl\:bui-pl-12{padding-left:var(--bui-space-12)}.xl\:bui-pl-13{padding-left:var(--bui-space-13)}.xl\:bui-pl-14{padding-left:var(--bui-space-14)}}.bui-pr{padding-right:var(--pr)}.bui-pr-0\.5{padding-right:var(--bui-space-0_5)}.bui-pr-1{padding-right:var(--bui-space-1)}.bui-pr-1\.5{padding-right:var(--bui-space-1_5)}.bui-pr-2{padding-right:var(--bui-space-2)}.bui-pr-3{padding-right:var(--bui-space-3)}.bui-pr-4{padding-right:var(--bui-space-4)}.bui-pr-5{padding-right:var(--bui-space-5)}.bui-pr-6{padding-right:var(--bui-space-6)}.bui-pr-7{padding-right:var(--bui-space-7)}.bui-pr-8{padding-right:var(--bui-space-8)}.bui-pr-9{padding-right:var(--bui-space-9)}.bui-pr-10{padding-right:var(--bui-space-10)}.bui-pr-11{padding-right:var(--bui-space-11)}.bui-pr-12{padding-right:var(--bui-space-12)}.bui-pr-13{padding-right:var(--bui-space-13)}.bui-pr-14{padding-right:var(--bui-space-14)}@media (width>=640px){.xs\:bui-pr{padding-right:var(--pr-xs)}.xs\:bui-pr-0\.5{padding-right:var(--bui-space-0_5)}.xs\:bui-pr-1{padding-right:var(--bui-space-1)}.xs\:bui-pr-1\.5{padding-right:var(--bui-space-1_5)}.xs\:bui-pr-2{padding-right:var(--bui-space-2)}.xs\:bui-pr-3{padding-right:var(--bui-space-3)}.xs\:bui-pr-4{padding-right:var(--bui-space-4)}.xs\:bui-pr-5{padding-right:var(--bui-space-5)}.xs\:bui-pr-6{padding-right:var(--bui-space-6)}.xs\:bui-pr-7{padding-right:var(--bui-space-7)}.xs\:bui-pr-8{padding-right:var(--bui-space-8)}.xs\:bui-pr-9{padding-right:var(--bui-space-9)}.xs\:bui-pr-10{padding-right:var(--bui-space-10)}.xs\:bui-pr-11{padding-right:var(--bui-space-11)}.xs\:bui-pr-12{padding-right:var(--bui-space-12)}.xs\:bui-pr-13{padding-right:var(--bui-space-13)}.xs\:bui-pr-14{padding-right:var(--bui-space-14)}}@media (width>=768px){.sm\:bui-pr{padding-right:var(--pr-sm)}.sm\:bui-pr-0\.5{padding-right:var(--bui-space-0_5)}.sm\:bui-pr-1{padding-right:var(--bui-space-1)}.sm\:bui-pr-1\.5{padding-right:var(--bui-space-1_5)}.sm\:bui-pr-2{padding-right:var(--bui-space-2)}.sm\:bui-pr-3{padding-right:var(--bui-space-3)}.sm\:bui-pr-4{padding-right:var(--bui-space-4)}.sm\:bui-pr-5{padding-right:var(--bui-space-5)}.sm\:bui-pr-6{padding-right:var(--bui-space-6)}.sm\:bui-pr-7{padding-right:var(--bui-space-7)}.sm\:bui-pr-8{padding-right:var(--bui-space-8)}.sm\:bui-pr-9{padding-right:var(--bui-space-9)}.sm\:bui-pr-10{padding-right:var(--bui-space-10)}.sm\:bui-pr-11{padding-right:var(--bui-space-11)}.sm\:bui-pr-12{padding-right:var(--bui-space-12)}.sm\:bui-pr-13{padding-right:var(--bui-space-13)}.sm\:bui-pr-14{padding-right:var(--bui-space-14)}}@media (width>=1024px){.md\:bui-pr{padding-right:var(--pr-md)}.md\:bui-pr-0\.5{padding-right:var(--bui-space-0_5)}.md\:bui-pr-1{padding-right:var(--bui-space-1)}.md\:bui-pr-1\.5{padding-right:var(--bui-space-1_5)}.md\:bui-pr-2{padding-right:var(--bui-space-2)}.md\:bui-pr-3{padding-right:var(--bui-space-3)}.md\:bui-pr-4{padding-right:var(--bui-space-4)}.md\:bui-pr-5{padding-right:var(--bui-space-5)}.md\:bui-pr-6{padding-right:var(--bui-space-6)}.md\:bui-pr-7{padding-right:var(--bui-space-7)}.md\:bui-pr-8{padding-right:var(--bui-space-8)}.md\:bui-pr-9{padding-right:var(--bui-space-9)}.md\:bui-pr-10{padding-right:var(--bui-space-10)}.md\:bui-pr-11{padding-right:var(--bui-space-11)}.md\:bui-pr-12{padding-right:var(--bui-space-12)}.md\:bui-pr-13{padding-right:var(--bui-space-13)}.md\:bui-pr-14{padding-right:var(--bui-space-14)}}@media (width>=1280px){.lg\:bui-pr{padding-right:var(--pr-lg)}.lg\:bui-pr-0\.5{padding-right:var(--bui-space-0_5)}.lg\:bui-pr-1{padding-right:var(--bui-space-1)}.lg\:bui-pr-1\.5{padding-right:var(--bui-space-1_5)}.lg\:bui-pr-2{padding-right:var(--bui-space-2)}.lg\:bui-pr-3{padding-right:var(--bui-space-3)}.lg\:bui-pr-4{padding-right:var(--bui-space-4)}.lg\:bui-pr-5{padding-right:var(--bui-space-5)}.lg\:bui-pr-6{padding-right:var(--bui-space-6)}.lg\:bui-pr-7{padding-right:var(--bui-space-7)}.lg\:bui-pr-8{padding-right:var(--bui-space-8)}.lg\:bui-pr-9{padding-right:var(--bui-space-9)}.lg\:bui-pr-10{padding-right:var(--bui-space-10)}.lg\:bui-pr-11{padding-right:var(--bui-space-11)}.lg\:bui-pr-12{padding-right:var(--bui-space-12)}.lg\:bui-pr-13{padding-right:var(--bui-space-13)}.lg\:bui-pr-14{padding-right:var(--bui-space-14)}}@media (width>=1536px){.xl\:bui-pr{padding-right:var(--pr-xl)}.xl\:bui-pr-0\.5{padding-right:var(--bui-space-0_5)}.xl\:bui-pr-1{padding-right:var(--bui-space-1)}.xl\:bui-pr-1\.5{padding-right:var(--bui-space-1_5)}.xl\:bui-pr-2{padding-right:var(--bui-space-2)}.xl\:bui-pr-3{padding-right:var(--bui-space-3)}.xl\:bui-pr-4{padding-right:var(--bui-space-4)}.xl\:bui-pr-5{padding-right:var(--bui-space-5)}.xl\:bui-pr-6{padding-right:var(--bui-space-6)}.xl\:bui-pr-7{padding-right:var(--bui-space-7)}.xl\:bui-pr-8{padding-right:var(--bui-space-8)}.xl\:bui-pr-9{padding-right:var(--bui-space-9)}.xl\:bui-pr-10{padding-right:var(--bui-space-10)}.xl\:bui-pr-11{padding-right:var(--bui-space-11)}.xl\:bui-pr-12{padding-right:var(--bui-space-12)}.xl\:bui-pr-13{padding-right:var(--bui-space-13)}.xl\:bui-pr-14{padding-right:var(--bui-space-14)}}.bui-pt{padding-top:var(--pt)}.bui-pt-0\.5{padding-top:var(--bui-space-0_5)}.bui-pt-1{padding-top:var(--bui-space-1)}.bui-pt-1\.5{padding-top:var(--bui-space-1_5)}.bui-pt-2{padding-top:var(--bui-space-2)}.bui-pt-3{padding-top:var(--bui-space-3)}.bui-pt-4{padding-top:var(--bui-space-4)}.bui-pt-5{padding-top:var(--bui-space-5)}.bui-pt-6{padding-top:var(--bui-space-6)}.bui-pt-7{padding-top:var(--bui-space-7)}.bui-pt-8{padding-top:var(--bui-space-8)}.bui-pt-9{padding-top:var(--bui-space-9)}.bui-pt-10{padding-top:var(--bui-space-10)}.bui-pt-11{padding-top:var(--bui-space-11)}.bui-pt-12{padding-top:var(--bui-space-12)}.bui-pt-13{padding-top:var(--bui-space-13)}.bui-pt-14{padding-top:var(--bui-space-14)}@media (width>=640px){.xs\:bui-pt{padding-top:var(--pt-xs)}.xs\:bui-pt-0\.5{padding-top:var(--bui-space-0_5)}.xs\:bui-pt-1{padding-top:var(--bui-space-1)}.xs\:bui-pt-1\.5{padding-top:var(--bui-space-1_5)}.xs\:bui-pt-2{padding-top:var(--bui-space-2)}.xs\:bui-pt-3{padding-top:var(--bui-space-3)}.xs\:bui-pt-4{padding-top:var(--bui-space-4)}.xs\:bui-pt-5{padding-top:var(--bui-space-5)}.xs\:bui-pt-6{padding-top:var(--bui-space-6)}.xs\:bui-pt-7{padding-top:var(--bui-space-7)}.xs\:bui-pt-8{padding-top:var(--bui-space-8)}.xs\:bui-pt-9{padding-top:var(--bui-space-9)}.xs\:bui-pt-10{padding-top:var(--bui-space-10)}.xs\:bui-pt-11{padding-top:var(--bui-space-11)}.xs\:bui-pt-12{padding-top:var(--bui-space-12)}.xs\:bui-pt-13{padding-top:var(--bui-space-13)}.xs\:bui-pt-14{padding-top:var(--bui-space-14)}}@media (width>=768px){.sm\:bui-pt{padding-top:var(--pt-sm)}.sm\:bui-pt-0\.5{padding-top:var(--bui-space-0_5)}.sm\:bui-pt-1{padding-top:var(--bui-space-1)}.sm\:bui-pt-1\.5{padding-top:var(--bui-space-1_5)}.sm\:bui-pt-2{padding-top:var(--bui-space-2)}.sm\:bui-pt-3{padding-top:var(--bui-space-3)}.sm\:bui-pt-4{padding-top:var(--bui-space-4)}.sm\:bui-pt-5{padding-top:var(--bui-space-5)}.sm\:bui-pt-6{padding-top:var(--bui-space-6)}.sm\:bui-pt-7{padding-top:var(--bui-space-7)}.sm\:bui-pt-8{padding-top:var(--bui-space-8)}.sm\:bui-pt-9{padding-top:var(--bui-space-9)}.sm\:bui-pt-10{padding-top:var(--bui-space-10)}.sm\:bui-pt-11{padding-top:var(--bui-space-11)}.sm\:bui-pt-12{padding-top:var(--bui-space-12)}.sm\:bui-pt-13{padding-top:var(--bui-space-13)}.sm\:bui-pt-14{padding-top:var(--bui-space-14)}}@media (width>=1024px){.md\:bui-pt{padding-top:var(--pt-md)}.md\:bui-pt-0\.5{padding-top:var(--bui-space-0_5)}.md\:bui-pt-1{padding-top:var(--bui-space-1)}.md\:bui-pt-1\.5{padding-top:var(--bui-space-1_5)}.md\:bui-pt-2{padding-top:var(--bui-space-2)}.md\:bui-pt-3{padding-top:var(--bui-space-3)}.md\:bui-pt-4{padding-top:var(--bui-space-4)}.md\:bui-pt-5{padding-top:var(--bui-space-5)}.md\:bui-pt-6{padding-top:var(--bui-space-6)}.md\:bui-pt-7{padding-top:var(--bui-space-7)}.md\:bui-pt-8{padding-top:var(--bui-space-8)}.md\:bui-pt-9{padding-top:var(--bui-space-9)}.md\:bui-pt-10{padding-top:var(--bui-space-10)}.md\:bui-pt-11{padding-top:var(--bui-space-11)}.md\:bui-pt-12{padding-top:var(--bui-space-12)}.md\:bui-pt-13{padding-top:var(--bui-space-13)}.md\:bui-pt-14{padding-top:var(--bui-space-14)}}@media (width>=1280px){.lg\:bui-pt{padding-top:var(--pt-lg)}.lg\:bui-pt-0\.5{padding-top:var(--bui-space-0_5)}.lg\:bui-pt-1{padding-top:var(--bui-space-1)}.lg\:bui-pt-1\.5{padding-top:var(--bui-space-1_5)}.lg\:bui-pt-2{padding-top:var(--bui-space-2)}.lg\:bui-pt-3{padding-top:var(--bui-space-3)}.lg\:bui-pt-4{padding-top:var(--bui-space-4)}.lg\:bui-pt-5{padding-top:var(--bui-space-5)}.lg\:bui-pt-6{padding-top:var(--bui-space-6)}.lg\:bui-pt-7{padding-top:var(--bui-space-7)}.lg\:bui-pt-8{padding-top:var(--bui-space-8)}.lg\:bui-pt-9{padding-top:var(--bui-space-9)}.lg\:bui-pt-10{padding-top:var(--bui-space-10)}.lg\:bui-pt-11{padding-top:var(--bui-space-11)}.lg\:bui-pt-12{padding-top:var(--bui-space-12)}.lg\:bui-pt-13{padding-top:var(--bui-space-13)}.lg\:bui-pt-14{padding-top:var(--bui-space-14)}}@media (width>=1536px){.xl\:bui-pt{padding-top:var(--pt-xl)}.xl\:bui-pt-0\.5{padding-top:var(--bui-space-0_5)}.xl\:bui-pt-1{padding-top:var(--bui-space-1)}.xl\:bui-pt-1\.5{padding-top:var(--bui-space-1_5)}.xl\:bui-pt-2{padding-top:var(--bui-space-2)}.xl\:bui-pt-3{padding-top:var(--bui-space-3)}.xl\:bui-pt-4{padding-top:var(--bui-space-4)}.xl\:bui-pt-5{padding-top:var(--bui-space-5)}.xl\:bui-pt-6{padding-top:var(--bui-space-6)}.xl\:bui-pt-7{padding-top:var(--bui-space-7)}.xl\:bui-pt-8{padding-top:var(--bui-space-8)}.xl\:bui-pt-9{padding-top:var(--bui-space-9)}.xl\:bui-pt-10{padding-top:var(--bui-space-10)}.xl\:bui-pt-11{padding-top:var(--bui-space-11)}.xl\:bui-pt-12{padding-top:var(--bui-space-12)}.xl\:bui-pt-13{padding-top:var(--bui-space-13)}.xl\:bui-pt-14{padding-top:var(--bui-space-14)}}.bui-pb{padding-bottom:var(--pb)}.bui-pb-0\.5{padding-bottom:var(--bui-space-0_5)}.bui-pb-1{padding-bottom:var(--bui-space-1)}.bui-pb-1\.5{padding-bottom:var(--bui-space-1_5)}.bui-pb-2{padding-bottom:var(--bui-space-2)}.bui-pb-3{padding-bottom:var(--bui-space-3)}.bui-pb-4{padding-bottom:var(--bui-space-4)}.bui-pb-5{padding-bottom:var(--bui-space-5)}.bui-pb-6{padding-bottom:var(--bui-space-6)}.bui-pb-7{padding-bottom:var(--bui-space-7)}.bui-pb-8{padding-bottom:var(--bui-space-8)}.bui-pb-9{padding-bottom:var(--bui-space-9)}.bui-pb-10{padding-bottom:var(--bui-space-10)}.bui-pb-11{padding-bottom:var(--bui-space-11)}.bui-pb-12{padding-bottom:var(--bui-space-12)}.bui-pb-13{padding-bottom:var(--bui-space-13)}.bui-pb-14{padding-bottom:var(--bui-space-14)}@media (width>=640px){.xs\:bui-pb{padding-bottom:var(--pb-xs)}.xs\:bui-pb-0\.5{padding-bottom:var(--bui-space-0_5)}.xs\:bui-pb-1{padding-bottom:var(--bui-space-1)}.xs\:bui-pb-1\.5{padding-bottom:var(--bui-space-1_5)}.xs\:bui-pb-2{padding-bottom:var(--bui-space-2)}.xs\:bui-pb-3{padding-bottom:var(--bui-space-3)}.xs\:bui-pb-4{padding-bottom:var(--bui-space-4)}.xs\:bui-pb-5{padding-bottom:var(--bui-space-5)}.xs\:bui-pb-6{padding-bottom:var(--bui-space-6)}.xs\:bui-pb-7{padding-bottom:var(--bui-space-7)}.xs\:bui-pb-8{padding-bottom:var(--bui-space-8)}.xs\:bui-pb-9{padding-bottom:var(--bui-space-9)}.xs\:bui-pb-10{padding-bottom:var(--bui-space-10)}.xs\:bui-pb-11{padding-bottom:var(--bui-space-11)}.xs\:bui-pb-12{padding-bottom:var(--bui-space-12)}.xs\:bui-pb-13{padding-bottom:var(--bui-space-13)}.xs\:bui-pb-14{padding-bottom:var(--bui-space-14)}}@media (width>=768px){.sm\:bui-pb{padding-bottom:var(--pb-sm)}.sm\:bui-pb-0\.5{padding-bottom:var(--bui-space-0_5)}.sm\:bui-pb-1{padding-bottom:var(--bui-space-1)}.sm\:bui-pb-1\.5{padding-bottom:var(--bui-space-1_5)}.sm\:bui-pb-2{padding-bottom:var(--bui-space-2)}.sm\:bui-pb-3{padding-bottom:var(--bui-space-3)}.sm\:bui-pb-4{padding-bottom:var(--bui-space-4)}.sm\:bui-pb-5{padding-bottom:var(--bui-space-5)}.sm\:bui-pb-6{padding-bottom:var(--bui-space-6)}.sm\:bui-pb-7{padding-bottom:var(--bui-space-7)}.sm\:bui-pb-8{padding-bottom:var(--bui-space-8)}.sm\:bui-pb-9{padding-bottom:var(--bui-space-9)}.sm\:bui-pb-10{padding-bottom:var(--bui-space-10)}.sm\:bui-pb-11{padding-bottom:var(--bui-space-11)}.sm\:bui-pb-12{padding-bottom:var(--bui-space-12)}.sm\:bui-pb-13{padding-bottom:var(--bui-space-13)}.sm\:bui-pb-14{padding-bottom:var(--bui-space-14)}}@media (width>=1024px){.md\:bui-pb{padding-bottom:var(--pb-md)}.md\:bui-pb-0\.5{padding-bottom:var(--bui-space-0_5)}.md\:bui-pb-1{padding-bottom:var(--bui-space-1)}.md\:bui-pb-1\.5{padding-bottom:var(--bui-space-1_5)}.md\:bui-pb-2{padding-bottom:var(--bui-space-2)}.md\:bui-pb-3{padding-bottom:var(--bui-space-3)}.md\:bui-pb-4{padding-bottom:var(--bui-space-4)}.md\:bui-pb-5{padding-bottom:var(--bui-space-5)}.md\:bui-pb-6{padding-bottom:var(--bui-space-6)}.md\:bui-pb-7{padding-bottom:var(--bui-space-7)}.md\:bui-pb-8{padding-bottom:var(--bui-space-8)}.md\:bui-pb-9{padding-bottom:var(--bui-space-9)}.md\:bui-pb-10{padding-bottom:var(--bui-space-10)}.md\:bui-pb-11{padding-bottom:var(--bui-space-11)}.md\:bui-pb-12{padding-bottom:var(--bui-space-12)}.md\:bui-pb-13{padding-bottom:var(--bui-space-13)}.md\:bui-pb-14{padding-bottom:var(--bui-space-14)}}@media (width>=1280px){.lg\:bui-pb{padding-bottom:var(--pb-lg)}.lg\:bui-pb-0\.5{padding-bottom:var(--bui-space-0_5)}.lg\:bui-pb-1{padding-bottom:var(--bui-space-1)}.lg\:bui-pb-1\.5{padding-bottom:var(--bui-space-1_5)}.lg\:bui-pb-2{padding-bottom:var(--bui-space-2)}.lg\:bui-pb-3{padding-bottom:var(--bui-space-3)}.lg\:bui-pb-4{padding-bottom:var(--bui-space-4)}.lg\:bui-pb-5{padding-bottom:var(--bui-space-5)}.lg\:bui-pb-6{padding-bottom:var(--bui-space-6)}.lg\:bui-pb-7{padding-bottom:var(--bui-space-7)}.lg\:bui-pb-8{padding-bottom:var(--bui-space-8)}.lg\:bui-pb-9{padding-bottom:var(--bui-space-9)}.lg\:bui-pb-10{padding-bottom:var(--bui-space-10)}.lg\:bui-pb-11{padding-bottom:var(--bui-space-11)}.lg\:bui-pb-12{padding-bottom:var(--bui-space-12)}.lg\:bui-pb-13{padding-bottom:var(--bui-space-13)}.lg\:bui-pb-14{padding-bottom:var(--bui-space-14)}}@media (width>=1536px){.xl\:bui-pb{padding-bottom:var(--pb-xl)}.xl\:bui-pb-0\.5{padding-bottom:var(--bui-space-0_5)}.xl\:bui-pb-1{padding-bottom:var(--bui-space-1)}.xl\:bui-pb-1\.5{padding-bottom:var(--bui-space-1_5)}.xl\:bui-pb-2{padding-bottom:var(--bui-space-2)}.xl\:bui-pb-3{padding-bottom:var(--bui-space-3)}.xl\:bui-pb-4{padding-bottom:var(--bui-space-4)}.xl\:bui-pb-5{padding-bottom:var(--bui-space-5)}.xl\:bui-pb-6{padding-bottom:var(--bui-space-6)}.xl\:bui-pb-7{padding-bottom:var(--bui-space-7)}.xl\:bui-pb-8{padding-bottom:var(--bui-space-8)}.xl\:bui-pb-9{padding-bottom:var(--bui-space-9)}.xl\:bui-pb-10{padding-bottom:var(--bui-space-10)}.xl\:bui-pb-11{padding-bottom:var(--bui-space-11)}.xl\:bui-pb-12{padding-bottom:var(--bui-space-12)}.xl\:bui-pb-13{padding-bottom:var(--bui-space-13)}.xl\:bui-pb-14{padding-bottom:var(--bui-space-14)}}.bui-py{padding-top:var(--py);padding-bottom:var(--py)}.bui-py-0\.5{padding-top:var(--bui-space-0_5);padding-bottom:var(--bui-space-0_5)}.bui-py-1{padding-top:var(--bui-space-1);padding-bottom:var(--bui-space-1)}.bui-py-1\.5{padding-top:var(--bui-space-1_5);padding-bottom:var(--bui-space-1_5)}.bui-py-2{padding-top:var(--bui-space-2);padding-bottom:var(--bui-space-2)}.bui-py-3{padding-top:var(--bui-space-3);padding-bottom:var(--bui-space-3)}.bui-py-4{padding-top:var(--bui-space-4);padding-bottom:var(--bui-space-4)}.bui-py-5{padding-top:var(--bui-space-5);padding-bottom:var(--bui-space-5)}.bui-py-6{padding-top:var(--bui-space-6);padding-bottom:var(--bui-space-6)}.bui-py-7{padding-top:var(--bui-space-7);padding-bottom:var(--bui-space-7)}.bui-py-8{padding-top:var(--bui-space-8);padding-bottom:var(--bui-space-8)}.bui-py-9{padding-top:var(--bui-space-9);padding-bottom:var(--bui-space-9)}.bui-py-10{padding-top:var(--bui-space-10);padding-bottom:var(--bui-space-10)}.bui-py-11{padding-top:var(--bui-space-11);padding-bottom:var(--bui-space-11)}.bui-py-12{padding-top:var(--bui-space-12);padding-bottom:var(--bui-space-12)}.bui-py-13{padding-top:var(--bui-space-13);padding-bottom:var(--bui-space-13)}.bui-py-14{padding-top:var(--bui-space-14);padding-bottom:var(--bui-space-14)}@media (width>=640px){.xs\:bui-py{padding-top:var(--py-xs);padding-bottom:var(--py-xs)}.xs\:bui-py-0\.5{padding-top:var(--bui-space-0_5);padding-bottom:var(--bui-space-0_5)}.xs\:bui-py-1{padding-top:var(--bui-space-1);padding-bottom:var(--bui-space-1)}.xs\:bui-py-1\.5{padding-top:var(--bui-space-1_5);padding-bottom:var(--bui-space-1_5)}.xs\:bui-py-2{padding-top:var(--bui-space-2);padding-bottom:var(--bui-space-2)}.xs\:bui-py-3{padding-top:var(--bui-space-3);padding-bottom:var(--bui-space-3)}.xs\:bui-py-4{padding-top:var(--bui-space-4);padding-bottom:var(--bui-space-4)}.xs\:bui-py-5{padding-top:var(--bui-space-5);padding-bottom:var(--bui-space-5)}.xs\:bui-py-6{padding-top:var(--bui-space-6);padding-bottom:var(--bui-space-6)}.xs\:bui-py-7{padding-top:var(--bui-space-7);padding-bottom:var(--bui-space-7)}.xs\:bui-py-8{padding-top:var(--bui-space-8);padding-bottom:var(--bui-space-8)}.xs\:bui-py-9{padding-top:var(--bui-space-9);padding-bottom:var(--bui-space-9)}.xs\:bui-py-10{padding-top:var(--bui-space-10);padding-bottom:var(--bui-space-10)}.xs\:bui-py-11{padding-top:var(--bui-space-11);padding-bottom:var(--bui-space-11)}.xs\:bui-py-12{padding-top:var(--bui-space-12);padding-bottom:var(--bui-space-12)}.xs\:bui-py-13{padding-top:var(--bui-space-13);padding-bottom:var(--bui-space-13)}.xs\:bui-py-14{padding-top:var(--bui-space-14);padding-bottom:var(--bui-space-14)}}@media (width>=768px){.sm\:bui-py{padding-top:var(--py-sm);padding-bottom:var(--py-sm)}.sm\:bui-py-0\.5{padding-top:var(--bui-space-0_5);padding-bottom:var(--bui-space-0_5)}.sm\:bui-py-1{padding-top:var(--bui-space-1);padding-bottom:var(--bui-space-1)}.sm\:bui-py-1\.5{padding-top:var(--bui-space-1_5);padding-bottom:var(--bui-space-1_5)}.sm\:bui-py-2{padding-top:var(--bui-space-2);padding-bottom:var(--bui-space-2)}.sm\:bui-py-3{padding-top:var(--bui-space-3);padding-bottom:var(--bui-space-3)}.sm\:bui-py-4{padding-top:var(--bui-space-4);padding-bottom:var(--bui-space-4)}.sm\:bui-py-5{padding-top:var(--bui-space-5);padding-bottom:var(--bui-space-5)}.sm\:bui-py-6{padding-top:var(--bui-space-6);padding-bottom:var(--bui-space-6)}.sm\:bui-py-7{padding-top:var(--bui-space-7);padding-bottom:var(--bui-space-7)}.sm\:bui-py-8{padding-top:var(--bui-space-8);padding-bottom:var(--bui-space-8)}.sm\:bui-py-9{padding-top:var(--bui-space-9);padding-bottom:var(--bui-space-9)}.sm\:bui-py-10{padding-top:var(--bui-space-10);padding-bottom:var(--bui-space-10)}.sm\:bui-py-11{padding-top:var(--bui-space-11);padding-bottom:var(--bui-space-11)}.sm\:bui-py-12{padding-top:var(--bui-space-12);padding-bottom:var(--bui-space-12)}.sm\:bui-py-13{padding-top:var(--bui-space-13);padding-bottom:var(--bui-space-13)}.sm\:bui-py-14{padding-top:var(--bui-space-14);padding-bottom:var(--bui-space-14)}}@media (width>=1024px){.md\:bui-py{padding-top:var(--py-md);padding-bottom:var(--py-md)}.md\:bui-py-0\.5{padding-top:var(--bui-space-0_5);padding-bottom:var(--bui-space-0_5)}.md\:bui-py-1{padding-top:var(--bui-space-1);padding-bottom:var(--bui-space-1)}.md\:bui-py-1\.5{padding-top:var(--bui-space-1_5);padding-bottom:var(--bui-space-1_5)}.md\:bui-py-2{padding-top:var(--bui-space-2);padding-bottom:var(--bui-space-2)}.md\:bui-py-3{padding-top:var(--bui-space-3);padding-bottom:var(--bui-space-3)}.md\:bui-py-4{padding-top:var(--bui-space-4);padding-bottom:var(--bui-space-4)}.md\:bui-py-5{padding-top:var(--bui-space-5);padding-bottom:var(--bui-space-5)}.md\:bui-py-6{padding-top:var(--bui-space-6);padding-bottom:var(--bui-space-6)}.md\:bui-py-7{padding-top:var(--bui-space-7);padding-bottom:var(--bui-space-7)}.md\:bui-py-8{padding-top:var(--bui-space-8);padding-bottom:var(--bui-space-8)}.md\:bui-py-9{padding-top:var(--bui-space-9);padding-bottom:var(--bui-space-9)}.md\:bui-py-10{padding-top:var(--bui-space-10);padding-bottom:var(--bui-space-10)}.md\:bui-py-11{padding-top:var(--bui-space-11);padding-bottom:var(--bui-space-11)}.md\:bui-py-12{padding-top:var(--bui-space-12);padding-bottom:var(--bui-space-12)}.md\:bui-py-13{padding-top:var(--bui-space-13);padding-bottom:var(--bui-space-13)}.md\:bui-py-14{padding-top:var(--bui-space-14);padding-bottom:var(--bui-space-14)}}@media (width>=1280px){.lg\:bui-py{padding-top:var(--py-lg);padding-bottom:var(--py-lg)}.lg\:bui-py-0\.5{padding-top:var(--bui-space-0_5);padding-bottom:var(--bui-space-0_5)}.lg\:bui-py-1{padding-top:var(--bui-space-1);padding-bottom:var(--bui-space-1)}.lg\:bui-py-1\.5{padding-top:var(--bui-space-1_5);padding-bottom:var(--bui-space-1_5)}.lg\:bui-py-2{padding-top:var(--bui-space-2);padding-bottom:var(--bui-space-2)}.lg\:bui-py-3{padding-top:var(--bui-space-3);padding-bottom:var(--bui-space-3)}.lg\:bui-py-4{padding-top:var(--bui-space-4);padding-bottom:var(--bui-space-4)}.lg\:bui-py-5{padding-top:var(--bui-space-5);padding-bottom:var(--bui-space-5)}.lg\:bui-py-6{padding-top:var(--bui-space-6);padding-bottom:var(--bui-space-6)}.lg\:bui-py-7{padding-top:var(--bui-space-7);padding-bottom:var(--bui-space-7)}.lg\:bui-py-8{padding-top:var(--bui-space-8);padding-bottom:var(--bui-space-8)}.lg\:bui-py-9{padding-top:var(--bui-space-9);padding-bottom:var(--bui-space-9)}.lg\:bui-py-10{padding-top:var(--bui-space-10);padding-bottom:var(--bui-space-10)}.lg\:bui-py-11{padding-top:var(--bui-space-11);padding-bottom:var(--bui-space-11)}.lg\:bui-py-12{padding-top:var(--bui-space-12);padding-bottom:var(--bui-space-12)}.lg\:bui-py-13{padding-top:var(--bui-space-13);padding-bottom:var(--bui-space-13)}.lg\:bui-py-14{padding-top:var(--bui-space-14);padding-bottom:var(--bui-space-14)}}@media (width>=1536px){.xl\:bui-py{padding-top:var(--py-xl);padding-bottom:var(--py-xl)}.xl\:bui-py-0\.5{padding-top:var(--bui-space-0_5);padding-bottom:var(--bui-space-0_5)}.xl\:bui-py-1{padding-top:var(--bui-space-1);padding-bottom:var(--bui-space-1)}.xl\:bui-py-1\.5{padding-top:var(--bui-space-1_5);padding-bottom:var(--bui-space-1_5)}.xl\:bui-py-2{padding-top:var(--bui-space-2);padding-bottom:var(--bui-space-2)}.xl\:bui-py-3{padding-top:var(--bui-space-3);padding-bottom:var(--bui-space-3)}.xl\:bui-py-4{padding-top:var(--bui-space-4);padding-bottom:var(--bui-space-4)}.xl\:bui-py-5{padding-top:var(--bui-space-5);padding-bottom:var(--bui-space-5)}.xl\:bui-py-6{padding-top:var(--bui-space-6);padding-bottom:var(--bui-space-6)}.xl\:bui-py-7{padding-top:var(--bui-space-7);padding-bottom:var(--bui-space-7)}.xl\:bui-py-8{padding-top:var(--bui-space-8);padding-bottom:var(--bui-space-8)}.xl\:bui-py-9{padding-top:var(--bui-space-9);padding-bottom:var(--bui-space-9)}.xl\:bui-py-10{padding-top:var(--bui-space-10);padding-bottom:var(--bui-space-10)}.xl\:bui-py-11{padding-top:var(--bui-space-11);padding-bottom:var(--bui-space-11)}.xl\:bui-py-12{padding-top:var(--bui-space-12);padding-bottom:var(--bui-space-12)}.xl\:bui-py-13{padding-top:var(--bui-space-13);padding-bottom:var(--bui-space-13)}.xl\:bui-py-14{padding-top:var(--bui-space-14);padding-bottom:var(--bui-space-14)}}.bui-px{padding-left:var(--px);padding-right:var(--px)}.bui-px-0\.5{padding-left:var(--bui-space-0_5);padding-right:var(--bui-space-0_5)}.bui-px-1{padding-left:var(--bui-space-1);padding-right:var(--bui-space-1)}.bui-px-1\.5{padding-left:var(--bui-space-1_5);padding-right:var(--bui-space-1_5)}.bui-px-2{padding-left:var(--bui-space-2);padding-right:var(--bui-space-2)}.bui-px-3{padding-left:var(--bui-space-3);padding-right:var(--bui-space-3)}.bui-px-4{padding-left:var(--bui-space-4);padding-right:var(--bui-space-4)}.bui-px-5{padding-left:var(--bui-space-5);padding-right:var(--bui-space-5)}.bui-px-6{padding-left:var(--bui-space-6);padding-right:var(--bui-space-6)}.bui-px-7{padding-left:var(--bui-space-7);padding-right:var(--bui-space-7)}.bui-px-8{padding-left:var(--bui-space-8);padding-right:var(--bui-space-8)}.bui-px-9{padding-left:var(--bui-space-9);padding-right:var(--bui-space-9)}.bui-px-10{padding-left:var(--bui-space-10);padding-right:var(--bui-space-10)}.bui-px-11{padding-left:var(--bui-space-11);padding-right:var(--bui-space-11)}.bui-px-12{padding-left:var(--bui-space-12);padding-right:var(--bui-space-12)}.bui-px-13{padding-left:var(--bui-space-13);padding-right:var(--bui-space-13)}.bui-px-14{padding-left:var(--bui-space-14);padding-right:var(--bui-space-14)}@media (width>=640px){.xs\:bui-px{padding-left:var(--px-xs);padding-right:var(--px-xs)}.xs\:bui-px-0\.5{padding-left:var(--bui-space-0_5);padding-right:var(--bui-space-0_5)}.xs\:bui-px-1{padding-left:var(--bui-space-1);padding-right:var(--bui-space-1)}.xs\:bui-px-1\.5{padding-left:var(--bui-space-1_5);padding-right:var(--bui-space-1_5)}.xs\:bui-px-2{padding-left:var(--bui-space-2);padding-right:var(--bui-space-2)}.xs\:bui-px-3{padding-left:var(--bui-space-3);padding-right:var(--bui-space-3)}.xs\:bui-px-4{padding-left:var(--bui-space-4);padding-right:var(--bui-space-4)}.xs\:bui-px-5{padding-left:var(--bui-space-5);padding-right:var(--bui-space-5)}.xs\:bui-px-6{padding-left:var(--bui-space-6);padding-right:var(--bui-space-6)}.xs\:bui-px-7{padding-left:var(--bui-space-7);padding-right:var(--bui-space-7)}.xs\:bui-px-8{padding-left:var(--bui-space-8);padding-right:var(--bui-space-8)}.xs\:bui-px-9{padding-left:var(--bui-space-9);padding-right:var(--bui-space-9)}.xs\:bui-px-10{padding-left:var(--bui-space-10);padding-right:var(--bui-space-10)}.xs\:bui-px-11{padding-left:var(--bui-space-11);padding-right:var(--bui-space-11)}.xs\:bui-px-12{padding-left:var(--bui-space-12);padding-right:var(--bui-space-12)}.xs\:bui-px-13{padding-left:var(--bui-space-13);padding-right:var(--bui-space-13)}.xs\:bui-px-14{padding-left:var(--bui-space-14);padding-right:var(--bui-space-14)}}@media (width>=768px){.sm\:bui-px{padding-left:var(--px-sm);padding-right:var(--px-sm)}.sm\:bui-px-0\.5{padding-left:var(--bui-space-0_5);padding-right:var(--bui-space-0_5)}.sm\:bui-px-1{padding-left:var(--bui-space-1);padding-right:var(--bui-space-1)}.sm\:bui-px-1\.5{padding-left:var(--bui-space-1_5);padding-right:var(--bui-space-1_5)}.sm\:bui-px-2{padding-left:var(--bui-space-2);padding-right:var(--bui-space-2)}.sm\:bui-px-3{padding-left:var(--bui-space-3);padding-right:var(--bui-space-3)}.sm\:bui-px-4{padding-left:var(--bui-space-4);padding-right:var(--bui-space-4)}.sm\:bui-px-5{padding-left:var(--bui-space-5);padding-right:var(--bui-space-5)}.sm\:bui-px-6{padding-left:var(--bui-space-6);padding-right:var(--bui-space-6)}.sm\:bui-px-7{padding-left:var(--bui-space-7);padding-right:var(--bui-space-7)}.sm\:bui-px-8{padding-left:var(--bui-space-8);padding-right:var(--bui-space-8)}.sm\:bui-px-9{padding-left:var(--bui-space-9);padding-right:var(--bui-space-9)}.sm\:bui-px-10{padding-left:var(--bui-space-10);padding-right:var(--bui-space-10)}.sm\:bui-px-11{padding-left:var(--bui-space-11);padding-right:var(--bui-space-11)}.sm\:bui-px-12{padding-left:var(--bui-space-12);padding-right:var(--bui-space-12)}.sm\:bui-px-13{padding-left:var(--bui-space-13);padding-right:var(--bui-space-13)}.sm\:bui-px-14{padding-left:var(--bui-space-14);padding-right:var(--bui-space-14)}}@media (width>=1024px){.md\:bui-px{padding-left:var(--px-md);padding-right:var(--px-md)}.md\:bui-px-0\.5{padding-left:var(--bui-space-0_5);padding-right:var(--bui-space-0_5)}.md\:bui-px-1{padding-left:var(--bui-space-1);padding-right:var(--bui-space-1)}.md\:bui-px-1\.5{padding-left:var(--bui-space-1_5);padding-right:var(--bui-space-1_5)}.md\:bui-px-2{padding-left:var(--bui-space-2);padding-right:var(--bui-space-2)}.md\:bui-px-3{padding-left:var(--bui-space-3);padding-right:var(--bui-space-3)}.md\:bui-px-4{padding-left:var(--bui-space-4);padding-right:var(--bui-space-4)}.md\:bui-px-5{padding-left:var(--bui-space-5);padding-right:var(--bui-space-5)}.md\:bui-px-6{padding-left:var(--bui-space-6);padding-right:var(--bui-space-6)}.md\:bui-px-7{padding-left:var(--bui-space-7);padding-right:var(--bui-space-7)}.md\:bui-px-8{padding-left:var(--bui-space-8);padding-right:var(--bui-space-8)}.md\:bui-px-9{padding-left:var(--bui-space-9);padding-right:var(--bui-space-9)}.md\:bui-px-10{padding-left:var(--bui-space-10);padding-right:var(--bui-space-10)}.md\:bui-px-11{padding-left:var(--bui-space-11);padding-right:var(--bui-space-11)}.md\:bui-px-12{padding-left:var(--bui-space-12);padding-right:var(--bui-space-12)}.md\:bui-px-13{padding-left:var(--bui-space-13);padding-right:var(--bui-space-13)}.md\:bui-px-14{padding-left:var(--bui-space-14);padding-right:var(--bui-space-14)}}@media (width>=1280px){.lg\:bui-px{padding-left:var(--px-lg);padding-right:var(--px-lg)}.lg\:bui-px-0\.5{padding-left:var(--bui-space-0_5);padding-right:var(--bui-space-0_5)}.lg\:bui-px-1{padding-left:var(--bui-space-1);padding-right:var(--bui-space-1)}.lg\:bui-px-1\.5{padding-left:var(--bui-space-1_5);padding-right:var(--bui-space-1_5)}.lg\:bui-px-2{padding-left:var(--bui-space-2);padding-right:var(--bui-space-2)}.lg\:bui-px-3{padding-left:var(--bui-space-3);padding-right:var(--bui-space-3)}.lg\:bui-px-4{padding-left:var(--bui-space-4);padding-right:var(--bui-space-4)}.lg\:bui-px-5{padding-left:var(--bui-space-5);padding-right:var(--bui-space-5)}.lg\:bui-px-6{padding-left:var(--bui-space-6);padding-right:var(--bui-space-6)}.lg\:bui-px-7{padding-left:var(--bui-space-7);padding-right:var(--bui-space-7)}.lg\:bui-px-8{padding-left:var(--bui-space-8);padding-right:var(--bui-space-8)}.lg\:bui-px-9{padding-left:var(--bui-space-9);padding-right:var(--bui-space-9)}.lg\:bui-px-10{padding-left:var(--bui-space-10);padding-right:var(--bui-space-10)}.lg\:bui-px-11{padding-left:var(--bui-space-11);padding-right:var(--bui-space-11)}.lg\:bui-px-12{padding-left:var(--bui-space-12);padding-right:var(--bui-space-12)}.lg\:bui-px-13{padding-left:var(--bui-space-13);padding-right:var(--bui-space-13)}.lg\:bui-px-14{padding-left:var(--bui-space-14);padding-right:var(--bui-space-14)}}@media (width>=1536px){.xl\:bui-px{padding-left:var(--px-xl);padding-right:var(--px-xl)}.xl\:bui-px-0\.5{padding-left:var(--bui-space-0_5);padding-right:var(--bui-space-0_5)}.xl\:bui-px-1{padding-left:var(--bui-space-1);padding-right:var(--bui-space-1)}.xl\:bui-px-1\.5{padding-left:var(--bui-space-1_5);padding-right:var(--bui-space-1_5)}.xl\:bui-px-2{padding-left:var(--bui-space-2);padding-right:var(--bui-space-2)}.xl\:bui-px-3{padding-left:var(--bui-space-3);padding-right:var(--bui-space-3)}.xl\:bui-px-4{padding-left:var(--bui-space-4);padding-right:var(--bui-space-4)}.xl\:bui-px-5{padding-left:var(--bui-space-5);padding-right:var(--bui-space-5)}.xl\:bui-px-6{padding-left:var(--bui-space-6);padding-right:var(--bui-space-6)}.xl\:bui-px-7{padding-left:var(--bui-space-7);padding-right:var(--bui-space-7)}.xl\:bui-px-8{padding-left:var(--bui-space-8);padding-right:var(--bui-space-8)}.xl\:bui-px-9{padding-left:var(--bui-space-9);padding-right:var(--bui-space-9)}.xl\:bui-px-10{padding-left:var(--bui-space-10);padding-right:var(--bui-space-10)}.xl\:bui-px-11{padding-left:var(--bui-space-11);padding-right:var(--bui-space-11)}.xl\:bui-px-12{padding-left:var(--bui-space-12);padding-right:var(--bui-space-12)}.xl\:bui-px-13{padding-left:var(--bui-space-13);padding-right:var(--bui-space-13)}.xl\:bui-px-14{padding-left:var(--bui-space-14);padding-right:var(--bui-space-14)}}.bui-m{margin:var(--m)}.bui-m-0\.5{margin:var(--bui-space-0_5)}.bui-m-1{margin:var(--bui-space-1)}.bui-m-1\.5{margin:var(--bui-space-1_5)}.bui-m-2{margin:var(--bui-space-2)}.bui-m-3{margin:var(--bui-space-3)}.bui-m-4{margin:var(--bui-space-4)}.bui-m-5{margin:var(--bui-space-5)}.bui-m-6{margin:var(--bui-space-6)}.bui-m-7{margin:var(--bui-space-7)}.bui-m-8{margin:var(--bui-space-8)}.bui-m-9{margin:var(--bui-space-9)}.bui-m-10{margin:var(--bui-space-10)}.bui-m-11{margin:var(--bui-space-11)}.bui-m-12{margin:var(--bui-space-12)}.bui-m-13{margin:var(--bui-space-13)}.bui-m-14{margin:var(--bui-space-14)}@media (width>=640px){.xs\:bui-m{margin:var(--p-xs)}.xs\:bui-m-0\.5{margin:var(--bui-space-0_5)}.xs\:bui-m-1{margin:var(--bui-space-1)}.xs\:bui-m-1\.5{margin:var(--bui-space-1_5)}.xs\:bui-m-2{margin:var(--bui-space-2)}.xs\:bui-m-3{margin:var(--bui-space-3)}.xs\:bui-m-4{margin:var(--bui-space-4)}.xs\:bui-m-5{margin:var(--bui-space-5)}.xs\:bui-m-6{margin:var(--bui-space-6)}.xs\:bui-m-7{margin:var(--bui-space-7)}.xs\:bui-m-8{margin:var(--bui-space-8)}.xs\:bui-m-9{margin:var(--bui-space-9)}.xs\:bui-m-10{margin:var(--bui-space-10)}.xs\:bui-m-11{margin:var(--bui-space-11)}.xs\:bui-m-12{margin:var(--bui-space-12)}.xs\:bui-m-13{margin:var(--bui-space-13)}.xs\:bui-m-14{margin:var(--bui-space-14)}}@media (width>=768px){.sm\:bui-m{margin:var(--p-sm)}.sm\:bui-m-0\.5{margin:var(--bui-space-0_5)}.sm\:bui-m-1{margin:var(--bui-space-1)}.sm\:bui-m-1\.5{margin:var(--bui-space-1_5)}.sm\:bui-m-2{margin:var(--bui-space-2)}.sm\:bui-m-3{margin:var(--bui-space-3)}.sm\:bui-m-4{margin:var(--bui-space-4)}.sm\:bui-m-5{margin:var(--bui-space-5)}.sm\:bui-m-6{margin:var(--bui-space-6)}.sm\:bui-m-7{margin:var(--bui-space-7)}.sm\:bui-m-8{margin:var(--bui-space-8)}.sm\:bui-m-9{margin:var(--bui-space-9)}.sm\:bui-m-10{margin:var(--bui-space-10)}.sm\:bui-m-11{margin:var(--bui-space-11)}.sm\:bui-m-12{margin:var(--bui-space-12)}.sm\:bui-m-13{margin:var(--bui-space-13)}.sm\:bui-m-14{margin:var(--bui-space-14)}}@media (width>=1024px){.md\:bui-m{margin:var(--p-md)}.md\:bui-m-0\.5{margin:var(--bui-space-0_5)}.md\:bui-m-1{margin:var(--bui-space-1)}.md\:bui-m-1\.5{margin:var(--bui-space-1_5)}.md\:bui-m-2{margin:var(--bui-space-2)}.md\:bui-m-3{margin:var(--bui-space-3)}.md\:bui-m-4{margin:var(--bui-space-4)}.md\:bui-m-5{margin:var(--bui-space-5)}.md\:bui-m-6{margin:var(--bui-space-6)}.md\:bui-m-7{margin:var(--bui-space-7)}.md\:bui-m-8{margin:var(--bui-space-8)}.md\:bui-m-9{margin:var(--bui-space-9)}.md\:bui-m-10{margin:var(--bui-space-10)}.md\:bui-m-11{margin:var(--bui-space-11)}.md\:bui-m-12{margin:var(--bui-space-12)}.md\:bui-m-13{margin:var(--bui-space-13)}.md\:bui-m-14{margin:var(--bui-space-14)}}@media (width>=1280px){.lg\:bui-m{margin:var(--p-lg)}.lg\:bui-m-0\.5{margin:var(--bui-space-0_5)}.lg\:bui-m-1{margin:var(--bui-space-1)}.lg\:bui-m-1\.5{margin:var(--bui-space-1_5)}.lg\:bui-m-2{margin:var(--bui-space-2)}.lg\:bui-m-3{margin:var(--bui-space-3)}.lg\:bui-m-4{margin:var(--bui-space-4)}.lg\:bui-m-5{margin:var(--bui-space-5)}.lg\:bui-m-6{margin:var(--bui-space-6)}.lg\:bui-m-7{margin:var(--bui-space-7)}.lg\:bui-m-8{margin:var(--bui-space-8)}.lg\:bui-m-9{margin:var(--bui-space-9)}.lg\:bui-m-10{margin:var(--bui-space-10)}.lg\:bui-m-11{margin:var(--bui-space-11)}.lg\:bui-m-12{margin:var(--bui-space-12)}.lg\:bui-m-13{margin:var(--bui-space-13)}.lg\:bui-m-14{margin:var(--bui-space-14)}}@media (width>=1536px){.xl\:bui-m{margin:var(--p-xl)}.xl\:bui-m-0\.5{margin:var(--bui-space-0_5)}.xl\:bui-m-1{margin:var(--bui-space-1)}.xl\:bui-m-1\.5{margin:var(--bui-space-1_5)}.xl\:bui-m-2{margin:var(--bui-space-2)}.xl\:bui-m-3{margin:var(--bui-space-3)}.xl\:bui-m-4{margin:var(--bui-space-4)}.xl\:bui-m-5{margin:var(--bui-space-5)}.xl\:bui-m-6{margin:var(--bui-space-6)}.xl\:bui-m-7{margin:var(--bui-space-7)}.xl\:bui-m-8{margin:var(--bui-space-8)}.xl\:bui-m-9{margin:var(--bui-space-9)}.xl\:bui-m-10{margin:var(--bui-space-10)}.xl\:bui-m-11{margin:var(--bui-space-11)}.xl\:bui-m-12{margin:var(--bui-space-12)}.xl\:bui-m-13{margin:var(--bui-space-13)}.xl\:bui-m-14{margin:var(--bui-space-14)}}.bui-ml{margin-left:var(--ml)}.bui-ml-0\.5{margin-left:var(--bui-space-0_5)}.bui-ml-1{margin-left:var(--bui-space-1)}.bui-ml-1\.5{margin-left:var(--bui-space-1_5)}.bui-ml-2{margin-left:var(--bui-space-2)}.bui-ml-3{margin-left:var(--bui-space-3)}.bui-ml-4{margin-left:var(--bui-space-4)}.bui-ml-5{margin-left:var(--bui-space-5)}.bui-ml-6{margin-left:var(--bui-space-6)}.bui-ml-7{margin-left:var(--bui-space-7)}.bui-ml-8{margin-left:var(--bui-space-8)}.bui-ml-9{margin-left:var(--bui-space-9)}.bui-ml-10{margin-left:var(--bui-space-10)}.bui-ml-11{margin-left:var(--bui-space-11)}.bui-ml-12{margin-left:var(--bui-space-12)}.bui-ml-13{margin-left:var(--bui-space-13)}.bui-ml-14{margin-left:var(--bui-space-14)}@media (width>=640px){.xs\:bui-ml{margin-left:var(--ml-xs)}.xs\:bui-ml-0\.5{margin-left:var(--bui-space-0_5)}.xs\:bui-ml-1{margin-left:var(--bui-space-1)}.xs\:bui-ml-1\.5{margin-left:var(--bui-space-1_5)}.xs\:bui-ml-2{margin-left:var(--bui-space-2)}.xs\:bui-ml-3{margin-left:var(--bui-space-3)}.xs\:bui-ml-4{margin-left:var(--bui-space-4)}.xs\:bui-ml-5{margin-left:var(--bui-space-5)}.xs\:bui-ml-6{margin-left:var(--bui-space-6)}.xs\:bui-ml-7{margin-left:var(--bui-space-7)}.xs\:bui-ml-8{margin-left:var(--bui-space-8)}.xs\:bui-ml-9{margin-left:var(--bui-space-9)}.xs\:bui-ml-10{margin-left:var(--bui-space-10)}.xs\:bui-ml-11{margin-left:var(--bui-space-11)}.xs\:bui-ml-12{margin-left:var(--bui-space-12)}.xs\:bui-ml-13{margin-left:var(--bui-space-13)}.xs\:bui-ml-14{margin-left:var(--bui-space-14)}}@media (width>=768px){.sm\:bui-ml{margin-left:var(--ml-sm)}.sm\:bui-ml-0\.5{margin-left:var(--bui-space-0_5)}.sm\:bui-ml-1{margin-left:var(--bui-space-1)}.sm\:bui-ml-1\.5{margin-left:var(--bui-space-1_5)}.sm\:bui-ml-2{margin-left:var(--bui-space-2)}.sm\:bui-ml-3{margin-left:var(--bui-space-3)}.sm\:bui-ml-4{margin-left:var(--bui-space-4)}.sm\:bui-ml-5{margin-left:var(--bui-space-5)}.sm\:bui-ml-6{margin-left:var(--bui-space-6)}.sm\:bui-ml-7{margin-left:var(--bui-space-7)}.sm\:bui-ml-8{margin-left:var(--bui-space-8)}.sm\:bui-ml-9{margin-left:var(--bui-space-9)}.sm\:bui-ml-10{margin-left:var(--bui-space-10)}.sm\:bui-ml-11{margin-left:var(--bui-space-11)}.sm\:bui-ml-12{margin-left:var(--bui-space-12)}.sm\:bui-ml-13{margin-left:var(--bui-space-13)}.sm\:bui-ml-14{margin-left:var(--bui-space-14)}}@media (width>=1024px){.md\:bui-ml{margin-left:var(--ml-md)}.md\:bui-ml-0\.5{margin-left:var(--bui-space-0_5)}.md\:bui-ml-1{margin-left:var(--bui-space-1)}.md\:bui-ml-1\.5{margin-left:var(--bui-space-1_5)}.md\:bui-ml-2{margin-left:var(--bui-space-2)}.md\:bui-ml-3{margin-left:var(--bui-space-3)}.md\:bui-ml-4{margin-left:var(--bui-space-4)}.md\:bui-ml-5{margin-left:var(--bui-space-5)}.md\:bui-ml-6{margin-left:var(--bui-space-6)}.md\:bui-ml-7{margin-left:var(--bui-space-7)}.md\:bui-ml-8{margin-left:var(--bui-space-8)}.md\:bui-ml-9{margin-left:var(--bui-space-9)}.md\:bui-ml-10{margin-left:var(--bui-space-10)}.md\:bui-ml-11{margin-left:var(--bui-space-11)}.md\:bui-ml-12{margin-left:var(--bui-space-12)}.md\:bui-ml-13{margin-left:var(--bui-space-13)}.md\:bui-ml-14{margin-left:var(--bui-space-14)}}@media (width>=1280px){.lg\:bui-ml{margin-left:var(--ml-lg)}.lg\:bui-ml-0\.5{margin-left:var(--bui-space-0_5)}.lg\:bui-ml-1{margin-left:var(--bui-space-1)}.lg\:bui-ml-1\.5{margin-left:var(--bui-space-1_5)}.lg\:bui-ml-2{margin-left:var(--bui-space-2)}.lg\:bui-ml-3{margin-left:var(--bui-space-3)}.lg\:bui-ml-4{margin-left:var(--bui-space-4)}.lg\:bui-ml-5{margin-left:var(--bui-space-5)}.lg\:bui-ml-6{margin-left:var(--bui-space-6)}.lg\:bui-ml-7{margin-left:var(--bui-space-7)}.lg\:bui-ml-8{margin-left:var(--bui-space-8)}.lg\:bui-ml-9{margin-left:var(--bui-space-9)}.lg\:bui-ml-10{margin-left:var(--bui-space-10)}.lg\:bui-ml-11{margin-left:var(--bui-space-11)}.lg\:bui-ml-12{margin-left:var(--bui-space-12)}.lg\:bui-ml-13{margin-left:var(--bui-space-13)}.lg\:bui-ml-14{margin-left:var(--bui-space-14)}}@media (width>=1536px){.xl\:bui-ml{margin-left:var(--ml-xl)}.xl\:bui-ml-0\.5{margin-left:var(--bui-space-0_5)}.xl\:bui-ml-1{margin-left:var(--bui-space-1)}.xl\:bui-ml-1\.5{margin-left:var(--bui-space-1_5)}.xl\:bui-ml-2{margin-left:var(--bui-space-2)}.xl\:bui-ml-3{margin-left:var(--bui-space-3)}.xl\:bui-ml-4{margin-left:var(--bui-space-4)}.xl\:bui-ml-5{margin-left:var(--bui-space-5)}.xl\:bui-ml-6{margin-left:var(--bui-space-6)}.xl\:bui-ml-7{margin-left:var(--bui-space-7)}.xl\:bui-ml-8{margin-left:var(--bui-space-8)}.xl\:bui-ml-9{margin-left:var(--bui-space-9)}.xl\:bui-ml-10{margin-left:var(--bui-space-10)}.xl\:bui-ml-11{margin-left:var(--bui-space-11)}.xl\:bui-ml-12{margin-left:var(--bui-space-12)}.xl\:bui-ml-13{margin-left:var(--bui-space-13)}.xl\:bui-ml-14{margin-left:var(--bui-space-14)}}.bui-mr{margin-right:var(--mr)}.bui-mr-0\.5{margin-right:var(--bui-space-0_5)}.bui-mr-1{margin-right:var(--bui-space-1)}.bui-mr-1\.5{margin-right:var(--bui-space-1_5)}.bui-mr-2{margin-right:var(--bui-space-2)}.bui-mr-3{margin-right:var(--bui-space-3)}.bui-mr-4{margin-right:var(--bui-space-4)}.bui-mr-5{margin-right:var(--bui-space-5)}.bui-mr-6{margin-right:var(--bui-space-6)}.bui-mr-7{margin-right:var(--bui-space-7)}.bui-mr-8{margin-right:var(--bui-space-8)}.bui-mr-9{margin-right:var(--bui-space-9)}.bui-mr-10{margin-right:var(--bui-space-10)}.bui-mr-11{margin-right:var(--bui-space-11)}.bui-mr-12{margin-right:var(--bui-space-12)}.bui-mr-13{margin-right:var(--bui-space-13)}.bui-mr-14{margin-right:var(--bui-space-14)}@media (width>=640px){.xs\:bui-mr{margin-right:var(--mr-xs)}.xs\:bui-mr-0\.5{margin-right:var(--bui-space-0_5)}.xs\:bui-mr-1{margin-right:var(--bui-space-1)}.xs\:bui-mr-1\.5{margin-right:var(--bui-space-1_5)}.xs\:bui-mr-2{margin-right:var(--bui-space-2)}.xs\:bui-mr-3{margin-right:var(--bui-space-3)}.xs\:bui-mr-4{margin-right:var(--bui-space-4)}.xs\:bui-mr-5{margin-right:var(--bui-space-5)}.xs\:bui-mr-6{margin-right:var(--bui-space-6)}.xs\:bui-mr-7{margin-right:var(--bui-space-7)}.xs\:bui-mr-8{margin-right:var(--bui-space-8)}.xs\:bui-mr-9{margin-right:var(--bui-space-9)}.xs\:bui-mr-10{margin-right:var(--bui-space-10)}.xs\:bui-mr-11{margin-right:var(--bui-space-11)}.xs\:bui-mr-12{margin-right:var(--bui-space-12)}.xs\:bui-mr-13{margin-right:var(--bui-space-13)}.xs\:bui-mr-14{margin-right:var(--bui-space-14)}}@media (width>=768px){.sm\:bui-mr{margin-right:var(--mr-sm)}.sm\:bui-mr-0\.5{margin-right:var(--bui-space-0_5)}.sm\:bui-mr-1{margin-right:var(--bui-space-1)}.sm\:bui-mr-1\.5{margin-right:var(--bui-space-1_5)}.sm\:bui-mr-2{margin-right:var(--bui-space-2)}.sm\:bui-mr-3{margin-right:var(--bui-space-3)}.sm\:bui-mr-4{margin-right:var(--bui-space-4)}.sm\:bui-mr-5{margin-right:var(--bui-space-5)}.sm\:bui-mr-6{margin-right:var(--bui-space-6)}.sm\:bui-mr-7{margin-right:var(--bui-space-7)}.sm\:bui-mr-8{margin-right:var(--bui-space-8)}.sm\:bui-mr-9{margin-right:var(--bui-space-9)}.sm\:bui-mr-10{margin-right:var(--bui-space-10)}.sm\:bui-mr-11{margin-right:var(--bui-space-11)}.sm\:bui-mr-12{margin-right:var(--bui-space-12)}.sm\:bui-mr-13{margin-right:var(--bui-space-13)}.sm\:bui-mr-14{margin-right:var(--bui-space-14)}}@media (width>=1024px){.md\:bui-mr{margin-right:var(--mr-md)}.md\:bui-mr-0\.5{margin-right:var(--bui-space-0_5)}.md\:bui-mr-1{margin-right:var(--bui-space-1)}.md\:bui-mr-1\.5{margin-right:var(--bui-space-1_5)}.md\:bui-mr-2{margin-right:var(--bui-space-2)}.md\:bui-mr-3{margin-right:var(--bui-space-3)}.md\:bui-mr-4{margin-right:var(--bui-space-4)}.md\:bui-mr-5{margin-right:var(--bui-space-5)}.md\:bui-mr-6{margin-right:var(--bui-space-6)}.md\:bui-mr-7{margin-right:var(--bui-space-7)}.md\:bui-mr-8{margin-right:var(--bui-space-8)}.md\:bui-mr-9{margin-right:var(--bui-space-9)}.md\:bui-mr-10{margin-right:var(--bui-space-10)}.md\:bui-mr-11{margin-right:var(--bui-space-11)}.md\:bui-mr-12{margin-right:var(--bui-space-12)}.md\:bui-mr-13{margin-right:var(--bui-space-13)}.md\:bui-mr-14{margin-right:var(--bui-space-14)}}@media (width>=1280px){.lg\:bui-mr{margin-right:var(--mr-lg)}.lg\:bui-mr-0\.5{margin-right:var(--bui-space-0_5)}.lg\:bui-mr-1{margin-right:var(--bui-space-1)}.lg\:bui-mr-1\.5{margin-right:var(--bui-space-1_5)}.lg\:bui-mr-2{margin-right:var(--bui-space-2)}.lg\:bui-mr-3{margin-right:var(--bui-space-3)}.lg\:bui-mr-4{margin-right:var(--bui-space-4)}.lg\:bui-mr-5{margin-right:var(--bui-space-5)}.lg\:bui-mr-6{margin-right:var(--bui-space-6)}.lg\:bui-mr-7{margin-right:var(--bui-space-7)}.lg\:bui-mr-8{margin-right:var(--bui-space-8)}.lg\:bui-mr-9{margin-right:var(--bui-space-9)}.lg\:bui-mr-10{margin-right:var(--bui-space-10)}.lg\:bui-mr-11{margin-right:var(--bui-space-11)}.lg\:bui-mr-12{margin-right:var(--bui-space-12)}.lg\:bui-mr-13{margin-right:var(--bui-space-13)}.lg\:bui-mr-14{margin-right:var(--bui-space-14)}}@media (width>=1536px){.xl\:bui-mr{margin-right:var(--mr-xl)}.xl\:bui-mr-0\.5{margin-right:var(--bui-space-0_5)}.xl\:bui-mr-1{margin-right:var(--bui-space-1)}.xl\:bui-mr-1\.5{margin-right:var(--bui-space-1_5)}.xl\:bui-mr-2{margin-right:var(--bui-space-2)}.xl\:bui-mr-3{margin-right:var(--bui-space-3)}.xl\:bui-mr-4{margin-right:var(--bui-space-4)}.xl\:bui-mr-5{margin-right:var(--bui-space-5)}.xl\:bui-mr-6{margin-right:var(--bui-space-6)}.xl\:bui-mr-7{margin-right:var(--bui-space-7)}.xl\:bui-mr-8{margin-right:var(--bui-space-8)}.xl\:bui-mr-9{margin-right:var(--bui-space-9)}.xl\:bui-mr-10{margin-right:var(--bui-space-10)}.xl\:bui-mr-11{margin-right:var(--bui-space-11)}.xl\:bui-mr-12{margin-right:var(--bui-space-12)}.xl\:bui-mr-13{margin-right:var(--bui-space-13)}.xl\:bui-mr-14{margin-right:var(--bui-space-14)}}.bui-mt{margin-top:var(--mt)}.bui-mt-0\.5{margin-top:var(--bui-space-0_5)}.bui-mt-1{margin-top:var(--bui-space-1)}.bui-mt-1\.5{margin-top:var(--bui-space-1_5)}.bui-mt-2{margin-top:var(--bui-space-2)}.bui-mt-3{margin-top:var(--bui-space-3)}.bui-mt-4{margin-top:var(--bui-space-4)}.bui-mt-5{margin-top:var(--bui-space-5)}.bui-mt-6{margin-top:var(--bui-space-6)}.bui-mt-7{margin-top:var(--bui-space-7)}.bui-mt-8{margin-top:var(--bui-space-8)}.bui-mt-9{margin-top:var(--bui-space-9)}.bui-mt-10{margin-top:var(--bui-space-10)}.bui-mt-11{margin-top:var(--bui-space-11)}.bui-mt-12{margin-top:var(--bui-space-12)}.bui-mt-13{margin-top:var(--bui-space-13)}.bui-mt-14{margin-top:var(--bui-space-14)}@media (width>=640px){.xs\:bui-mt{margin-top:var(--mt-xs)}.xs\:bui-mt-0\.5{margin-top:var(--bui-space-0_5)}.xs\:bui-mt-1{margin-top:var(--bui-space-1)}.xs\:bui-mt-1\.5{margin-top:var(--bui-space-1_5)}.xs\:bui-mt-2{margin-top:var(--bui-space-2)}.xs\:bui-mt-3{margin-top:var(--bui-space-3)}.xs\:bui-mt-4{margin-top:var(--bui-space-4)}.xs\:bui-mt-5{margin-top:var(--bui-space-5)}.xs\:bui-mt-6{margin-top:var(--bui-space-6)}.xs\:bui-mt-7{margin-top:var(--bui-space-7)}.xs\:bui-mt-8{margin-top:var(--bui-space-8)}.xs\:bui-mt-9{margin-top:var(--bui-space-9)}.xs\:bui-mt-10{margin-top:var(--bui-space-10)}.xs\:bui-mt-11{margin-top:var(--bui-space-11)}.xs\:bui-mt-12{margin-top:var(--bui-space-12)}.xs\:bui-mt-13{margin-top:var(--bui-space-13)}.xs\:bui-mt-14{margin-top:var(--bui-space-14)}}@media (width>=768px){.sm\:bui-mt{margin-top:var(--mt-sm)}.sm\:bui-mt-0\.5{margin-top:var(--bui-space-0_5)}.sm\:bui-mt-1{margin-top:var(--bui-space-1)}.sm\:bui-mt-1\.5{margin-top:var(--bui-space-1_5)}.sm\:bui-mt-2{margin-top:var(--bui-space-2)}.sm\:bui-mt-3{margin-top:var(--bui-space-3)}.sm\:bui-mt-4{margin-top:var(--bui-space-4)}.sm\:bui-mt-5{margin-top:var(--bui-space-5)}.sm\:bui-mt-6{margin-top:var(--bui-space-6)}.sm\:bui-mt-7{margin-top:var(--bui-space-7)}.sm\:bui-mt-8{margin-top:var(--bui-space-8)}.sm\:bui-mt-9{margin-top:var(--bui-space-9)}.sm\:bui-mt-10{margin-top:var(--bui-space-10)}.sm\:bui-mt-11{margin-top:var(--bui-space-11)}.sm\:bui-mt-12{margin-top:var(--bui-space-12)}.sm\:bui-mt-13{margin-top:var(--bui-space-13)}.sm\:bui-mt-14{margin-top:var(--bui-space-14)}}@media (width>=1024px){.md\:bui-mt{margin-top:var(--mt-md)}.md\:bui-mt-0\.5{margin-top:var(--bui-space-0_5)}.md\:bui-mt-1{margin-top:var(--bui-space-1)}.md\:bui-mt-1\.5{margin-top:var(--bui-space-1_5)}.md\:bui-mt-2{margin-top:var(--bui-space-2)}.md\:bui-mt-3{margin-top:var(--bui-space-3)}.md\:bui-mt-4{margin-top:var(--bui-space-4)}.md\:bui-mt-5{margin-top:var(--bui-space-5)}.md\:bui-mt-6{margin-top:var(--bui-space-6)}.md\:bui-mt-7{margin-top:var(--bui-space-7)}.md\:bui-mt-8{margin-top:var(--bui-space-8)}.md\:bui-mt-9{margin-top:var(--bui-space-9)}.md\:bui-mt-10{margin-top:var(--bui-space-10)}.md\:bui-mt-11{margin-top:var(--bui-space-11)}.md\:bui-mt-12{margin-top:var(--bui-space-12)}.md\:bui-mt-13{margin-top:var(--bui-space-13)}.md\:bui-mt-14{margin-top:var(--bui-space-14)}}@media (width>=1280px){.lg\:bui-mt{margin-top:var(--mt-lg)}.lg\:bui-mt-0\.5{margin-top:var(--bui-space-0_5)}.lg\:bui-mt-1{margin-top:var(--bui-space-1)}.lg\:bui-mt-1\.5{margin-top:var(--bui-space-1_5)}.lg\:bui-mt-2{margin-top:var(--bui-space-2)}.lg\:bui-mt-3{margin-top:var(--bui-space-3)}.lg\:bui-mt-4{margin-top:var(--bui-space-4)}.lg\:bui-mt-5{margin-top:var(--bui-space-5)}.lg\:bui-mt-6{margin-top:var(--bui-space-6)}.lg\:bui-mt-7{margin-top:var(--bui-space-7)}.lg\:bui-mt-8{margin-top:var(--bui-space-8)}.lg\:bui-mt-9{margin-top:var(--bui-space-9)}.lg\:bui-mt-10{margin-top:var(--bui-space-10)}.lg\:bui-mt-11{margin-top:var(--bui-space-11)}.lg\:bui-mt-12{margin-top:var(--bui-space-12)}.lg\:bui-mt-13{margin-top:var(--bui-space-13)}.lg\:bui-mt-14{margin-top:var(--bui-space-14)}}@media (width>=1536px){.xl\:bui-mt{margin-top:var(--mt-xl)}.xl\:bui-mt-0\.5{margin-top:var(--bui-space-0_5)}.xl\:bui-mt-1{margin-top:var(--bui-space-1)}.xl\:bui-mt-1\.5{margin-top:var(--bui-space-1_5)}.xl\:bui-mt-2{margin-top:var(--bui-space-2)}.xl\:bui-mt-3{margin-top:var(--bui-space-3)}.xl\:bui-mt-4{margin-top:var(--bui-space-4)}.xl\:bui-mt-5{margin-top:var(--bui-space-5)}.xl\:bui-mt-6{margin-top:var(--bui-space-6)}.xl\:bui-mt-7{margin-top:var(--bui-space-7)}.xl\:bui-mt-8{margin-top:var(--bui-space-8)}.xl\:bui-mt-9{margin-top:var(--bui-space-9)}.xl\:bui-mt-10{margin-top:var(--bui-space-10)}.xl\:bui-mt-11{margin-top:var(--bui-space-11)}.xl\:bui-mt-12{margin-top:var(--bui-space-12)}.xl\:bui-mt-13{margin-top:var(--bui-space-13)}.xl\:bui-mt-14{margin-top:var(--bui-space-14)}}.bui-mb{margin-bottom:var(--mb)}.bui-mb-0\.5{margin-bottom:var(--bui-space-0_5)}.bui-mb-1{margin-bottom:var(--bui-space-1)}.bui-mb-1\.5{margin-bottom:var(--bui-space-1_5)}.bui-mb-2{margin-bottom:var(--bui-space-2)}.bui-mb-3{margin-bottom:var(--bui-space-3)}.bui-mb-4{margin-bottom:var(--bui-space-4)}.bui-mb-5{margin-bottom:var(--bui-space-5)}.bui-mb-6{margin-bottom:var(--bui-space-6)}.bui-mb-7{margin-bottom:var(--bui-space-7)}.bui-mb-8{margin-bottom:var(--bui-space-8)}.bui-mb-9{margin-bottom:var(--bui-space-9)}.bui-mb-10{margin-bottom:var(--bui-space-10)}.bui-mb-11{margin-bottom:var(--bui-space-11)}.bui-mb-12{margin-bottom:var(--bui-space-12)}.bui-mb-13{margin-bottom:var(--bui-space-13)}.bui-mb-14{margin-bottom:var(--bui-space-14)}@media (width>=640px){.xs\:bui-mb{margin-bottom:var(--mb-xs)}.xs\:bui-mb-0\.5{margin-bottom:var(--bui-space-0_5)}.xs\:bui-mb-1{margin-bottom:var(--bui-space-1)}.xs\:bui-mb-1\.5{margin-bottom:var(--bui-space-1_5)}.xs\:bui-mb-2{margin-bottom:var(--bui-space-2)}.xs\:bui-mb-3{margin-bottom:var(--bui-space-3)}.xs\:bui-mb-4{margin-bottom:var(--bui-space-4)}.xs\:bui-mb-5{margin-bottom:var(--bui-space-5)}.xs\:bui-mb-6{margin-bottom:var(--bui-space-6)}.xs\:bui-mb-7{margin-bottom:var(--bui-space-7)}.xs\:bui-mb-8{margin-bottom:var(--bui-space-8)}.xs\:bui-mb-9{margin-bottom:var(--bui-space-9)}.xs\:bui-mb-10{margin-bottom:var(--bui-space-10)}.xs\:bui-mb-11{margin-bottom:var(--bui-space-11)}.xs\:bui-mb-12{margin-bottom:var(--bui-space-12)}.xs\:bui-mb-13{margin-bottom:var(--bui-space-13)}.xs\:bui-mb-14{margin-bottom:var(--bui-space-14)}}@media (width>=768px){.sm\:bui-mb{margin-bottom:var(--mb-sm)}.sm\:bui-mb-0\.5{margin-bottom:var(--bui-space-0_5)}.sm\:bui-mb-1{margin-bottom:var(--bui-space-1)}.sm\:bui-mb-1\.5{margin-bottom:var(--bui-space-1_5)}.sm\:bui-mb-2{margin-bottom:var(--bui-space-2)}.sm\:bui-mb-3{margin-bottom:var(--bui-space-3)}.sm\:bui-mb-4{margin-bottom:var(--bui-space-4)}.sm\:bui-mb-5{margin-bottom:var(--bui-space-5)}.sm\:bui-mb-6{margin-bottom:var(--bui-space-6)}.sm\:bui-mb-7{margin-bottom:var(--bui-space-7)}.sm\:bui-mb-8{margin-bottom:var(--bui-space-8)}.sm\:bui-mb-9{margin-bottom:var(--bui-space-9)}.sm\:bui-mb-10{margin-bottom:var(--bui-space-10)}.sm\:bui-mb-11{margin-bottom:var(--bui-space-11)}.sm\:bui-mb-12{margin-bottom:var(--bui-space-12)}.sm\:bui-mb-13{margin-bottom:var(--bui-space-13)}.sm\:bui-mb-14{margin-bottom:var(--bui-space-14)}}@media (width>=1024px){.md\:bui-mb{margin-bottom:var(--mb-md)}.md\:bui-mb-0\.5{margin-bottom:var(--bui-space-0_5)}.md\:bui-mb-1{margin-bottom:var(--bui-space-1)}.md\:bui-mb-1\.5{margin-bottom:var(--bui-space-1_5)}.md\:bui-mb-2{margin-bottom:var(--bui-space-2)}.md\:bui-mb-3{margin-bottom:var(--bui-space-3)}.md\:bui-mb-4{margin-bottom:var(--bui-space-4)}.md\:bui-mb-5{margin-bottom:var(--bui-space-5)}.md\:bui-mb-6{margin-bottom:var(--bui-space-6)}.md\:bui-mb-7{margin-bottom:var(--bui-space-7)}.md\:bui-mb-8{margin-bottom:var(--bui-space-8)}.md\:bui-mb-9{margin-bottom:var(--bui-space-9)}.md\:bui-mb-10{margin-bottom:var(--bui-space-10)}.md\:bui-mb-11{margin-bottom:var(--bui-space-11)}.md\:bui-mb-12{margin-bottom:var(--bui-space-12)}.md\:bui-mb-13{margin-bottom:var(--bui-space-13)}.md\:bui-mb-14{margin-bottom:var(--bui-space-14)}}@media (width>=1280px){.lg\:bui-mb{margin-bottom:var(--mb-lg)}.lg\:bui-mb-0\.5{margin-bottom:var(--bui-space-0_5)}.lg\:bui-mb-1{margin-bottom:var(--bui-space-1)}.lg\:bui-mb-1\.5{margin-bottom:var(--bui-space-1_5)}.lg\:bui-mb-2{margin-bottom:var(--bui-space-2)}.lg\:bui-mb-3{margin-bottom:var(--bui-space-3)}.lg\:bui-mb-4{margin-bottom:var(--bui-space-4)}.lg\:bui-mb-5{margin-bottom:var(--bui-space-5)}.lg\:bui-mb-6{margin-bottom:var(--bui-space-6)}.lg\:bui-mb-7{margin-bottom:var(--bui-space-7)}.lg\:bui-mb-8{margin-bottom:var(--bui-space-8)}.lg\:bui-mb-9{margin-bottom:var(--bui-space-9)}.lg\:bui-mb-10{margin-bottom:var(--bui-space-10)}.lg\:bui-mb-11{margin-bottom:var(--bui-space-11)}.lg\:bui-mb-12{margin-bottom:var(--bui-space-12)}.lg\:bui-mb-13{margin-bottom:var(--bui-space-13)}.lg\:bui-mb-14{margin-bottom:var(--bui-space-14)}}@media (width>=1536px){.xl\:bui-mb{margin-bottom:var(--mb-xl)}.xl\:bui-mb-0\.5{margin-bottom:var(--bui-space-0_5)}.xl\:bui-mb-1{margin-bottom:var(--bui-space-1)}.xl\:bui-mb-1\.5{margin-bottom:var(--bui-space-1_5)}.xl\:bui-mb-2{margin-bottom:var(--bui-space-2)}.xl\:bui-mb-3{margin-bottom:var(--bui-space-3)}.xl\:bui-mb-4{margin-bottom:var(--bui-space-4)}.xl\:bui-mb-5{margin-bottom:var(--bui-space-5)}.xl\:bui-mb-6{margin-bottom:var(--bui-space-6)}.xl\:bui-mb-7{margin-bottom:var(--bui-space-7)}.xl\:bui-mb-8{margin-bottom:var(--bui-space-8)}.xl\:bui-mb-9{margin-bottom:var(--bui-space-9)}.xl\:bui-mb-10{margin-bottom:var(--bui-space-10)}.xl\:bui-mb-11{margin-bottom:var(--bui-space-11)}.xl\:bui-mb-12{margin-bottom:var(--bui-space-12)}.xl\:bui-mb-13{margin-bottom:var(--bui-space-13)}.xl\:bui-mb-14{margin-bottom:var(--bui-space-14)}}.bui-my{margin-top:var(--my);margin-bottom:var(--my)}.bui-my-0\.5{margin-top:var(--bui-space-0_5);margin-bottom:var(--bui-space-0_5)}.bui-my-1{margin-top:var(--bui-space-1);margin-bottom:var(--bui-space-1)}.bui-my-1\.5{margin-top:var(--bui-space-1_5);margin-bottom:var(--bui-space-1_5)}.bui-my-2{margin-top:var(--bui-space-2);margin-bottom:var(--bui-space-2)}.bui-my-3{margin-top:var(--bui-space-3);margin-bottom:var(--bui-space-3)}.bui-my-4{margin-top:var(--bui-space-4);margin-bottom:var(--bui-space-4)}.bui-my-5{margin-top:var(--bui-space-5);margin-bottom:var(--bui-space-5)}.bui-my-6{margin-top:var(--bui-space-6);margin-bottom:var(--bui-space-6)}.bui-my-7{margin-top:var(--bui-space-7);margin-bottom:var(--bui-space-7)}.bui-my-8{margin-top:var(--bui-space-8);margin-bottom:var(--bui-space-8)}.bui-my-9{margin-top:var(--bui-space-9);margin-bottom:var(--bui-space-9)}.bui-my-10{margin-top:var(--bui-space-10);margin-bottom:var(--bui-space-10)}.bui-my-11{margin-top:var(--bui-space-11);margin-bottom:var(--bui-space-11)}.bui-my-12{margin-top:var(--bui-space-12);margin-bottom:var(--bui-space-12)}.bui-my-13{margin-top:var(--bui-space-13);margin-bottom:var(--bui-space-13)}.bui-my-14{margin-top:var(--bui-space-14);margin-bottom:var(--bui-space-14)}@media (width>=640px){.xs\:bui-my{margin-top:var(--my-xs);margin-bottom:var(--my-xs)}.xs\:bui-my-0\.5{margin-top:var(--bui-space-0_5);margin-bottom:var(--bui-space-0_5)}.xs\:bui-my-1{margin-top:var(--bui-space-1);margin-bottom:var(--bui-space-1)}.xs\:bui-my-1\.5{margin-top:var(--bui-space-1_5);margin-bottom:var(--bui-space-1_5)}.xs\:bui-my-2{margin-top:var(--bui-space-2);margin-bottom:var(--bui-space-2)}.xs\:bui-my-3{margin-top:var(--bui-space-3);margin-bottom:var(--bui-space-3)}.xs\:bui-my-4{margin-top:var(--bui-space-4);margin-bottom:var(--bui-space-4)}.xs\:bui-my-5{margin-top:var(--bui-space-5);margin-bottom:var(--bui-space-5)}.xs\:bui-my-6{margin-top:var(--bui-space-6);margin-bottom:var(--bui-space-6)}.xs\:bui-my-7{margin-top:var(--bui-space-7);margin-bottom:var(--bui-space-7)}.xs\:bui-my-8{margin-top:var(--bui-space-8);margin-bottom:var(--bui-space-8)}.xs\:bui-my-9{margin-top:var(--bui-space-9);margin-bottom:var(--bui-space-9)}.xs\:bui-my-10{margin-top:var(--bui-space-10);margin-bottom:var(--bui-space-10)}.xs\:bui-my-11{margin-top:var(--bui-space-11);margin-bottom:var(--bui-space-11)}.xs\:bui-my-12{margin-top:var(--bui-space-12);margin-bottom:var(--bui-space-12)}.xs\:bui-my-13{margin-top:var(--bui-space-13);margin-bottom:var(--bui-space-13)}.xs\:bui-my-14{margin-top:var(--bui-space-14);margin-bottom:var(--bui-space-14)}}@media (width>=768px){.sm\:bui-my{margin-top:var(--my-sm);margin-bottom:var(--my-sm)}.sm\:bui-my-0\.5{margin-top:var(--bui-space-0_5);margin-bottom:var(--bui-space-0_5)}.sm\:bui-my-1{margin-top:var(--bui-space-1);margin-bottom:var(--bui-space-1)}.sm\:bui-my-1\.5{margin-top:var(--bui-space-1_5);margin-bottom:var(--bui-space-1_5)}.sm\:bui-my-2{margin-top:var(--bui-space-2);margin-bottom:var(--bui-space-2)}.sm\:bui-my-3{margin-top:var(--bui-space-3);margin-bottom:var(--bui-space-3)}.sm\:bui-my-4{margin-top:var(--bui-space-4);margin-bottom:var(--bui-space-4)}.sm\:bui-my-5{margin-top:var(--bui-space-5);margin-bottom:var(--bui-space-5)}.sm\:bui-my-6{margin-top:var(--bui-space-6);margin-bottom:var(--bui-space-6)}.sm\:bui-my-7{margin-top:var(--bui-space-7);margin-bottom:var(--bui-space-7)}.sm\:bui-my-8{margin-top:var(--bui-space-8);margin-bottom:var(--bui-space-8)}.sm\:bui-my-9{margin-top:var(--bui-space-9);margin-bottom:var(--bui-space-9)}.sm\:bui-my-10{margin-top:var(--bui-space-10);margin-bottom:var(--bui-space-10)}.sm\:bui-my-11{margin-top:var(--bui-space-11);margin-bottom:var(--bui-space-11)}.sm\:bui-my-12{margin-top:var(--bui-space-12);margin-bottom:var(--bui-space-12)}.sm\:bui-my-13{margin-top:var(--bui-space-13);margin-bottom:var(--bui-space-13)}.sm\:bui-my-14{margin-top:var(--bui-space-14);margin-bottom:var(--bui-space-14)}}@media (width>=1024px){.md\:bui-my{margin-top:var(--my-md);margin-bottom:var(--my-md)}.md\:bui-my-0\.5{margin-top:var(--bui-space-0_5);margin-bottom:var(--bui-space-0_5)}.md\:bui-my-1{margin-top:var(--bui-space-1);margin-bottom:var(--bui-space-1)}.md\:bui-my-1\.5{margin-top:var(--bui-space-1_5);margin-bottom:var(--bui-space-1_5)}.md\:bui-my-2{margin-top:var(--bui-space-2);margin-bottom:var(--bui-space-2)}.md\:bui-my-3{margin-top:var(--bui-space-3);margin-bottom:var(--bui-space-3)}.md\:bui-my-4{margin-top:var(--bui-space-4);margin-bottom:var(--bui-space-4)}.md\:bui-my-5{margin-top:var(--bui-space-5);margin-bottom:var(--bui-space-5)}.md\:bui-my-6{margin-top:var(--bui-space-6);margin-bottom:var(--bui-space-6)}.md\:bui-my-7{margin-top:var(--bui-space-7);margin-bottom:var(--bui-space-7)}.md\:bui-my-8{margin-top:var(--bui-space-8);margin-bottom:var(--bui-space-8)}.md\:bui-my-9{margin-top:var(--bui-space-9);margin-bottom:var(--bui-space-9)}.md\:bui-my-10{margin-top:var(--bui-space-10);margin-bottom:var(--bui-space-10)}.md\:bui-my-11{margin-top:var(--bui-space-11);margin-bottom:var(--bui-space-11)}.md\:bui-my-12{margin-top:var(--bui-space-12);margin-bottom:var(--bui-space-12)}.md\:bui-my-13{margin-top:var(--bui-space-13);margin-bottom:var(--bui-space-13)}.md\:bui-my-14{margin-top:var(--bui-space-14);margin-bottom:var(--bui-space-14)}}@media (width>=1280px){.lg\:bui-my{margin-top:var(--my-lg);margin-bottom:var(--my-lg)}.lg\:bui-my-0\.5{margin-top:var(--bui-space-0_5);margin-bottom:var(--bui-space-0_5)}.lg\:bui-my-1{margin-top:var(--bui-space-1);margin-bottom:var(--bui-space-1)}.lg\:bui-my-1\.5{margin-top:var(--bui-space-1_5);margin-bottom:var(--bui-space-1_5)}.lg\:bui-my-2{margin-top:var(--bui-space-2);margin-bottom:var(--bui-space-2)}.lg\:bui-my-3{margin-top:var(--bui-space-3);margin-bottom:var(--bui-space-3)}.lg\:bui-my-4{margin-top:var(--bui-space-4);margin-bottom:var(--bui-space-4)}.lg\:bui-my-5{margin-top:var(--bui-space-5);margin-bottom:var(--bui-space-5)}.lg\:bui-my-6{margin-top:var(--bui-space-6);margin-bottom:var(--bui-space-6)}.lg\:bui-my-7{margin-top:var(--bui-space-7);margin-bottom:var(--bui-space-7)}.lg\:bui-my-8{margin-top:var(--bui-space-8);margin-bottom:var(--bui-space-8)}.lg\:bui-my-9{margin-top:var(--bui-space-9);margin-bottom:var(--bui-space-9)}.lg\:bui-my-10{margin-top:var(--bui-space-10);margin-bottom:var(--bui-space-10)}.lg\:bui-my-11{margin-top:var(--bui-space-11);margin-bottom:var(--bui-space-11)}.lg\:bui-my-12{margin-top:var(--bui-space-12);margin-bottom:var(--bui-space-12)}.lg\:bui-my-13{margin-top:var(--bui-space-13);margin-bottom:var(--bui-space-13)}.lg\:bui-my-14{margin-top:var(--bui-space-14);margin-bottom:var(--bui-space-14)}}@media (width>=1536px){.xl\:bui-my{margin-top:var(--my-xl);margin-bottom:var(--my-xl)}.xl\:bui-my-0\.5{margin-top:var(--bui-space-0_5);margin-bottom:var(--bui-space-0_5)}.xl\:bui-my-1{margin-top:var(--bui-space-1);margin-bottom:var(--bui-space-1)}.xl\:bui-my-1\.5{margin-top:var(--bui-space-1_5);margin-bottom:var(--bui-space-1_5)}.xl\:bui-my-2{margin-top:var(--bui-space-2);margin-bottom:var(--bui-space-2)}.xl\:bui-my-3{margin-top:var(--bui-space-3);margin-bottom:var(--bui-space-3)}.xl\:bui-my-4{margin-top:var(--bui-space-4);margin-bottom:var(--bui-space-4)}.xl\:bui-my-5{margin-top:var(--bui-space-5);margin-bottom:var(--bui-space-5)}.xl\:bui-my-6{margin-top:var(--bui-space-6);margin-bottom:var(--bui-space-6)}.xl\:bui-my-7{margin-top:var(--bui-space-7);margin-bottom:var(--bui-space-7)}.xl\:bui-my-8{margin-top:var(--bui-space-8);margin-bottom:var(--bui-space-8)}.xl\:bui-my-9{margin-top:var(--bui-space-9);margin-bottom:var(--bui-space-9)}.xl\:bui-my-10{margin-top:var(--bui-space-10);margin-bottom:var(--bui-space-10)}.xl\:bui-my-11{margin-top:var(--bui-space-11);margin-bottom:var(--bui-space-11)}.xl\:bui-my-12{margin-top:var(--bui-space-12);margin-bottom:var(--bui-space-12)}.xl\:bui-my-13{margin-top:var(--bui-space-13);margin-bottom:var(--bui-space-13)}.xl\:bui-my-14{margin-top:var(--bui-space-14);margin-bottom:var(--bui-space-14)}}.bui-mx{margin-left:var(--mx);margin-right:var(--mx)}.bui-mx-0\.5{margin-left:var(--bui-space-0_5);margin-right:var(--bui-space-0_5)}.bui-mx-1{margin-left:var(--bui-space-1);margin-right:var(--bui-space-1)}.bui-mx-1\.5{margin-left:var(--bui-space-1_5);margin-right:var(--bui-space-1_5)}.bui-mx-2{margin-left:var(--bui-space-2);margin-right:var(--bui-space-2)}.bui-mx-3{margin-left:var(--bui-space-3);margin-right:var(--bui-space-3)}.bui-mx-4{margin-left:var(--bui-space-4);margin-right:var(--bui-space-4)}.bui-mx-5{margin-left:var(--bui-space-5);margin-right:var(--bui-space-5)}.bui-mx-6{margin-left:var(--bui-space-6);margin-right:var(--bui-space-6)}.bui-mx-7{margin-left:var(--bui-space-7);margin-right:var(--bui-space-7)}.bui-mx-8{margin-left:var(--bui-space-8);margin-right:var(--bui-space-8)}.bui-mx-9{margin-left:var(--bui-space-9);margin-right:var(--bui-space-9)}.bui-mx-10{margin-left:var(--bui-space-10);margin-right:var(--bui-space-10)}.bui-mx-11{margin-left:var(--bui-space-11);margin-right:var(--bui-space-11)}.bui-mx-12{margin-left:var(--bui-space-12);margin-right:var(--bui-space-12)}.bui-mx-13{margin-left:var(--bui-space-13);margin-right:var(--bui-space-13)}.bui-mx-14{margin-left:var(--bui-space-14);margin-right:var(--bui-space-14)}@media (width>=640px){.xs\:bui-mx{margin-left:var(--mx-xs);margin-right:var(--mx-xs)}.xs\:bui-mx-0\.5{margin-left:var(--bui-space-0_5);margin-right:var(--bui-space-0_5)}.xs\:bui-mx-1{margin-left:var(--bui-space-1);margin-right:var(--bui-space-1)}.xs\:bui-mx-1\.5{margin-left:var(--bui-space-1_5);margin-right:var(--bui-space-1_5)}.xs\:bui-mx-2{margin-left:var(--bui-space-2);margin-right:var(--bui-space-2)}.xs\:bui-mx-3{margin-left:var(--bui-space-3);margin-right:var(--bui-space-3)}.xs\:bui-mx-4{margin-left:var(--bui-space-4);margin-right:var(--bui-space-4)}.xs\:bui-mx-5{margin-left:var(--bui-space-5);margin-right:var(--bui-space-5)}.xs\:bui-mx-6{margin-left:var(--bui-space-6);margin-right:var(--bui-space-6)}.xs\:bui-mx-7{margin-left:var(--bui-space-7);margin-right:var(--bui-space-7)}.xs\:bui-mx-8{margin-left:var(--bui-space-8);margin-right:var(--bui-space-8)}.xs\:bui-mx-9{margin-left:var(--bui-space-9);margin-right:var(--bui-space-9)}.xs\:bui-mx-10{margin-left:var(--bui-space-10);margin-right:var(--bui-space-10)}.xs\:bui-mx-11{margin-left:var(--bui-space-11);margin-right:var(--bui-space-11)}.xs\:bui-mx-12{margin-left:var(--bui-space-12);margin-right:var(--bui-space-12)}.xs\:bui-mx-13{margin-left:var(--bui-space-13);margin-right:var(--bui-space-13)}.xs\:bui-mx-14{margin-left:var(--bui-space-14);margin-right:var(--bui-space-14)}}@media (width>=768px){.sm\:bui-mx{margin-left:var(--mx-sm);margin-right:var(--mx-sm)}.sm\:bui-mx-0\.5{margin-left:var(--bui-space-0_5);margin-right:var(--bui-space-0_5)}.sm\:bui-mx-1{margin-left:var(--bui-space-1);margin-right:var(--bui-space-1)}.sm\:bui-mx-1\.5{margin-left:var(--bui-space-1_5);margin-right:var(--bui-space-1_5)}.sm\:bui-mx-2{margin-left:var(--bui-space-2);margin-right:var(--bui-space-2)}.sm\:bui-mx-3{margin-left:var(--bui-space-3);margin-right:var(--bui-space-3)}.sm\:bui-mx-4{margin-left:var(--bui-space-4);margin-right:var(--bui-space-4)}.sm\:bui-mx-5{margin-left:var(--bui-space-5);margin-right:var(--bui-space-5)}.sm\:bui-mx-6{margin-left:var(--bui-space-6);margin-right:var(--bui-space-6)}.sm\:bui-mx-7{margin-left:var(--bui-space-7);margin-right:var(--bui-space-7)}.sm\:bui-mx-8{margin-left:var(--bui-space-8);margin-right:var(--bui-space-8)}.sm\:bui-mx-9{margin-left:var(--bui-space-9);margin-right:var(--bui-space-9)}.sm\:bui-mx-10{margin-left:var(--bui-space-10);margin-right:var(--bui-space-10)}.sm\:bui-mx-11{margin-left:var(--bui-space-11);margin-right:var(--bui-space-11)}.sm\:bui-mx-12{margin-left:var(--bui-space-12);margin-right:var(--bui-space-12)}.sm\:bui-mx-13{margin-left:var(--bui-space-13);margin-right:var(--bui-space-13)}.sm\:bui-mx-14{margin-left:var(--bui-space-14);margin-right:var(--bui-space-14)}}@media (width>=1024px){.md\:bui-mx{margin-left:var(--mx-md);margin-right:var(--mx-md)}.md\:bui-mx-0\.5{margin-left:var(--bui-space-0_5);margin-right:var(--bui-space-0_5)}.md\:bui-mx-1{margin-left:var(--bui-space-1);margin-right:var(--bui-space-1)}.md\:bui-mx-1\.5{margin-left:var(--bui-space-1_5);margin-right:var(--bui-space-1_5)}.md\:bui-mx-2{margin-left:var(--bui-space-2);margin-right:var(--bui-space-2)}.md\:bui-mx-3{margin-left:var(--bui-space-3);margin-right:var(--bui-space-3)}.md\:bui-mx-4{margin-left:var(--bui-space-4);margin-right:var(--bui-space-4)}.md\:bui-mx-5{margin-left:var(--bui-space-5);margin-right:var(--bui-space-5)}.md\:bui-mx-6{margin-left:var(--bui-space-6);margin-right:var(--bui-space-6)}.md\:bui-mx-7{margin-left:var(--bui-space-7);margin-right:var(--bui-space-7)}.md\:bui-mx-8{margin-left:var(--bui-space-8);margin-right:var(--bui-space-8)}.md\:bui-mx-9{margin-left:var(--bui-space-9);margin-right:var(--bui-space-9)}.md\:bui-mx-10{margin-left:var(--bui-space-10);margin-right:var(--bui-space-10)}.md\:bui-mx-11{margin-left:var(--bui-space-11);margin-right:var(--bui-space-11)}.md\:bui-mx-12{margin-left:var(--bui-space-12);margin-right:var(--bui-space-12)}.md\:bui-mx-13{margin-left:var(--bui-space-13);margin-right:var(--bui-space-13)}.md\:bui-mx-14{margin-left:var(--bui-space-14);margin-right:var(--bui-space-14)}}@media (width>=1280px){.lg\:bui-mx{margin-left:var(--mx-lg);margin-right:var(--mx-lg)}.lg\:bui-mx-0\.5{margin-left:var(--bui-space-0_5);margin-right:var(--bui-space-0_5)}.lg\:bui-mx-1{margin-left:var(--bui-space-1);margin-right:var(--bui-space-1)}.lg\:bui-mx-1\.5{margin-left:var(--bui-space-1_5);margin-right:var(--bui-space-1_5)}.lg\:bui-mx-2{margin-left:var(--bui-space-2);margin-right:var(--bui-space-2)}.lg\:bui-mx-3{margin-left:var(--bui-space-3);margin-right:var(--bui-space-3)}.lg\:bui-mx-4{margin-left:var(--bui-space-4);margin-right:var(--bui-space-4)}.lg\:bui-mx-5{margin-left:var(--bui-space-5);margin-right:var(--bui-space-5)}.lg\:bui-mx-6{margin-left:var(--bui-space-6);margin-right:var(--bui-space-6)}.lg\:bui-mx-7{margin-left:var(--bui-space-7);margin-right:var(--bui-space-7)}.lg\:bui-mx-8{margin-left:var(--bui-space-8);margin-right:var(--bui-space-8)}.lg\:bui-mx-9{margin-left:var(--bui-space-9);margin-right:var(--bui-space-9)}.lg\:bui-mx-10{margin-left:var(--bui-space-10);margin-right:var(--bui-space-10)}.lg\:bui-mx-11{margin-left:var(--bui-space-11);margin-right:var(--bui-space-11)}.lg\:bui-mx-12{margin-left:var(--bui-space-12);margin-right:var(--bui-space-12)}.lg\:bui-mx-13{margin-left:var(--bui-space-13);margin-right:var(--bui-space-13)}.lg\:bui-mx-14{margin-left:var(--bui-space-14);margin-right:var(--bui-space-14)}}@media (width>=1536px){.xl\:bui-mx{margin-left:var(--mx-xl);margin-right:var(--mx-xl)}.xl\:bui-mx-0\.5{margin-left:var(--bui-space-0_5);margin-right:var(--bui-space-0_5)}.xl\:bui-mx-1{margin-left:var(--bui-space-1);margin-right:var(--bui-space-1)}.xl\:bui-mx-1\.5{margin-left:var(--bui-space-1_5);margin-right:var(--bui-space-1_5)}.xl\:bui-mx-2{margin-left:var(--bui-space-2);margin-right:var(--bui-space-2)}.xl\:bui-mx-3{margin-left:var(--bui-space-3);margin-right:var(--bui-space-3)}.xl\:bui-mx-4{margin-left:var(--bui-space-4);margin-right:var(--bui-space-4)}.xl\:bui-mx-5{margin-left:var(--bui-space-5);margin-right:var(--bui-space-5)}.xl\:bui-mx-6{margin-left:var(--bui-space-6);margin-right:var(--bui-space-6)}.xl\:bui-mx-7{margin-left:var(--bui-space-7);margin-right:var(--bui-space-7)}.xl\:bui-mx-8{margin-left:var(--bui-space-8);margin-right:var(--bui-space-8)}.xl\:bui-mx-9{margin-left:var(--bui-space-9);margin-right:var(--bui-space-9)}.xl\:bui-mx-10{margin-left:var(--bui-space-10);margin-right:var(--bui-space-10)}.xl\:bui-mx-11{margin-left:var(--bui-space-11);margin-right:var(--bui-space-11)}.xl\:bui-mx-12{margin-left:var(--bui-space-12);margin-right:var(--bui-space-12)}.xl\:bui-mx-13{margin-left:var(--bui-space-13);margin-right:var(--bui-space-13)}.xl\:bui-mx-14{margin-left:var(--bui-space-14);margin-right:var(--bui-space-14)}}.bui-display-none{display:none}.bui-display-inline{display:inline}.bui-display-inline-block{display:inline-block}.bui-display-block{display:block}@media (width>=640px){.xs\:bui-display-none{display:none}.xs\:bui-display-inline{display:inline}.xs\:bui-display-inline-block{display:inline-block}.xs\:bui-display-block{display:block}}@media (width>=768px){.sm\:bui-display-none{display:none}.sm\:bui-display-inline{display:inline}.sm\:bui-display-inline-block{display:inline-block}.sm\:bui-display-block{display:block}}@media (width>=1024px){.md\:bui-display-none{display:none}.md\:bui-display-inline{display:inline}.md\:bui-display-inline-block{display:inline-block}.md\:bui-display-block{display:block}}@media (width>=1280px){.lg\:bui-display-none{display:none}.lg\:bui-display-inline{display:inline}.lg\:bui-display-inline-block{display:inline-block}.lg\:bui-display-block{display:block}}@media (width>=1536px){.xl\:bui-display-none{display:none}.xl\:bui-display-inline{display:inline}.xl\:bui-display-inline-block{display:inline-block}.xl\:bui-display-block{display:block}}.bui-w{width:var(--width)}.bui-min-w{min-width:var(--min-width)}.bui-max-w{max-width:var(--max-width)}@media (width>=640px){.xs\:bui-w{width:var(--width)}.xs\:bui-min-w{min-width:var(--min-width)}.xs\:bui-max-w{max-width:var(--max-width)}}@media (width>=768px){.sm\:bui-w{width:var(--width)}.sm\:bui-min-w{min-width:var(--min-width)}.sm\:bui-max-w{max-width:var(--max-width)}}@media (width>=1024px){.md\:bui-w{width:var(--width)}.md\:bui-min-w{min-width:var(--min-width)}.md\:bui-max-w{max-width:var(--max-width)}}@media (width>=1280px){.lg\:bui-w{width:var(--width)}.lg\:bui-min-w{min-width:var(--min-width)}.lg\:bui-max-w{max-width:var(--max-width)}}@media (width>=1536px){.xl\:bui-w{width:var(--width)}.xl\:bui-min-w{min-width:var(--min-width)}.xl\:bui-max-w{max-width:var(--max-width)}}.bui-h{height:var(--height)}.bui-min-h{min-height:var(--min-height)}.bui-max-h{max-height:var(--max-height)}@media (width>=640px){.xs\:bui-h{height:var(--height)}.xs\:bui-min-h{min-height:var(--min-height)}.xs\:bui-max-h{max-height:var(--max-height)}}@media (width>=768px){.sm\:bui-h{height:var(--height)}.sm\:bui-min-h{min-height:var(--min-height)}.sm\:bui-max-h{max-height:var(--max-height)}}@media (width>=1024px){.md\:bui-h{height:var(--height)}.md\:bui-min-h{min-height:var(--min-height)}.md\:bui-max-h{max-height:var(--max-height)}}@media (width>=1280px){.lg\:bui-h{height:var(--height)}.lg\:bui-min-h{min-height:var(--min-height)}.lg\:bui-max-h{max-height:var(--max-height)}}@media (width>=1536px){.xl\:bui-h{height:var(--height)}.xl\:bui-min-h{min-height:var(--min-height)}.xl\:bui-max-h{max-height:var(--max-height)}}.bui-position-absolute{position:absolute}.bui-position-fixed{position:fixed}.bui-position-sticky{position:sticky}.bui-position-relative{position:relative}.bui-position-static{position:static}@media (width>=640px){.xs\:bui-position-absolute{position:absolute}.xs\:bui-position-fixed{position:fixed}.xs\:bui-position-sticky{position:sticky}.xs\:bui-position-relative{position:relative}.xs\:bui-position-static{position:static}}@media (width>=768px){.sm\:bui-position-absolute{position:absolute}.sm\:bui-position-fixed{position:fixed}.sm\:bui-position-sticky{position:sticky}.sm\:bui-position-relative{position:relative}.sm\:bui-position-static{position:static}}@media (width>=1024px){.md\:bui-position-absolute{position:absolute}.md\:bui-position-fixed{position:fixed}.md\:bui-position-sticky{position:sticky}.md\:bui-position-relative{position:relative}.md\:bui-position-static{position:static}}@media (width>=1280px){.lg\:bui-position-absolute{position:absolute}.lg\:bui-position-fixed{position:fixed}.lg\:bui-position-sticky{position:sticky}.lg\:bui-position-relative{position:relative}.lg\:bui-position-static{position:static}}@media (width>=1536px){.xl\:bui-position-absolute{position:absolute}.xl\:bui-position-fixed{position:fixed}.xl\:bui-position-sticky{position:sticky}.xl\:bui-position-relative{position:relative}.xl\:bui-position-static{position:static}}.bui-columns-1{grid-template-columns:repeat(1,minmax(0,1fr))}.bui-columns-2{grid-template-columns:repeat(2,minmax(0,1fr))}.bui-columns-3{grid-template-columns:repeat(3,minmax(0,1fr))}.bui-columns-4{grid-template-columns:repeat(4,minmax(0,1fr))}.bui-columns-5{grid-template-columns:repeat(5,minmax(0,1fr))}.bui-columns-6{grid-template-columns:repeat(6,minmax(0,1fr))}.bui-columns-7{grid-template-columns:repeat(7,minmax(0,1fr))}.bui-columns-8{grid-template-columns:repeat(8,minmax(0,1fr))}.bui-columns-9{grid-template-columns:repeat(9,minmax(0,1fr))}.bui-columns-10{grid-template-columns:repeat(10,minmax(0,1fr))}.bui-columns-11{grid-template-columns:repeat(11,minmax(0,1fr))}.bui-columns-12{grid-template-columns:repeat(12,minmax(0,1fr))}.bui-columns-auto{grid-template-columns:repeat(auto-fit,minmax(0,1fr))}.bui-col-span-1{grid-column:span 1/span 1}.bui-col-span-2{grid-column:span 2/span 2}.bui-col-span-3{grid-column:span 3/span 3}.bui-col-span-4{grid-column:span 4/span 4}.bui-col-span-5{grid-column:span 5/span 5}.bui-col-span-6{grid-column:span 6/span 6}.bui-col-span-7{grid-column:span 7/span 7}.bui-col-span-8{grid-column:span 8/span 8}.bui-col-span-9{grid-column:span 9/span 9}.bui-col-span-10{grid-column:span 10/span 10}.bui-col-span-11{grid-column:span 11/span 11}.bui-col-span-12{grid-column:span 12/span 12}.bui-col-span-auto{grid-column:span auto/span auto}.bui-col-start-1{grid-column-start:1}.bui-col-start-2{grid-column-start:2}.bui-col-start-3{grid-column-start:3}.bui-col-start-4{grid-column-start:4}.bui-col-start-5{grid-column-start:5}.bui-col-start-6{grid-column-start:6}.bui-col-start-7{grid-column-start:7}.bui-col-start-8{grid-column-start:8}.bui-col-start-9{grid-column-start:9}.bui-col-start-10{grid-column-start:10}.bui-col-start-11{grid-column-start:11}.bui-col-start-12{grid-column-start:12}.bui-col-start-13{grid-column-start:13}.bui-col-start-auto{grid-column-start:auto}.bui-col-end-1{grid-column-end:1}.bui-col-end-2{grid-column-end:2}.bui-col-end-3{grid-column-end:3}.bui-col-end-4{grid-column-end:4}.bui-col-end-5{grid-column-end:5}.bui-col-end-6{grid-column-end:6}.bui-col-end-7{grid-column-end:7}.bui-col-end-8{grid-column-end:8}.bui-col-end-9{grid-column-end:9}.bui-col-end-10{grid-column-end:10}.bui-col-end-11{grid-column-end:11}.bui-col-end-12{grid-column-end:12}.bui-col-end-13{grid-column-end:13}.bui-col-end-auto{grid-column-end:auto}.bui-row-span-1{grid-row:span 1/span 1}.bui-row-span-2{grid-row:span 2/span 2}.bui-row-span-3{grid-row:span 3/span 3}.bui-row-span-4{grid-row:span 4/span 4}.bui-row-span-5{grid-row:span 5/span 5}.bui-row-span-6{grid-row:span 6/span 6}.bui-row-span-7{grid-row:span 7/span 7}.bui-row-span-8{grid-row:span 8/span 8}.bui-row-span-9{grid-row:span 9/span 9}.bui-row-span-10{grid-row:span 10/span 10}.bui-row-span-11{grid-row:span 11/span 11}.bui-row-span-12{grid-row:span 12/span 12}.bui-row-span-auto{grid-row:span auto/span auto}@media (width>=640px){.xs\:bui-columns-1{grid-template-columns:repeat(1,minmax(0,1fr))}.xs\:bui-columns-2{grid-template-columns:repeat(2,minmax(0,1fr))}.xs\:bui-columns-3{grid-template-columns:repeat(3,minmax(0,1fr))}.xs\:bui-columns-4{grid-template-columns:repeat(4,minmax(0,1fr))}.xs\:bui-columns-5{grid-template-columns:repeat(5,minmax(0,1fr))}.xs\:bui-columns-6{grid-template-columns:repeat(6,minmax(0,1fr))}.xs\:bui-columns-7{grid-template-columns:repeat(7,minmax(0,1fr))}.xs\:bui-columns-8{grid-template-columns:repeat(8,minmax(0,1fr))}.xs\:bui-columns-9{grid-template-columns:repeat(9,minmax(0,1fr))}.xs\:bui-columns-10{grid-template-columns:repeat(10,minmax(0,1fr))}.xs\:bui-columns-11{grid-template-columns:repeat(11,minmax(0,1fr))}.xs\:bui-columns-12{grid-template-columns:repeat(12,minmax(0,1fr))}.xs\:bui-columns-auto{grid-template-columns:repeat(auto-fit,minmax(0,1fr))}.xs\:bui-col-span-1{grid-column:span 1/span 1}.xs\:bui-col-span-2{grid-column:span 2/span 2}.xs\:bui-col-span-3{grid-column:span 3/span 3}.xs\:bui-col-span-4{grid-column:span 4/span 4}.xs\:bui-col-span-5{grid-column:span 5/span 5}.xs\:bui-col-span-6{grid-column:span 6/span 6}.xs\:bui-col-span-7{grid-column:span 7/span 7}.xs\:bui-col-span-8{grid-column:span 8/span 8}.xs\:bui-col-span-9{grid-column:span 9/span 9}.xs\:bui-col-span-10{grid-column:span 10/span 10}.xs\:bui-col-span-11{grid-column:span 11/span 11}.xs\:bui-col-span-12{grid-column:span 12/span 12}.xs\:bui-col-span-auto{grid-column:span auto/span auto}.xs\:bui-col-start-1{grid-column-start:1}.xs\:bui-col-start-2{grid-column-start:2}.xs\:bui-col-start-3{grid-column-start:3}.xs\:bui-col-start-4{grid-column-start:4}.xs\:bui-col-start-5{grid-column-start:5}.xs\:bui-col-start-6{grid-column-start:6}.xs\:bui-col-start-7{grid-column-start:7}.xs\:bui-col-start-8{grid-column-start:8}.xs\:bui-col-start-9{grid-column-start:9}.xs\:bui-col-start-10{grid-column-start:10}.xs\:bui-col-start-11{grid-column-start:11}.xs\:bui-col-start-12{grid-column-start:12}.xs\:bui-col-start-13{grid-column-start:13}.xs\:bui-col-start-auto{grid-column-start:auto}.xs\:bui-col-end-1{grid-column-end:1}.xs\:bui-col-end-2{grid-column-end:2}.xs\:bui-col-end-3{grid-column-end:3}.xs\:bui-col-end-4{grid-column-end:4}.xs\:bui-col-end-5{grid-column-end:5}.xs\:bui-col-end-6{grid-column-end:6}.xs\:bui-col-end-7{grid-column-end:7}.xs\:bui-col-end-8{grid-column-end:8}.xs\:bui-col-end-9{grid-column-end:9}.xs\:bui-col-end-10{grid-column-end:10}.xs\:bui-col-end-11{grid-column-end:11}.xs\:bui-col-end-12{grid-column-end:12}.xs\:bui-col-end-13{grid-column-end:13}.xs\:bui-col-end-auto{grid-column-end:auto}.xs\:bui-row-span-1{grid-row:span 1/span 1}.xs\:bui-row-span-2{grid-row:span 2/span 2}.xs\:bui-row-span-3{grid-row:span 3/span 3}.xs\:bui-row-span-4{grid-row:span 4/span 4}.xs\:bui-row-span-5{grid-row:span 5/span 5}.xs\:bui-row-span-6{grid-row:span 6/span 6}.xs\:bui-row-span-7{grid-row:span 7/span 7}.xs\:bui-row-span-8{grid-row:span 8/span 8}.xs\:bui-row-span-9{grid-row:span 9/span 9}.xs\:bui-row-span-10{grid-row:span 10/span 10}.xs\:bui-row-span-11{grid-row:span 11/span 11}.xs\:bui-row-span-12{grid-row:span 12/span 12}.xs\:bui-row-span-auto{grid-row:span auto/span auto}}@media (width>=768px){.sm\:bui-columns-1{grid-template-columns:repeat(1,minmax(0,1fr))}.sm\:bui-columns-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:bui-columns-3{grid-template-columns:repeat(3,minmax(0,1fr))}.sm\:bui-columns-4{grid-template-columns:repeat(4,minmax(0,1fr))}.sm\:bui-columns-5{grid-template-columns:repeat(5,minmax(0,1fr))}.sm\:bui-columns-6{grid-template-columns:repeat(6,minmax(0,1fr))}.sm\:bui-columns-7{grid-template-columns:repeat(7,minmax(0,1fr))}.sm\:bui-columns-8{grid-template-columns:repeat(8,minmax(0,1fr))}.sm\:bui-columns-9{grid-template-columns:repeat(9,minmax(0,1fr))}.sm\:bui-columns-10{grid-template-columns:repeat(10,minmax(0,1fr))}.sm\:bui-columns-11{grid-template-columns:repeat(11,minmax(0,1fr))}.sm\:bui-columns-12{grid-template-columns:repeat(12,minmax(0,1fr))}.sm\:bui-columns-auto{grid-template-columns:repeat(auto-fit,minmax(0,1fr))}.sm\:bui-col-span-1{grid-column:span 1/span 1}.sm\:bui-col-span-2{grid-column:span 2/span 2}.sm\:bui-col-span-3{grid-column:span 3/span 3}.sm\:bui-col-span-4{grid-column:span 4/span 4}.sm\:bui-col-span-5{grid-column:span 5/span 5}.sm\:bui-col-span-6{grid-column:span 6/span 6}.sm\:bui-col-span-7{grid-column:span 7/span 7}.sm\:bui-col-span-8{grid-column:span 8/span 8}.sm\:bui-col-span-9{grid-column:span 9/span 9}.sm\:bui-col-span-10{grid-column:span 10/span 10}.sm\:bui-col-span-11{grid-column:span 11/span 11}.sm\:bui-col-span-12{grid-column:span 12/span 12}.sm\:bui-col-span-auto{grid-column:span auto/span auto}.sm\:bui-col-start-1{grid-column-start:1}.sm\:bui-col-start-2{grid-column-start:2}.sm\:bui-col-start-3{grid-column-start:3}.sm\:bui-col-start-4{grid-column-start:4}.sm\:bui-col-start-5{grid-column-start:5}.sm\:bui-col-start-6{grid-column-start:6}.sm\:bui-col-start-7{grid-column-start:7}.sm\:bui-col-start-8{grid-column-start:8}.sm\:bui-col-start-9{grid-column-start:9}.sm\:bui-col-start-10{grid-column-start:10}.sm\:bui-col-start-11{grid-column-start:11}.sm\:bui-col-start-12{grid-column-start:12}.sm\:bui-col-start-13{grid-column-start:13}.sm\:bui-col-start-auto{grid-column-start:auto}.sm\:bui-col-end-1{grid-column-end:1}.sm\:bui-col-end-2{grid-column-end:2}.sm\:bui-col-end-3{grid-column-end:3}.sm\:bui-col-end-4{grid-column-end:4}.sm\:bui-col-end-5{grid-column-end:5}.sm\:bui-col-end-6{grid-column-end:6}.sm\:bui-col-end-7{grid-column-end:7}.sm\:bui-col-end-8{grid-column-end:8}.sm\:bui-col-end-9{grid-column-end:9}.sm\:bui-col-end-10{grid-column-end:10}.sm\:bui-col-end-11{grid-column-end:11}.sm\:bui-col-end-12{grid-column-end:12}.sm\:bui-col-end-13{grid-column-end:13}.sm\:bui-col-end-auto{grid-column-end:auto}.sm\:bui-row-span-1{grid-row:span 1/span 1}.sm\:bui-row-span-2{grid-row:span 2/span 2}.sm\:bui-row-span-3{grid-row:span 3/span 3}.sm\:bui-row-span-4{grid-row:span 4/span 4}.sm\:bui-row-span-5{grid-row:span 5/span 5}.sm\:bui-row-span-6{grid-row:span 6/span 6}.sm\:bui-row-span-7{grid-row:span 7/span 7}.sm\:bui-row-span-8{grid-row:span 8/span 8}.sm\:bui-row-span-9{grid-row:span 9/span 9}.sm\:bui-row-span-10{grid-row:span 10/span 10}.sm\:bui-row-span-11{grid-row:span 11/span 11}.sm\:bui-row-span-12{grid-row:span 12/span 12}.sm\:bui-row-span-auto{grid-row:span auto/span auto}}@media (width>=1024px){.md\:bui-columns-1{grid-template-columns:repeat(1,minmax(0,1fr))}.md\:bui-columns-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:bui-columns-3{grid-template-columns:repeat(3,minmax(0,1fr))}.md\:bui-columns-4{grid-template-columns:repeat(4,minmax(0,1fr))}.md\:bui-columns-5{grid-template-columns:repeat(5,minmax(0,1fr))}.md\:bui-columns-6{grid-template-columns:repeat(6,minmax(0,1fr))}.md\:bui-columns-7{grid-template-columns:repeat(7,minmax(0,1fr))}.md\:bui-columns-8{grid-template-columns:repeat(8,minmax(0,1fr))}.md\:bui-columns-9{grid-template-columns:repeat(9,minmax(0,1fr))}.md\:bui-columns-10{grid-template-columns:repeat(10,minmax(0,1fr))}.md\:bui-columns-11{grid-template-columns:repeat(11,minmax(0,1fr))}.md\:bui-columns-12{grid-template-columns:repeat(12,minmax(0,1fr))}.md\:bui-columns-auto{grid-template-columns:repeat(auto-fit,minmax(0,1fr))}.md\:bui-col-span-1{grid-column:span 1/span 1}.md\:bui-col-span-2{grid-column:span 2/span 2}.md\:bui-col-span-3{grid-column:span 3/span 3}.md\:bui-col-span-4{grid-column:span 4/span 4}.md\:bui-col-span-5{grid-column:span 5/span 5}.md\:bui-col-span-6{grid-column:span 6/span 6}.md\:bui-col-span-7{grid-column:span 7/span 7}.md\:bui-col-span-8{grid-column:span 8/span 8}.md\:bui-col-span-9{grid-column:span 9/span 9}.md\:bui-col-span-10{grid-column:span 10/span 10}.md\:bui-col-span-11{grid-column:span 11/span 11}.md\:bui-col-span-12{grid-column:span 12/span 12}.md\:bui-col-span-auto{grid-column:span auto/span auto}.md\:bui-col-start-1{grid-column-start:1}.md\:bui-col-start-2{grid-column-start:2}.md\:bui-col-start-3{grid-column-start:3}.md\:bui-col-start-4{grid-column-start:4}.md\:bui-col-start-5{grid-column-start:5}.md\:bui-col-start-6{grid-column-start:6}.md\:bui-col-start-7{grid-column-start:7}.md\:bui-col-start-8{grid-column-start:8}.md\:bui-col-start-9{grid-column-start:9}.md\:bui-col-start-10{grid-column-start:10}.md\:bui-col-start-11{grid-column-start:11}.md\:bui-col-start-12{grid-column-start:12}.md\:bui-col-start-13{grid-column-start:13}.md\:bui-col-start-auto{grid-column-start:auto}.md\:bui-col-end-1{grid-column-end:1}.md\:bui-col-end-2{grid-column-end:2}.md\:bui-col-end-3{grid-column-end:3}.md\:bui-col-end-4{grid-column-end:4}.md\:bui-col-end-5{grid-column-end:5}.md\:bui-col-end-6{grid-column-end:6}.md\:bui-col-end-7{grid-column-end:7}.md\:bui-col-end-8{grid-column-end:8}.md\:bui-col-end-9{grid-column-end:9}.md\:bui-col-end-10{grid-column-end:10}.md\:bui-col-end-11{grid-column-end:11}.md\:bui-col-end-12{grid-column-end:12}.md\:bui-col-end-13{grid-column-end:13}.md\:bui-col-end-auto{grid-column-end:auto}.md\:bui-row-span-1{grid-row:span 1/span 1}.md\:bui-row-span-2{grid-row:span 2/span 2}.md\:bui-row-span-3{grid-row:span 3/span 3}.md\:bui-row-span-4{grid-row:span 4/span 4}.md\:bui-row-span-5{grid-row:span 5/span 5}.md\:bui-row-span-6{grid-row:span 6/span 6}.md\:bui-row-span-7{grid-row:span 7/span 7}.md\:bui-row-span-8{grid-row:span 8/span 8}.md\:bui-row-span-9{grid-row:span 9/span 9}.md\:bui-row-span-10{grid-row:span 10/span 10}.md\:bui-row-span-11{grid-row:span 11/span 11}.md\:bui-row-span-12{grid-row:span 12/span 12}.md\:bui-row-span-auto{grid-row:span auto/span auto}}@media (width>=1280px){.lg\:bui-columns-1{grid-template-columns:repeat(1,minmax(0,1fr))}.lg\:bui-columns-2{grid-template-columns:repeat(2,minmax(0,1fr))}.lg\:bui-columns-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:bui-columns-4{grid-template-columns:repeat(4,minmax(0,1fr))}.lg\:bui-columns-5{grid-template-columns:repeat(5,minmax(0,1fr))}.lg\:bui-columns-6{grid-template-columns:repeat(6,minmax(0,1fr))}.lg\:bui-columns-7{grid-template-columns:repeat(7,minmax(0,1fr))}.lg\:bui-columns-8{grid-template-columns:repeat(8,minmax(0,1fr))}.lg\:bui-columns-9{grid-template-columns:repeat(9,minmax(0,1fr))}.lg\:bui-columns-10{grid-template-columns:repeat(10,minmax(0,1fr))}.lg\:bui-columns-11{grid-template-columns:repeat(11,minmax(0,1fr))}.lg\:bui-columns-12{grid-template-columns:repeat(12,minmax(0,1fr))}.lg\:bui-columns-auto{grid-template-columns:repeat(auto-fit,minmax(0,1fr))}.lg\:bui-col-span-1{grid-column:span 1/span 1}.lg\:bui-col-span-2{grid-column:span 2/span 2}.lg\:bui-col-span-3{grid-column:span 3/span 3}.lg\:bui-col-span-4{grid-column:span 4/span 4}.lg\:bui-col-span-5{grid-column:span 5/span 5}.lg\:bui-col-span-6{grid-column:span 6/span 6}.lg\:bui-col-span-7{grid-column:span 7/span 7}.lg\:bui-col-span-8{grid-column:span 8/span 8}.lg\:bui-col-span-9{grid-column:span 9/span 9}.lg\:bui-col-span-10{grid-column:span 10/span 10}.lg\:bui-col-span-11{grid-column:span 11/span 11}.lg\:bui-col-span-12{grid-column:span 12/span 12}.lg\:bui-col-span-auto{grid-column:span auto/span auto}.lg\:bui-col-start-1{grid-column-start:1}.lg\:bui-col-start-2{grid-column-start:2}.lg\:bui-col-start-3{grid-column-start:3}.lg\:bui-col-start-4{grid-column-start:4}.lg\:bui-col-start-5{grid-column-start:5}.lg\:bui-col-start-6{grid-column-start:6}.lg\:bui-col-start-7{grid-column-start:7}.lg\:bui-col-start-8{grid-column-start:8}.lg\:bui-col-start-9{grid-column-start:9}.lg\:bui-col-start-10{grid-column-start:10}.lg\:bui-col-start-11{grid-column-start:11}.lg\:bui-col-start-12{grid-column-start:12}.lg\:bui-col-start-13{grid-column-start:13}.lg\:bui-col-start-auto{grid-column-start:auto}.lg\:bui-col-end-1{grid-column-end:1}.lg\:bui-col-end-2{grid-column-end:2}.lg\:bui-col-end-3{grid-column-end:3}.lg\:bui-col-end-4{grid-column-end:4}.lg\:bui-col-end-5{grid-column-end:5}.lg\:bui-col-end-6{grid-column-end:6}.lg\:bui-col-end-7{grid-column-end:7}.lg\:bui-col-end-8{grid-column-end:8}.lg\:bui-col-end-9{grid-column-end:9}.lg\:bui-col-end-10{grid-column-end:10}.lg\:bui-col-end-11{grid-column-end:11}.lg\:bui-col-end-12{grid-column-end:12}.lg\:bui-col-end-13{grid-column-end:13}.lg\:bui-col-end-auto{grid-column-end:auto}.lg\:bui-row-span-1{grid-row:span 1/span 1}.lg\:bui-row-span-2{grid-row:span 2/span 2}.lg\:bui-row-span-3{grid-row:span 3/span 3}.lg\:bui-row-span-4{grid-row:span 4/span 4}.lg\:bui-row-span-5{grid-row:span 5/span 5}.lg\:bui-row-span-6{grid-row:span 6/span 6}.lg\:bui-row-span-7{grid-row:span 7/span 7}.lg\:bui-row-span-8{grid-row:span 8/span 8}.lg\:bui-row-span-9{grid-row:span 9/span 9}.lg\:bui-row-span-10{grid-row:span 10/span 10}.lg\:bui-row-span-11{grid-row:span 11/span 11}.lg\:bui-row-span-12{grid-row:span 12/span 12}.lg\:bui-row-span-auto{grid-row:span auto/span auto}}@media (width>=1536px){.xl\:bui-columns-1{grid-template-columns:repeat(1,minmax(0,1fr))}.xl\:bui-columns-2{grid-template-columns:repeat(2,minmax(0,1fr))}.xl\:bui-columns-3{grid-template-columns:repeat(3,minmax(0,1fr))}.xl\:bui-columns-4{grid-template-columns:repeat(4,minmax(0,1fr))}.xl\:bui-columns-5{grid-template-columns:repeat(5,minmax(0,1fr))}.xl\:bui-columns-6{grid-template-columns:repeat(6,minmax(0,1fr))}.xl\:bui-columns-7{grid-template-columns:repeat(7,minmax(0,1fr))}.xl\:bui-columns-8{grid-template-columns:repeat(8,minmax(0,1fr))}.xl\:bui-columns-9{grid-template-columns:repeat(9,minmax(0,1fr))}.xl\:bui-columns-10{grid-template-columns:repeat(10,minmax(0,1fr))}.xl\:bui-columns-11{grid-template-columns:repeat(11,minmax(0,1fr))}.xl\:bui-columns-12{grid-template-columns:repeat(12,minmax(0,1fr))}.xl\:bui-columns-auto{grid-template-columns:repeat(auto-fit,minmax(0,1fr))}.xl\:bui-col-span-1{grid-column:span 1/span 1}.xl\:bui-col-span-2{grid-column:span 2/span 2}.xl\:bui-col-span-3{grid-column:span 3/span 3}.xl\:bui-col-span-4{grid-column:span 4/span 4}.xl\:bui-col-span-5{grid-column:span 5/span 5}.xl\:bui-col-span-6{grid-column:span 6/span 6}.xl\:bui-col-span-7{grid-column:span 7/span 7}.xl\:bui-col-span-8{grid-column:span 8/span 8}.xl\:bui-col-span-9{grid-column:span 9/span 9}.xl\:bui-col-span-10{grid-column:span 10/span 10}.xl\:bui-col-span-11{grid-column:span 11/span 11}.xl\:bui-col-span-12{grid-column:span 12/span 12}.xl\:bui-col-span-auto{grid-column:span auto/span auto}.xl\:bui-col-start-1{grid-column-start:1}.xl\:bui-col-start-2{grid-column-start:2}.xl\:bui-col-start-3{grid-column-start:3}.xl\:bui-col-start-4{grid-column-start:4}.xl\:bui-col-start-5{grid-column-start:5}.xl\:bui-col-start-6{grid-column-start:6}.xl\:bui-col-start-7{grid-column-start:7}.xl\:bui-col-start-8{grid-column-start:8}.xl\:bui-col-start-9{grid-column-start:9}.xl\:bui-col-start-10{grid-column-start:10}.xl\:bui-col-start-11{grid-column-start:11}.xl\:bui-col-start-12{grid-column-start:12}.xl\:bui-col-start-13{grid-column-start:13}.xl\:bui-col-start-auto{grid-column-start:auto}.xl\:bui-col-end-1{grid-column-end:1}.xl\:bui-col-end-2{grid-column-end:2}.xl\:bui-col-end-3{grid-column-end:3}.xl\:bui-col-end-4{grid-column-end:4}.xl\:bui-col-end-5{grid-column-end:5}.xl\:bui-col-end-6{grid-column-end:6}.xl\:bui-col-end-7{grid-column-end:7}.xl\:bui-col-end-8{grid-column-end:8}.xl\:bui-col-end-9{grid-column-end:9}.xl\:bui-col-end-10{grid-column-end:10}.xl\:bui-col-end-11{grid-column-end:11}.xl\:bui-col-end-12{grid-column-end:12}.xl\:bui-col-end-13{grid-column-end:13}.xl\:bui-col-end-auto{grid-column-end:auto}.xl\:bui-row-span-1{grid-row:span 1/span 1}.xl\:bui-row-span-2{grid-row:span 2/span 2}.xl\:bui-row-span-3{grid-row:span 3/span 3}.xl\:bui-row-span-4{grid-row:span 4/span 4}.xl\:bui-row-span-5{grid-row:span 5/span 5}.xl\:bui-row-span-6{grid-row:span 6/span 6}.xl\:bui-row-span-7{grid-row:span 7/span 7}.xl\:bui-row-span-8{grid-row:span 8/span 8}.xl\:bui-row-span-9{grid-row:span 9/span 9}.xl\:bui-row-span-10{grid-row:span 10/span 10}.xl\:bui-row-span-11{grid-row:span 11/span 11}.xl\:bui-row-span-12{grid-row:span 12/span 12}.xl\:bui-row-span-auto{grid-row:span auto/span auto}}.bui-gap{gap:var(--gap)}.bui-gap-0\.5{gap:var(--bui-space-0_5)}.bui-gap-1{gap:var(--bui-space-1)}.bui-gap-1\.5{gap:var(--bui-space-1_5)}.bui-gap-2{gap:var(--bui-space-2)}.bui-gap-3{gap:var(--bui-space-3)}.bui-gap-4{gap:var(--bui-space-4)}.bui-gap-5{gap:var(--bui-space-5)}.bui-gap-6{gap:var(--bui-space-6)}.bui-gap-7{gap:var(--bui-space-7)}.bui-gap-8{gap:var(--bui-space-8)}.bui-gap-9{gap:var(--bui-space-9)}.bui-gap-10{gap:var(--bui-space-10)}.bui-gap-11{gap:var(--bui-space-11)}.bui-gap-12{gap:var(--bui-space-12)}.bui-gap-13{gap:var(--bui-space-13)}.bui-gap-14{gap:var(--bui-space-14)}@media (width>=640px){.xs\:bui-gap{gap:var(--gap-xs)}.xs\:bui-gap-0\.5{gap:var(--bui-space-0_5)}.xs\:bui-gap-1{gap:var(--bui-space-1)}.xs\:bui-gap-1\.5{gap:var(--bui-space-1_5)}.xs\:bui-gap-2{gap:var(--bui-space-2)}.xs\:bui-gap-3{gap:var(--bui-space-3)}.xs\:bui-gap-4{gap:var(--bui-space-4)}.xs\:bui-gap-5{gap:var(--bui-space-5)}.xs\:bui-gap-6{gap:var(--bui-space-6)}.xs\:bui-gap-7{gap:var(--bui-space-7)}.xs\:bui-gap-8{gap:var(--bui-space-8)}.xs\:bui-gap-9{gap:var(--bui-space-9)}.xs\:bui-gap-10{gap:var(--bui-space-10)}.xs\:bui-gap-11{gap:var(--bui-space-11)}.xs\:bui-gap-12{gap:var(--bui-space-12)}.xs\:bui-gap-13{gap:var(--bui-space-13)}.xs\:bui-gap-14{gap:var(--bui-space-14)}}@media (width>=768px){.sm\:bui-gap{gap:var(--gap-sm)}.sm\:bui-gap-0\.5{gap:var(--bui-space-0_5)}.sm\:bui-gap-1{gap:var(--bui-space-1)}.sm\:bui-gap-1\.5{gap:var(--bui-space-1_5)}.sm\:bui-gap-2{gap:var(--bui-space-2)}.sm\:bui-gap-3{gap:var(--bui-space-3)}.sm\:bui-gap-4{gap:var(--bui-space-4)}.sm\:bui-gap-5{gap:var(--bui-space-5)}.sm\:bui-gap-6{gap:var(--bui-space-6)}.sm\:bui-gap-7{gap:var(--bui-space-7)}.sm\:bui-gap-8{gap:var(--bui-space-8)}.sm\:bui-gap-9{gap:var(--bui-space-9)}.sm\:bui-gap-10{gap:var(--bui-space-10)}.sm\:bui-gap-11{gap:var(--bui-space-11)}.sm\:bui-gap-12{gap:var(--bui-space-12)}.sm\:bui-gap-13{gap:var(--bui-space-13)}.sm\:bui-gap-14{gap:var(--bui-space-14)}}@media (width>=1024px){.md\:bui-gap{gap:var(--gap-md)}.md\:bui-gap-0\.5{gap:var(--bui-space-0_5)}.md\:bui-gap-1{gap:var(--bui-space-1)}.md\:bui-gap-1\.5{gap:var(--bui-space-1_5)}.md\:bui-gap-2{gap:var(--bui-space-2)}.md\:bui-gap-3{gap:var(--bui-space-3)}.md\:bui-gap-4{gap:var(--bui-space-4)}.md\:bui-gap-5{gap:var(--bui-space-5)}.md\:bui-gap-6{gap:var(--bui-space-6)}.md\:bui-gap-7{gap:var(--bui-space-7)}.md\:bui-gap-8{gap:var(--bui-space-8)}.md\:bui-gap-9{gap:var(--bui-space-9)}.md\:bui-gap-10{gap:var(--bui-space-10)}.md\:bui-gap-11{gap:var(--bui-space-11)}.md\:bui-gap-12{gap:var(--bui-space-12)}.md\:bui-gap-13{gap:var(--bui-space-13)}.md\:bui-gap-14{gap:var(--bui-space-14)}}@media (width>=1280px){.lg\:bui-gap{gap:var(--gap-lg)}.lg\:bui-gap-0\.5{gap:var(--bui-space-0_5)}.lg\:bui-gap-1{gap:var(--bui-space-1)}.lg\:bui-gap-1\.5{gap:var(--bui-space-1_5)}.lg\:bui-gap-2{gap:var(--bui-space-2)}.lg\:bui-gap-3{gap:var(--bui-space-3)}.lg\:bui-gap-4{gap:var(--bui-space-4)}.lg\:bui-gap-5{gap:var(--bui-space-5)}.lg\:bui-gap-6{gap:var(--bui-space-6)}.lg\:bui-gap-7{gap:var(--bui-space-7)}.lg\:bui-gap-8{gap:var(--bui-space-8)}.lg\:bui-gap-9{gap:var(--bui-space-9)}.lg\:bui-gap-10{gap:var(--bui-space-10)}.lg\:bui-gap-11{gap:var(--bui-space-11)}.lg\:bui-gap-12{gap:var(--bui-space-12)}.lg\:bui-gap-13{gap:var(--bui-space-13)}.lg\:bui-gap-14{gap:var(--bui-space-14)}}@media (width>=1536px){.xl\:bui-gap{gap:var(--gap-xl)}.xl\:bui-gap-0\.5{gap:var(--bui-space-0_5)}.xl\:bui-gap-1{gap:var(--bui-space-1)}.xl\:bui-gap-1\.5{gap:var(--bui-space-1_5)}.xl\:bui-gap-2{gap:var(--bui-space-2)}.xl\:bui-gap-3{gap:var(--bui-space-3)}.xl\:bui-gap-4{gap:var(--bui-space-4)}.xl\:bui-gap-5{gap:var(--bui-space-5)}.xl\:bui-gap-6{gap:var(--bui-space-6)}.xl\:bui-gap-7{gap:var(--bui-space-7)}.xl\:bui-gap-8{gap:var(--bui-space-8)}.xl\:bui-gap-9{gap:var(--bui-space-9)}.xl\:bui-gap-10{gap:var(--bui-space-10)}.xl\:bui-gap-11{gap:var(--bui-space-11)}.xl\:bui-gap-12{gap:var(--bui-space-12)}.xl\:bui-gap-13{gap:var(--bui-space-13)}.xl\:bui-gap-14{gap:var(--bui-space-14)}}.bui-align-start{align-items:start}.bui-align-center{align-items:center}.bui-align-end{align-items:end}.bui-align-baseline{align-items:baseline}.bui-align-stretch{align-items:stretch}.bui-jc-start{justify-content:start}.bui-jc-center{justify-content:center}.bui-jc-end{justify-content:end}.bui-jc-between{justify-content:space-between}.bui-fd-row{flex-direction:row}.bui-fd-column{flex-direction:column}.bui-fd-row-reverse{flex-direction:row-reverse}.bui-fd-column-reverse{flex-direction:column-reverse}@media (width>=640px){.xs\:bui-align-start{align-items:start}.xs\:bui-align-center{align-items:center}.xs\:bui-align-end{align-items:end}.xs\:bui-align-stretch{align-items:stretch}.xs\:bui-jc-start{justify-content:start}.xs\:bui-jc-center{justify-content:center}.xs\:bui-jc-end{justify-content:end}.xs\:bui-jc-between{justify-content:space-between}.xs\:bui-fd-row{flex-direction:row}.xs\:bui-fd-column{flex-direction:column}.xs\:bui-fd-row-reverse{flex-direction:row-reverse}.xs\:bui-fd-column-reverse{flex-direction:column-reverse}}@media (width>=768px){.sm\:bui-align-start{align-items:start}.sm\:bui-align-center{align-items:center}.sm\:bui-align-end{align-items:end}.sm\:bui-align-stretch{align-items:stretch}.sm\:bui-jc-start{justify-content:start}.sm\:bui-jc-center{justify-content:center}.sm\:bui-jc-end{justify-content:end}.sm\:bui-jc-between{justify-content:space-between}.sm\:bui-fd-row{flex-direction:row}.sm\:bui-fd-column{flex-direction:column}.sm\:bui-fd-row-reverse{flex-direction:row-reverse}.sm\:bui-fd-column-reverse{flex-direction:column-reverse}}@media (width>=1024px){.md\:bui-align-start{align-items:start}.md\:bui-align-center{align-items:center}.md\:bui-align-end{align-items:end}.md\:bui-align-stretch{align-items:stretch}.md\:bui-jc-start{justify-content:start}.md\:bui-jc-center{justify-content:center}.md\:bui-jc-end{justify-content:end}.md\:bui-jc-between{justify-content:space-between}.md\:bui-fd-row{flex-direction:row}.md\:bui-fd-column{flex-direction:column}.md\:bui-fd-row-reverse{flex-direction:row-reverse}.md\:bui-fd-column-reverse{flex-direction:column-reverse}}@media (width>=1280px){.lg\:bui-align-start{align-items:start}.lg\:bui-align-center{align-items:center}.lg\:bui-align-end{align-items:end}.lg\:bui-align-stretch{align-items:stretch}.lg\:bui-jc-start{justify-content:start}.lg\:bui-jc-center{justify-content:center}.lg\:bui-jc-end{justify-content:end}.lg\:bui-jc-between{justify-content:space-between}.lg\:bui-fd-row{flex-direction:row}.lg\:bui-fd-column{flex-direction:column}.lg\:bui-fd-row-reverse{flex-direction:row-reverse}.lg\:bui-fd-column-reverse{flex-direction:column-reverse}}@media (width>=1536px){.xl\:bui-align-start{align-items:start}.xl\:bui-align-center{align-items:center}.xl\:bui-align-end{align-items:end}.xl\:bui-align-stretch{align-items:stretch}.xl\:bui-jc-start{justify-content:start}.xl\:bui-jc-center{justify-content:center}.xl\:bui-jc-end{justify-content:end}.xl\:bui-jc-between{justify-content:space-between}.xl\:bui-fd-row{flex-direction:row}.xl\:bui-fd-column{flex-direction:column}.xl\:bui-fd-row-reverse{flex-direction:row-reverse}.xl\:bui-fd-column-reverse{flex-direction:column-reverse}}} \ No newline at end of file diff --git a/docs-ui/src/app/about/page.mdx b/docs-ui/src/app/about/page.mdx deleted file mode 100644 index 2c87a3d03e..0000000000 --- a/docs-ui/src/app/about/page.mdx +++ /dev/null @@ -1,32 +0,0 @@ -# About Backstage UI - -Backstage UI is a design system created specifically for Backstage, built with React, TypeScript, and vanilla CSS. -This open-source library is hosted in the Backstage monorepo. While it can be used in other projects, Backstage UI -is designed to deliver a consistent, accessible, and extensible experience tailored to Backstage users. - -## Philosophy - -Backstage empowers product teams to build software faster and with greater quality. Its extensibility, -however, required us to rethink how to deliver a consistent and accessible user experience. Our goal is -to enable plugin creators to design plugins that seamlessly integrate with Backstage's look and feel while -still allowing customization to match individual brands. - -Instead of reinventing the wheel, we chose to focus on layout and styling while leveraging existing headless -component libraries for functionality. This approach allows us to dedicate our efforts to creating a cohesive -and flexible theming system. - -## Team - -Backstage UI is designed and maintained primarily by Spotify's Backstage team, leveraging Spotify's expertise in -crafting high-quality design and technology. Drawing from our experience in building reliable and intuitive -user experiences for the music industry, we've created a design system that looks great and works seamlessly. - -## Community - -Backstage UI is an open-source project and we welcome contributions from the community. If you are interested in -contributing to Backstage UI, please read our [contributing guide](https://github.com/backstage/backstage/blob/master/CONTRIBUTING.md) -and our [code of conduct](https://github.com/backstage/backstage/blob/master/CODE_OF_CONDUCT.md). - -## License - -Backstage UI is licensed under the Apache 2.0 license. See the [LICENSE](https://github.com/backstage/backstage/blob/master/LICENSE) file for more details. diff --git a/docs-ui/src/app/components/[slug]/page.tsx b/docs-ui/src/app/components/[slug]/page.tsx index d6f98c8f93..1422334471 100644 --- a/docs-ui/src/app/components/[slug]/page.tsx +++ b/docs-ui/src/app/components/[slug]/page.tsx @@ -7,9 +7,7 @@ export default async function Page({ }) { const { slug } = await params; - const { default: Component } = await import( - `@/content/components/${slug}.mdx` - ); + const { default: Component } = await import(`@/content/${slug}.mdx`); return ; } diff --git a/docs-ui/src/app/components/page.mdx b/docs-ui/src/app/components/page.mdx new file mode 100644 index 0000000000..bd18005d19 --- /dev/null +++ b/docs-ui/src/app/components/page.mdx @@ -0,0 +1,173 @@ +import { ComponentCards, ComponentCard } from '@/components/ComponentCards'; +import { LayoutComponents } from '@/components/LayoutComponents'; +import { CodeBlock } from '@/components/CodeBlock'; + +# Components + +## Layout Components + +We built a couple of layout components to help you build responsive elements +that will be consistent with the rest of your Backstage instance. These +components are opinionated and use TypeScript to ensure that the props you +provide are the ones coming from the theme. + + + Hello World + + Project 1 + Project 2 + + +`} +/> + + + +## Components + +### Actions + + + + + + + + +### Content display + + + + + + +### Selection and inputs + + + + + + + + + + + +### Navigation + + + + + + + + +### Images and icons + + + + + + +### Feedback indicators + + + + + + +### Typography + + + + diff --git a/docs-ui/src/app/iconography/page.mdx b/docs-ui/src/app/iconography/page.mdx deleted file mode 100644 index b4b1aa2183..0000000000 --- a/docs-ui/src/app/iconography/page.mdx +++ /dev/null @@ -1,24 +0,0 @@ -import { CodeBlock } from '@/components/CodeBlock'; -import { IconLibrary } from '@/components/IconLibrary'; - -# Iconography - -All our default icons are provided by [Remix Icon](https://remixicon.com/). We -don't import all icons to reduce the bundle size but we cherry pick a nice -selection for you to use in your application. The list of names is set down -below. To use an icon, you can use the `Icon` component and pass the name of -the icon you want to use. - -`} /> - -## Icon overrides - -You can override any icons in our library by using the `IconProvider` at the root of your application. - -
Custom Icon
}} />`} -/> - -## Icon library - - diff --git a/docs-ui/src/app/install/page.mdx b/docs-ui/src/app/install/page.mdx deleted file mode 100644 index 329d477eca..0000000000 --- a/docs-ui/src/app/install/page.mdx +++ /dev/null @@ -1,36 +0,0 @@ -import { CodeBlock } from '@/components/CodeBlock'; - -# How to install Backstage UI - -## 1. Import the package - -Import the package using a package manager. - - - -## 2. Import the css files - -Import the global CSS file at the root of your application. - -```tsx -import '@backstage/ui/css/styles.css'; -``` - -## 3. Start building ✨ - -Now you can start building your plugin using the new design system. - -```tsx -import { Flex, Button, Text } from '@backstage/ui'; - - - Hello World - -; -``` - -## Next steps - -Now that you have the basics down, you can start building your plugin using the new design system. -Please familiarise yourself first with our theming principles. This will help you understand the core concepts of the design system. -If you have any questions, please reach out to us on [Discord](https://discord.gg/MUpMjP2). diff --git a/docs-ui/src/app/layout.module.css b/docs-ui/src/app/layout.module.css index 7399fc2485..df89f02283 100644 --- a/docs-ui/src/app/layout.module.css +++ b/docs-ui/src/app/layout.module.css @@ -1,28 +1,46 @@ .container { position: relative; z-index: 20; - width: calc(100% - 64px); - margin-inline: 0 16px; - margin-left: 32px; - margin-right: 32px; - margin-top: 112px; margin-bottom: 48px; - padding-inline: 40px; - padding-block: 48px; - background-color: var(--panel); - border-radius: 0.5rem; + padding-inline: 24px; +} + +.contentWrapper { + display: flex; + width: 100%; + max-width: 1200px; + margin: 0 auto; + justify-content: center; + flex-direction: column; } .content { - width: 100%; - max-width: 960px; - margin: 0 auto; + display: flex; + gap: 40px; +} + +.contentInner { + flex: 1; + min-width: 0; + overflow-x: hidden; +} + +.toc { + display: none; } @media (min-width: 768px) { .container { - width: calc(100% - 332px - 40px); - margin-left: 332px; + width: calc(100% - 260px); + margin-left: 260px; margin-right: 40px; } } + +@media (min-width: 1280px) { + .toc { + display: block; + width: 240px; + flex-shrink: 0; + } +} diff --git a/docs-ui/src/app/layout.tsx b/docs-ui/src/app/layout.tsx index d197112431..ad056626d8 100644 --- a/docs-ui/src/app/layout.tsx +++ b/docs-ui/src/app/layout.tsx @@ -1,9 +1,10 @@ import type { Metadata } from 'next'; -import { Sidebar } from '../components/Sidebar'; +import { Sidebar } from '@/components/Sidebar/Sidebar'; import { Toolbar } from '@/components/Toolbar'; -import { StickyHeader } from '../components/StickyHeader/StickyHeader'; import { Providers } from './providers'; import { CustomTheme } from '@/components/CustomTheme'; +import { TableOfContents } from '@/components/TableOfContents'; +import { MobileBottomNav } from '@/components/MobileBottomNav'; import styles from './layout.module.css'; import '../css/globals.css'; @@ -51,11 +52,18 @@ export default async function RootLayout({ - -
-
{children}
+
+ +
+
{children}
+ +
+
+
diff --git a/docs-ui/src/app/layout/page.mdx b/docs-ui/src/app/layout/page.mdx deleted file mode 100644 index 3c3b998490..0000000000 --- a/docs-ui/src/app/layout/page.mdx +++ /dev/null @@ -1,42 +0,0 @@ -import { LayoutComponents } from '@/components/LayoutComponents'; -import { CodeBlock } from '@/components/CodeBlock'; - -# Layout - -Backstage UI is made for extensibility. We built this library to make it easy for any -Backstage plugin creator to be able to build their ideas at speed ensuring -consistency across the rest of your ecosystem. Each component is designed to -be editable to match your need but sometimes you want to have more control -over the layout of your page. To help you with that, we created a set of -layout components that you can use to build your own layouts. All of these -components are built to extend on our theming system, making it easy for you -to build your own layouts. Sometimes these components are not enough so we -created a set of helpers to be used with any CSS-in-JS library. - -## Layout Components - -We built a couple of layout components to help you build responsive elements -that will be consistent with the rest of your Backstage instance. These -components are opinionated and use TypeScript to ensure that the props you -provide are the ones coming from the theme. - - - Hello World - - Project 1 - Project 2 - - -`} -/> - - - -## Layout Helpers - -Sometimes you want to use global tokens dynamically outside of React -components. To help you with that we would like to provide a set of helpers -that you can use in your code. These helpers are not available just yet but we -are working on it. diff --git a/docs-ui/src/app/page.mdx b/docs-ui/src/app/page.mdx index f31cf4524e..9a796b401b 100644 --- a/docs-ui/src/app/page.mdx +++ b/docs-ui/src/app/page.mdx @@ -1,174 +1,83 @@ -import { ComponentCards, ComponentCard } from '@/components/ComponentCards'; +import { CodeBlock } from '@/components/CodeBlock'; +import { Banner } from '@/components/Banner'; +import { snippet } from './snippets'; -## Welcome to Backstage UI, the new design library for Backstage plugins. +# Welcome to Backstage UI -This project is still under active development but we will make sure to document -the API as we go. We are aiming to improve the general UI of Backstage and -plugins across Backstage. This new library will take time to build but we are -building it incrementally with not conflict with the existing theming system. +Backstage UI is a design system created specifically for Backstage, built with React, TypeScript, and vanilla CSS. +This open-source library is hosted in the Backstage monorepo. While it can be used in other projects, Backstage UI +is designed to deliver a consistent, accessible, and extensible experience tailored to Backstage users. -### Actions +## Import BUI's global styles - - - - - - +Backstage UI works by importing a global CSS file at the root of your application. This file includes all the default styles for the components. +First, you'll need to install the package using a package manager. For example, if you're using Yarn: -### Layout + - - - - - - - - +);`} +/> - - - - - - - - + -### Navigation +## Use BUI components - - - - - - +As a plugin maintainer, you can use BUI components in your plugin. As mentioned above, you should not import the styles +again in your plugin as this will be handled at the root of your application. To get started, just add the library to +your plugin and import the components you need. -### Images and icons + - - - - - -### Feedback indicators - - - - - - -### Typography - - - - + ## Support Now that you have the basics down, you can start building your plugin using the new design system. Please familiarise yourself first with our theming principles. This will help you understand the core concepts of the design system. If you have any questions, please reach out to us on [Discord](https://discord.gg/MUpMjP2). + +## Philosophy + +Backstage empowers product teams to build software faster and with greater quality. Its extensibility, +however, required us to rethink how to deliver a consistent and accessible user experience. Our goal is +to enable plugin creators to design plugins that seamlessly integrate with Backstage's look and feel while +still allowing customization to match individual brands. + +Instead of reinventing the wheel, we chose to focus on layout and styling while leveraging existing headless +component libraries for functionality. This approach allows us to dedicate our efforts to creating a cohesive +and flexible theming system. + +## Team + +Backstage UI is designed and maintained primarily by Spotify's Backstage team, leveraging Spotify's expertise in +crafting high-quality design and technology. Drawing from our experience in building reliable and intuitive +user experiences for the music industry, we've created a design system that looks great and works seamlessly. + +## Community + +Backstage UI is an open-source project and we welcome contributions from the community. If you are interested in +contributing to Backstage UI, please read our [contributing guide](https://github.com/backstage/backstage/blob/master/CONTRIBUTING.md) +and our [code of conduct](https://github.com/backstage/backstage/blob/master/CODE_OF_CONDUCT.md). + +## License + +Backstage UI is licensed under the Apache 2.0 license. See the [LICENSE](https://github.com/backstage/backstage/blob/master/LICENSE) file for more details. diff --git a/docs-ui/src/app/responsive/page.mdx b/docs-ui/src/app/responsive/page.mdx deleted file mode 100644 index 9d37b1329f..0000000000 --- a/docs-ui/src/app/responsive/page.mdx +++ /dev/null @@ -1,107 +0,0 @@ -import * as Table from '@/components/Table'; -import { Chip } from '@/components/Chip'; -import { CodeBlock } from '@/components/CodeBlock'; - -# Responsive - -Backstage UI is built on a responsive design system, meaning that the components are -designed to adapt to different screen sizes. By default we offer a set of -breakpoints that you can use to create responsive components. - -## Breakpoints - - - - - Breakpoint prefix - Minimum width - CSS - - - - - - xs - - - 0px - - - {`{ ... }`} - - - - - sm - - - 640px - - - {`@media (min-width: 640px) { ... }`} - - - - - md - - - 768px - - - {`@media (min-width: 768px) { ... }`} - - - - - lg - - - 1024px - - - {`@media (min-width: 1024px) { ... }`} - - - - - xl - - - 1280px - - - {`@media (min-width: 1280px) { ... }`} - - - - - 2xl - - - 1536px - - - {`@media (min-width: 1536px) { ... }`} - - - - - -## Responsive components - -Backstage UI components are designed to be responsive, meaning that they will adapt -to different screen sizes. Not every component is responsive, but the ones -that are will have a prop to control the responsive behavior. - -The behaviour is the same for each component. For each prop, instead of adding -the value, you add an object with the value and the breakpoint prefix. - -Button - -// Responsive value - -`} /> diff --git a/docs-ui/src/app/snippets.ts b/docs-ui/src/app/snippets.ts new file mode 100644 index 0000000000..7e63005172 --- /dev/null +++ b/docs-ui/src/app/snippets.ts @@ -0,0 +1,6 @@ +export const snippet = `import { Flex, Button, Text } from '@backstage/ui'; + + + Hello World + +;`; diff --git a/docs-ui/src/app/theming/page.mdx b/docs-ui/src/app/tokens/page.mdx similarity index 86% rename from docs-ui/src/app/theming/page.mdx rename to docs-ui/src/app/tokens/page.mdx index 73275020bc..8e9031c81c 100644 --- a/docs-ui/src/app/theming/page.mdx +++ b/docs-ui/src/app/tokens/page.mdx @@ -3,48 +3,109 @@ import * as Table from '@/components/Table'; import { Chip } from '@/components/Chip'; import { customTheme } from '@/snippets/code-snippets'; -# Theming +# Tokens -Backstage UI theming is built entirely on CSS, without relying on any CSS-in-JS libraries. -At its core, it provides a solid default theme that is easily customizable using a -comprehensive set of CSS variables. Additionally, it enables anyone to adapt the design -to their specific needs. Each component comes with fixed class names, making customization -even more straightforward. +## Responsive breakpoints -## Light & Dark modes +Backstage UI is built on a responsive design system, meaning that the components are +designed to adapt to different screen sizes. By default we offer a set of +breakpoints that you can use to create responsive components. -By default, Backstage UI supports both light and dark modes using the `data-theme-mode` attribute. -The light theme is applied by default if no `data-theme-mode` attribute is specified. To create -a custom theme, you'll need to define both light and dark modes as outlined below. If -only one mode is defined, the other will fall back to the default theme. + + + + Breakpoint prefix + Minimum width + CSS + + + + + + xs + + + 0px + + + {`{ ... }`} + + + + + sm + + + 640px + + + {`@media (min-width: 640px) { ... }`} + + + + + md + + + 768px + + + {`@media (min-width: 768px) { ... }`} + + + + + lg + + + 1024px + + + {`@media (min-width: 1024px) { ... }`} + + + + + xl + + + 1280px + + + {`@media (min-width: 1280px) { ... }`} + + + + + 2xl + + + 1536px + + + {`@media (min-width: 1536px) { ... }`} + + + + -## How to create your own theme +Backstage UI components are designed to be responsive, meaning that they will adapt +to different screen sizes. Not every component is responsive, but the ones +that are will have a prop to control the responsive behavior. -In our [started guide](/), we ask you to import two css files. The `core.css` file includes -the default set of variables. We recommend to keep this file in place and add your own theme -on top of it. `core.css` also include an opinionated reset. If you decided to remove `core.css` -you will have to provide your own reset css. +The behaviour is the same for each component. For each prop, instead of adding +the value, you add an object with the value and the breakpoint prefix. -Here's an example of how your theme.css file should look like: + + -## CSS class name structure +// Responsive value -All Backstage UI components come with a set of CSS classes that you can use to style them. To make it -easier to identify the class name you can use, we use a specific structure for the class names. +`} /> - - -Every component has a unique prefix `.bui-` followed by the component name. Component props -are represented using the `data-` attribute. That way, class names are easily identifiable. - -## Available CSS variables - -### Base colors +## Base colors These colors are used for special purposes like ring, scrollbar, ... @@ -123,7 +184,7 @@ These colors are used for special purposes like ring, scrollbar, ... -### Core background colors +## Core background colors These colors are used for the background of your application. We are mostly using for now a single elevated background for panels. `--bui-bg` should mostly use as the main background @@ -224,9 +285,9 @@ color of your app. -### Foreground colors +## Foreground colors -Foreground colours are meant to work in pair with a background colours. Typeically this would work +Foreground colours are meant to work in pair with a background colours. Typically this would work for icons, texts, shapes, ... Use a matching name to know what foreground color to use. These colors are prefixed with `fg` to make it easier to identify. @@ -329,7 +390,7 @@ are prefixed with `fg` to make it easier to identify. -### Border colors +## Border colors These border colors are mostly meant to be used as borders on top of any components with low contrast to help as a separator with the different background colors. @@ -391,7 +452,7 @@ low contrast to help as a separator with the different background colors. -### Special colors +## Special colors These colors are used for special purposes like ring, scrollbar, ... @@ -424,7 +485,7 @@ These colors are used for special purposes like ring, scrollbar, ... -### Font families +## Font families We have two fonts that we use across Backstage UI. The first one is the sans-serif font that we use for the body of the application. The second one is the @@ -446,14 +507,14 @@ monospace font that we use for code blocks and tables. - --bui-font-mono + --bui-font-monospace The monospace font for the theme. -### Font weights +## Font weights We have two font weights that we use across Backstage UI. Regular or Bold. @@ -480,7 +541,7 @@ We have two font weights that we use across Backstage UI. Regular or Bold. -### Spacing +## Spacing We built a spacing system based on a single value `--bui-space`. This value is used to calculate the spacing for all the components. By default if you would like to @@ -619,7 +680,7 @@ tokens for pretty much each spacing properties like padding, margin, gaps, ... -### Radius +## Radius We use a radius system to make sure that the components have a consistent look and feel. diff --git a/docs-ui/src/components/CodeBlock/client.tsx b/docs-ui/src/components/CodeBlock/client.tsx index b4ee98f2f3..666d880358 100644 --- a/docs-ui/src/components/CodeBlock/client.tsx +++ b/docs-ui/src/components/CodeBlock/client.tsx @@ -1,10 +1,14 @@ 'use client'; import { CodeBlockProps } from '.'; -import { Text } from '@backstage/ui'; import styles from './styles.module.css'; import parse from 'html-react-parser'; +// Convert markdown-style backticks to HTML code tags +function parseMarkdownInlineCode(text: string): string { + return text.replace(/`([^`]+)`/g, '$1'); +} + export const CodeBlockClient = ({ out, title, @@ -16,7 +20,7 @@ export const CodeBlockClient = ({
{title && (
- {title} + {parse(parseMarkdownInlineCode(title))}
)}
{parse(out)}
diff --git a/docs-ui/src/components/CodeBlock/index.tsx b/docs-ui/src/components/CodeBlock/index.tsx index 4c0fe72960..1467bca465 100644 --- a/docs-ui/src/components/CodeBlock/index.tsx +++ b/docs-ui/src/components/CodeBlock/index.tsx @@ -1,4 +1,5 @@ import type { BundledLanguage } from 'shiki'; +import { transformerNotationDiff } from '@shikijs/transformers'; import { codeToHtml } from 'shiki'; import { CodeBlockClient } from './client'; @@ -8,14 +9,24 @@ export interface CodeBlockProps { code?: string; } -export async function CodeBlock({ lang = 'tsx', title, code }: CodeBlockProps) { - const out = await codeToHtml(code || '', { - lang: lang, +export async function CodeBlock(props: CodeBlockProps) { + const { lang = 'tsx', title, code } = props; + let out = await codeToHtml(code || '', { + lang, + transformers: [transformerNotationDiff({ matchAlgorithm: 'v3' })], themes: { - light: 'min-light', + light: 'github-dark', dark: 'min-dark', }, }); + // Remove background-color from the pre tag to use our theme colors + out = out.replace( + /style="([^"]*?)background-color:[^;]+;?([^"]*?)"/g, + 'style="$1$2"', + ); + // Clean up empty style attributes + out = out.replace(/style=""\s?/g, ''); + return ; } diff --git a/docs-ui/src/components/CodeBlock/styles.module.css b/docs-ui/src/components/CodeBlock/styles.module.css index 767982a582..30e1837f60 100644 --- a/docs-ui/src/components/CodeBlock/styles.module.css +++ b/docs-ui/src/components/CodeBlock/styles.module.css @@ -1,23 +1,29 @@ .codeBlock { - border-radius: 4px; - border: 1px solid var(--border); + border-radius: 8px; position: relative; background: transparent; overflow-x: auto; font-family: var(--font-mono); - background-color: #fff; + background-color: var(--code-bg); margin-bottom: 1rem; } -[data-theme-mode='dark'] .codeBlock { - background-color: #121212; +.title { + padding: 12px 20px; + font-size: 0.8125rem; + color: #fff; + font-family: var(--font-regular); + font-weight: var(--font-weight-bold); + background-color: var(--code-title); } -.title { - border-bottom: 1px solid var(--border); - padding: 12px 20px; - font-size: 0.875rem; - color: var(--secondary); +.title code { + background-color: rgba(255, 255, 255, 0.1); + padding: 0.2rem 0.375rem; + border-radius: 0.25rem; + color: #fff; + font-size: 0.75rem; + font-weight: var(--font-weight-bold); } .code { diff --git a/docs-ui/src/components/ComponentCards/ComponentCards.module.css b/docs-ui/src/components/ComponentCards/ComponentCards.module.css index 688010110c..8eb9dbd3d0 100644 --- a/docs-ui/src/components/ComponentCards/ComponentCards.module.css +++ b/docs-ui/src/components/ComponentCards/ComponentCards.module.css @@ -28,7 +28,7 @@ display: flex; flex-direction: column; justify-content: flex-end; - background-color: var(--panel); + background-color: var(--bg); border-radius: 8px; border: 1px solid var(--border); padding: 16px; @@ -36,7 +36,7 @@ min-height: 120px; &:hover { - background-color: var(--panel-hover); + background-color: var(--bg-hover); } } diff --git a/docs-ui/src/components/CustomTheme/customTheme.tsx b/docs-ui/src/components/CustomTheme/customTheme.tsx index c70d59173a..ae765d7d88 100644 --- a/docs-ui/src/components/CustomTheme/customTheme.tsx +++ b/docs-ui/src/components/CustomTheme/customTheme.tsx @@ -6,9 +6,9 @@ import { sass } from '@codemirror/lang-sass'; import styles from './styles.module.css'; import { usePlayground } from '@/utils/playground-context'; import { AnimatePresence, motion } from 'motion/react'; -import { Icon } from '../../../../packages/ui'; import { createTheme } from '@uiw/codemirror-themes'; import { tags as t } from '@lezer/highlight'; +import { RiArrowDownSLine } from '@remixicon/react'; const defaultTheme = `:root { --bui-bg-solid: #000; @@ -125,7 +125,12 @@ export const CustomTheme = () => { className={styles.buttonClose} onClick={() => setOpen(!open)} > - +
diff --git a/docs-ui/src/components/CustomTheme/styles.module.css b/docs-ui/src/components/CustomTheme/styles.module.css index 05604c48be..dddd3ff7e4 100644 --- a/docs-ui/src/components/CustomTheme/styles.module.css +++ b/docs-ui/src/components/CustomTheme/styles.module.css @@ -5,7 +5,7 @@ right: 16px; width: 240px; height: 47px; - background-color: var(--panel); + background-color: var(--bg); border-radius: 0.375rem; border: 1px solid var(--border); display: flex; @@ -33,7 +33,7 @@ height: 46px; flex-shrink: 0; border-bottom: 1px solid var(--border); - background-color: var(--panel); + background-color: var(--bg); display: flex; justify-content: space-between; align-items: center; diff --git a/docs-ui/src/components/HeadlessBanners/styles.module.css b/docs-ui/src/components/HeadlessBanners/styles.module.css index 384327c394..c0066eccff 100644 --- a/docs-ui/src/components/HeadlessBanners/styles.module.css +++ b/docs-ui/src/components/HeadlessBanners/styles.module.css @@ -1,6 +1,6 @@ .container { display: flex; - background-color: var(--panel); + background-color: var(--bg); padding: 1.5rem; border-radius: 0.25rem; border: 1px solid var(--border); diff --git a/docs-ui/src/components/IconLibrary/IconLibrary.tsx b/docs-ui/src/components/IconLibrary/IconLibrary.tsx deleted file mode 100644 index bad51e62bf..0000000000 --- a/docs-ui/src/components/IconLibrary/IconLibrary.tsx +++ /dev/null @@ -1,22 +0,0 @@ -'use client'; - -import { Text, Icon, icons } from '../../../../packages/ui'; -import type { IconNames } from '../../../../packages/ui'; -import styles from './styles.module.css'; - -export const IconLibrary = () => { - const list = Object.keys(icons); - - return ( -
- {list.map(icon => ( -
-
- -
- {icon} -
- ))} -
- ); -}; diff --git a/docs-ui/src/components/IconLibrary/index.ts b/docs-ui/src/components/IconLibrary/index.ts deleted file mode 100644 index b21b4511d3..0000000000 --- a/docs-ui/src/components/IconLibrary/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { IconLibrary } from './IconLibrary'; diff --git a/docs-ui/src/components/IconLibrary/styles.module.css b/docs-ui/src/components/IconLibrary/styles.module.css deleted file mode 100644 index c9f317ef6a..0000000000 --- a/docs-ui/src/components/IconLibrary/styles.module.css +++ /dev/null @@ -1,28 +0,0 @@ -.library { - display: grid; - grid-template-columns: repeat(6, 1fr); - gap: 1rem; -} - -.item { - display: flex; - flex-direction: column; - align-items: center; - gap: 8px; -} - -.icon { - display: flex; - width: 100%; - justify-content: center; - align-items: center; - height: 80px; - border-radius: 0.5rem; - border: 1px solid var(--border); - background-color: var(--panel); - transition: background-color 0.2s ease-in-out; - - &:hover { - background-color: var(--surface-1); - } -} diff --git a/docs-ui/src/components/LayoutComponents/LayoutComponents.module.css b/docs-ui/src/components/LayoutComponents/LayoutComponents.module.css index a09a04fb5c..9009cdb6a4 100644 --- a/docs-ui/src/components/LayoutComponents/LayoutComponents.module.css +++ b/docs-ui/src/components/LayoutComponents/LayoutComponents.module.css @@ -19,7 +19,7 @@ & .content { flex: none; - background-color: var(--panel); + background-color: var(--bg); border: 1px solid var(--border); border-radius: 4px; width: 100%; diff --git a/docs-ui/src/components/MobileBottomNav/AnimatedMenuIcon.module.css b/docs-ui/src/components/MobileBottomNav/AnimatedMenuIcon.module.css new file mode 100644 index 0000000000..632f005b65 --- /dev/null +++ b/docs-ui/src/components/MobileBottomNav/AnimatedMenuIcon.module.css @@ -0,0 +1,40 @@ +.icon { + display: block; +} + +.topLine, +.middleLine, +.bottomLine { + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); +} + +/* Menu state (default) */ +.topLine { + transform-origin: 10px 5px; + transform: rotate(0deg); +} + +.middleLine { + opacity: 1; + transform-origin: 10px 10px; +} + +.bottomLine { + transform-origin: 10px 15px; + transform: rotate(0deg); +} + +/* Close state (when open) */ +.icon[data-open='true'] .topLine { + transform-origin: 10px 5px; + transform: translateY(5px) rotate(45deg); +} + +.icon[data-open='true'] .middleLine { + opacity: 0; +} + +.icon[data-open='true'] .bottomLine { + transform-origin: 10px 15px; + transform: translateY(-5px) rotate(-45deg); +} diff --git a/docs-ui/src/components/MobileBottomNav/AnimatedMenuIcon.tsx b/docs-ui/src/components/MobileBottomNav/AnimatedMenuIcon.tsx new file mode 100644 index 0000000000..80edd64d43 --- /dev/null +++ b/docs-ui/src/components/MobileBottomNav/AnimatedMenuIcon.tsx @@ -0,0 +1,56 @@ +import styles from './AnimatedMenuIcon.module.css'; + +interface AnimatedMenuIconProps { + isOpen: boolean; + size?: number; +} + +export const AnimatedMenuIcon = ({ + isOpen, + size = 20, +}: AnimatedMenuIconProps) => { + return ( + + + + + + + + ); +}; diff --git a/docs-ui/src/components/MobileBottomNav/MobileBottomNav.module.css b/docs-ui/src/components/MobileBottomNav/MobileBottomNav.module.css new file mode 100644 index 0000000000..9c128c3959 --- /dev/null +++ b/docs-ui/src/components/MobileBottomNav/MobileBottomNav.module.css @@ -0,0 +1,205 @@ +/* Mobile Bottom Navigation Island */ +.mobileBottomNav { + display: block; + position: fixed; + bottom: 0; + left: 0; + right: 0; + padding: 16px; + z-index: 300; + pointer-events: none; + + @media (min-width: 768px) { + display: none; + } +} + +.island { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + margin: 0 auto; + width: 50vw; + padding: 8px 12px 8px 12px; + border-radius: 32px; + pointer-events: auto; + background-color: #000; +} + +:global([data-theme-mode='dark']) .island { + background-color: #fff; + box-shadow: 0 4px 24px rgba(0, 0, 0, 0.3); +} + +.menuButton { + display: flex; + align-items: center; + justify-content: center; + background: none; + border: none; + cursor: pointer; + padding: 8px; + border-radius: 50%; + transition: background-color 0.2s ease-in-out; + + /* White icons on light mode (black island) */ + color: white; + + &:hover { + background-color: rgba(255, 255, 255, 0.1); + } +} + +:global([data-theme-mode='dark']) .menuButton { + /* Black icons on dark mode (white island) */ + color: black; + + &:hover { + background-color: rgba(0, 0, 0, 0.1); + } +} + +.buttonGroup { + display: flex; + align-items: center; + gap: 4px; + padding: 4px; + border-radius: 24px; + background-color: rgba(255, 255, 255, 0.2); +} + +:global([data-theme-mode='dark']) .buttonGroup { + background-color: rgba(0, 0, 0, 0.08); +} + +.buttonGroup button { + display: flex; + align-items: center; + justify-content: center; + background: none; + border: none; + cursor: pointer; + padding: 6px 12px; + border-radius: 20px; + transition: background-color 0.2s ease-in-out; + + /* White icons on light mode */ + color: white; + + &[data-selected] { + background-color: rgba(255, 255, 255, 0.2); + } + + &:hover { + background-color: rgba(255, 255, 255, 0.15); + } +} + +:global([data-theme-mode='dark']) .buttonGroup button { + /* Black icons on dark mode */ + color: black; + + &[data-selected] { + background-color: rgba(0, 0, 0, 0.2); + } + + &:hover { + background-color: rgba(0, 0, 0, 0.15); + } +} + +/* Mobile Menu Bottom Sheet */ +.overlay { + position: fixed; + inset: 0; + z-index: 200; + background-color: rgba(38, 38, 38, 0.7); + display: flex; + align-items: flex-end; + justify-content: center; + + &[data-entering] { + animation: overlayFadeIn 200ms ease-out; + } + + &[data-exiting] { + animation: overlayFadeOut 200ms ease-in; + } +} + +@keyframes overlayFadeIn { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +@keyframes overlayFadeOut { + from { + opacity: 1; + } + to { + opacity: 0; + } +} + +.modal { + width: calc(100% - 48px); + max-height: 50vh; + background-color: var(--bg); + border-radius: 16px; + box-shadow: 0 -4px 24px rgba(0, 0, 0, 0.2); + display: flex; + flex-direction: column; + outline: none; + margin-bottom: 96px; + + &[data-entering] { + animation: slideUp 300ms cubic-bezier(0.32, 0.72, 0, 1); + } + + &[data-exiting] { + animation: slideDown 200ms cubic-bezier(0.32, 0.72, 0, 1); + } +} + +@keyframes slideUp { + from { + transform: translateY(8%); + } + to { + transform: translateY(0); + } +} + +@keyframes slideDown { + from { + transform: translateY(0); + } + to { + transform: translateY(8%); + } +} + +.dialog { + display: flex; + flex-direction: column; + height: 100%; + max-height: 70vh; + outline: none; + overflow: hidden; +} + +.menuContent { + flex: 1; + overflow-y: auto; + overflow-x: hidden; + padding: 16px; + display: flex; + flex-direction: column; + gap: 2px; + min-height: 0; +} diff --git a/docs-ui/src/components/MobileBottomNav/MobileBottomNav.tsx b/docs-ui/src/components/MobileBottomNav/MobileBottomNav.tsx new file mode 100644 index 0000000000..52ec2ee9d9 --- /dev/null +++ b/docs-ui/src/components/MobileBottomNav/MobileBottomNav.tsx @@ -0,0 +1,44 @@ +'use client'; + +import { useState } from 'react'; +import { RiMoonLine, RiSunLine } from '@remixicon/react'; +import { Button, ToggleButton, ToggleButtonGroup } from 'react-aria-components'; +import styles from './MobileBottomNav.module.css'; +import { usePlayground } from '@/utils/playground-context'; +import { MobileMenu } from './MobileMenu'; +import { AnimatedMenuIcon } from './AnimatedMenuIcon'; + +export const MobileBottomNav = () => { + const [isMenuOpen, setIsMenuOpen] = useState(false); + const { selectedTheme, setSelectedTheme } = usePlayground(); + + return ( + <> +
+
+ + + + + + + + + +
+
+ setIsMenuOpen(false)} /> + + ); +}; diff --git a/docs-ui/src/components/MobileBottomNav/MobileMenu.tsx b/docs-ui/src/components/MobileBottomNav/MobileMenu.tsx new file mode 100644 index 0000000000..3a3a485aa5 --- /dev/null +++ b/docs-ui/src/components/MobileBottomNav/MobileMenu.tsx @@ -0,0 +1,31 @@ +'use client'; + +import { Dialog, Modal, ModalOverlay } from 'react-aria-components'; +import styles from './MobileBottomNav.module.css'; +import { Navigation } from '@/components/Navigation'; + +interface MobileMenuProps { + isOpen: boolean; + onClose: () => void; +} + +export const MobileMenu = ({ isOpen, onClose }: MobileMenuProps) => { + return ( + + + + {({ close }) => ( +
+ +
+ )} +
+
+
+ ); +}; diff --git a/docs-ui/src/components/MobileBottomNav/index.tsx b/docs-ui/src/components/MobileBottomNav/index.tsx new file mode 100644 index 0000000000..4b7449c9c5 --- /dev/null +++ b/docs-ui/src/components/MobileBottomNav/index.tsx @@ -0,0 +1 @@ +export { MobileBottomNav } from './MobileBottomNav'; diff --git a/docs-ui/src/components/Navigation/Navigation.module.css b/docs-ui/src/components/Navigation/Navigation.module.css new file mode 100644 index 0000000000..f5e1312ab1 --- /dev/null +++ b/docs-ui/src/components/Navigation/Navigation.module.css @@ -0,0 +1,100 @@ +.topNav { + & ul { + margin: 0; + padding: 0; + list-style: none; + display: flex; + flex-direction: column; + gap: 2px; + } + + & li { + margin: 0; + padding: 0; + list-style: none; + } + + & li div, + & li a { + display: flex; + align-items: center; + gap: 8px; + padding: 8px 12px; + border-radius: 4px; + cursor: pointer; + color: var(--primary); + text-decoration: none; + + &:hover { + background-color: var(--action); + + &[data-disabled='true'] { + background-color: transparent; + } + } + + &[data-active='true'] { + background-color: var(--action); + + &[data-disabled='true'] { + background-color: transparent; + } + } + + &[data-disabled='true'] { + opacity: 0.5; + cursor: not-allowed; + } + } +} + +.sectionTitle { + font-size: 0.6875rem; + font-weight: 500; + padding: 0 12px 4px; + color: var(--secondary); + margin-top: 40px; + text-transform: uppercase; + + &:first-child { + margin-top: 12px; + } +} + +.line { + text-decoration: none; + align-items: center; + width: 100%; + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + height: 28px; + padding: 0 12px; + border-radius: 4px; + color: var(--primary); + flex-shrink: 0; + + &:hover { + background-color: var(--action); + } + + &.active { + background-color: var(--action); + } + + &.active .lineTitle { + color: var(--primary); + } +} + +.lineTitle { + font-size: 14px; + font-weight: 400; + color: var(--primary); +} + +.lineStatus { + font-size: 14px; + color: var(--secondary); +} diff --git a/docs-ui/src/components/Navigation/Navigation.tsx b/docs-ui/src/components/Navigation/Navigation.tsx new file mode 100644 index 0000000000..13fa87661f --- /dev/null +++ b/docs-ui/src/components/Navigation/Navigation.tsx @@ -0,0 +1,125 @@ +'use client'; + +import Link from 'next/link'; +import { usePathname } from 'next/navigation'; +import { Fragment } from 'react'; +import clsx from 'clsx'; +import { + RiCollageLine, + RiFileHistoryLine, + RiHazeLine, + RiPaletteLine, + RiServiceLine, + RiStackLine, +} from '@remixicon/react'; +import { components, layoutComponents } from '@/utils/data'; +import styles from './Navigation.module.css'; + +interface NavigationProps { + onLinkClick?: () => void; +} + +const data = [ + { + title: 'Layout Components', + content: layoutComponents, + url: '/components', + }, + { + title: 'Components', + content: components, + url: '/components', + }, +]; + +export const Navigation = ({ onLinkClick }: NavigationProps) => { + const pathname = usePathname(); + + return ( + <> + + {data.map(section => { + return ( + +
{section.title}
+ + {section.content.map(item => { + const isActive = pathname === `${section.url}/${item.slug}`; + + return ( + +
{item.title}
+
+ {item.status === 'alpha' && 'Alpha'} + {item.status === 'beta' && 'Beta'} + {item.status === 'inProgress' && 'In Progress'} + {item.status === 'stable' && 'Stable'} + {item.status === 'deprecated' && 'Deprecated'} +
+ + ); + })} +
+ ); + })} + + ); +}; diff --git a/docs-ui/src/components/Navigation/index.tsx b/docs-ui/src/components/Navigation/index.tsx new file mode 100644 index 0000000000..61f39d15c9 --- /dev/null +++ b/docs-ui/src/components/Navigation/index.tsx @@ -0,0 +1 @@ +export { Navigation } from './Navigation'; diff --git a/docs-ui/src/components/PageTitle/index.tsx b/docs-ui/src/components/PageTitle/index.tsx index 540e19eb36..c43ff75fc7 100644 --- a/docs-ui/src/components/PageTitle/index.tsx +++ b/docs-ui/src/components/PageTitle/index.tsx @@ -5,15 +5,12 @@ import styles from './PageTitle.module.css'; export const PageTitle = ({ title, description, - type = 'component', }: { title: string; - type?: string; description: string; }) => { return (
-
{type}
>({ {Object.keys(data).map(n => { const enumValues = - data[n].values === 'icon' - ? Object.keys(icons).map(icon => {icon}) - : Array.isArray(data[n].values) && - data[n].values.map(t => {t}); + Array.isArray(data[n].values) && + data[n].values.map(t => {t}); return ( diff --git a/docs-ui/src/components/PropsTable/TypePopup.module.css b/docs-ui/src/components/PropsTable/TypePopup.module.css index f8240566c7..b1423a7e4a 100644 --- a/docs-ui/src/components/PropsTable/TypePopup.module.css +++ b/docs-ui/src/components/PropsTable/TypePopup.module.css @@ -25,7 +25,7 @@ border: 1px solid var(--border); box-shadow: 0 8px 20px rgba(0 0 0 / 0.1); border-radius: 6px; - background: var(--panel); + background: var(--bg); color: var(--primary); outline: none; /* max-width: 400px; */ @@ -43,7 +43,7 @@ .arrow svg { display: block; - fill: var(--panel); + fill: var(--bg); stroke: var(--border); stroke-width: 1px; transform: rotate(180deg); diff --git a/docs-ui/src/components/Toolbar/Logo.tsx b/docs-ui/src/components/Sidebar/Logo.tsx similarity index 100% rename from docs-ui/src/components/Toolbar/Logo.tsx rename to docs-ui/src/components/Sidebar/Logo.tsx diff --git a/docs-ui/src/components/Sidebar/Sidebar.module.css b/docs-ui/src/components/Sidebar/Sidebar.module.css index 75ac541d9f..83c1e0bde5 100644 --- a/docs-ui/src/components/Sidebar/Sidebar.module.css +++ b/docs-ui/src/components/Sidebar/Sidebar.module.css @@ -6,17 +6,24 @@ .sidebar { display: block; position: fixed; - top: 112px; + top: 0; left: 0; - width: 332px; - height: calc(100vh - 112px); + width: 260px; + height: 100vh; color: var(--primary); overflow: hidden; } } +.logoContainer { + height: 80px; + display: flex; + align-items: center; + padding-left: 32px; +} + .root { - height: 100%; + height: calc(100% - 80px); } .scrollbar { @@ -56,67 +63,13 @@ } .content { - padding: 0 20px 64px; + padding: 32px 20px 64px; } .menu { - display: flex; - flex-direction: row; - position: relative; -} - -.section { width: 100%; display: flex; flex-direction: column; gap: 2px; -} - -.sectionTitle { - font-size: 0.6875rem; - font-weight: 500; - padding: 0 12px 4px; - color: var(--secondary); - margin-top: 40px; - text-transform: uppercase; - - &:first-child { - margin-top: 12px; - } -} - -.line { - text-decoration: none; - align-items: center; - width: 100%; - display: flex; - flex-direction: row; - justify-content: space-between; - align-items: center; - height: 26px; - padding: 0 12px; - border-radius: 4px; - - &:hover { - background-color: var(--action); - } -} - -.line.active { - background-color: var(--action); -} - -.line.active .lineTitle { - color: var(--primary); -} - -.lineTitle { - font-size: 14px; - font-weight: 400; - color: var(--primary); -} - -.lineStatus { - font-size: 14px; - color: var(--secondary); + position: relative; } diff --git a/docs-ui/src/components/Sidebar/Sidebar.tsx b/docs-ui/src/components/Sidebar/Sidebar.tsx new file mode 100644 index 0000000000..d9c16f1905 --- /dev/null +++ b/docs-ui/src/components/Sidebar/Sidebar.tsx @@ -0,0 +1,28 @@ +'use client'; + +import styles from './Sidebar.module.css'; +import { ScrollArea } from '@base-ui-components/react/scroll-area'; +import { Logo } from './Logo'; +import { Navigation } from '@/components/Navigation'; + +export const Sidebar = () => { + return ( +
+
+ +
+ + +
+
+ +
+
+
+ + + +
+
+ ); +}; diff --git a/docs-ui/src/components/Sidebar/index.tsx b/docs-ui/src/components/Sidebar/index.tsx deleted file mode 100644 index 59e297e26e..0000000000 --- a/docs-ui/src/components/Sidebar/index.tsx +++ /dev/null @@ -1,104 +0,0 @@ -'use client'; - -import styles from './Sidebar.module.css'; -import { - components, - overview, - layoutComponents, - coreConcepts, -} from '@/utils/data'; -import { ScrollArea } from '@base-ui-components/react/scroll-area'; -import Link from 'next/link'; -import { usePathname } from 'next/navigation'; -import { motion } from 'motion/react'; -import { Fragment } from 'react'; - -const data = [ - { - title: 'Overview', - content: overview, - url: '', - }, - { - title: 'Core Concepts', - content: coreConcepts, - url: '', - }, - { - title: 'Layout Components', - content: layoutComponents, - url: '/components', - }, - { - title: 'Components', - content: components, - url: '/components', - }, -]; - -export const Sidebar = () => { - const pathname = usePathname(); - const isPlayground = pathname.includes('/playground'); - - return ( -
- - -
-
- - {data.map(section => { - return ( - -
{section.title}
- - {section.content.map(item => { - const isActive = - pathname === `${section.url}/${item.slug}`; - - return ( - -
{item.title}
-
- {item.status === 'alpha' && 'Alpha'} - {item.status === 'beta' && 'Beta'} - {item.status === 'inProgress' && 'In Progress'} - {item.status === 'stable' && 'Stable'} - {item.status === 'deprecated' && 'Deprecated'} -
- - ); - })} -
- ); - })} -
-
-
-
- - - -
-
- ); -}; diff --git a/docs-ui/src/components/Snippet/styles.module.css b/docs-ui/src/components/Snippet/styles.module.css index bb0d1cbc50..3141564293 100644 --- a/docs-ui/src/components/Snippet/styles.module.css +++ b/docs-ui/src/components/Snippet/styles.module.css @@ -5,9 +5,9 @@ } .preview { - border-radius: 4px; + border-radius: 8px; box-shadow: inset 0 0 0 1px var(--border); - background-color: var(--bg); + background-color: var(--bui-bg); padding: 1px; position: relative; } diff --git a/docs-ui/src/components/StickyHeader/StickyHeader.module.css b/docs-ui/src/components/StickyHeader/StickyHeader.module.css deleted file mode 100644 index 7cfc197f83..0000000000 --- a/docs-ui/src/components/StickyHeader/StickyHeader.module.css +++ /dev/null @@ -1,98 +0,0 @@ -.stickyHeader { - position: fixed; - top: 0px; - left: 32px; - right: 32px; - z-index: 99999; - background-color: var(--background); - padding: 16px 20px 16px 40px; - display: flex; - justify-content: space-between; - align-items: center; - backdrop-filter: blur(10px); - pointer-events: auto; - isolation: isolate; - transform: translateZ(0); - opacity: 0; - width: calc(100% - 64px); - mask-image: linear-gradient(to bottom, black 0%, black 80%, transparent 100%); - -webkit-mask-image: linear-gradient( - to bottom, - black 0%, - black 80%, - transparent 100% - ); -} - -@media (max-width: 768px) { - .stickyHeader { - display: none; - } -} - -.right { - display: flex; - align-items: center; - gap: 24px; -} - -.name { - font-size: 24px; - color: var(--text-primary); - font-weight: 400; -} - -.version { - font-size: 14px; - color: var(--text-secondary); - font-weight: 500; -} - -.actions { - display: flex; - align-items: center; - gap: 16px; -} - -.versionLinks { - display: flex; - align-items: center; - - a { - width: 48px; - height: 48px; - display: flex; - align-items: center; - justify-content: center; - color: var(--secondary); - transition: color 0.2s ease-in-out, background-color 0.2s ease-in-out; - border-radius: 48px; - - &:hover { - color: var(--primary); - background-color: var(--action); - } - } -} - -@media (max-width: 768px) { - .stickyHeader { - padding: 12px 16px; - } - - .right { - gap: 16px; - } - - .version { - font-size: 12px; - } -} - -@media (min-width: 768px) { - .stickyHeader { - width: calc(100% - 332px - 40px); - left: 332px; - right: 40px; - } -} diff --git a/docs-ui/src/components/StickyHeader/StickyHeader.tsx b/docs-ui/src/components/StickyHeader/StickyHeader.tsx deleted file mode 100644 index 804d759715..0000000000 --- a/docs-ui/src/components/StickyHeader/StickyHeader.tsx +++ /dev/null @@ -1,50 +0,0 @@ -'use client'; - -import { motion, useScroll, useTransform, circOut } from 'framer-motion'; -import { RiGithubLine, RiNpmjsLine } from '@remixicon/react'; -import { ThemeSelector } from '../Toolbar/theme'; -import { ThemeNameSelector } from '../Toolbar/theme-name'; -import { useCurrentPage } from '@/hooks/useCurrentPage'; -import styles from './StickyHeader.module.css'; - -export const StickyHeader = () => { - const { scrollY } = useScroll(); - const currentPage = useCurrentPage(); - - // Transform scroll position to opacity only - const opacity = useTransform(scrollY, [100, 200], [0, 1], { - clamp: false, - }); - - const yPos = useTransform(scrollY, [200, 500], [-60, 0], { - clamp: true, - ease: circOut, - }); - - return ( - -
{currentPage || 'Backstage UI'}
-
- - - -
-
- ); -}; diff --git a/docs-ui/src/components/Table/styles.module.css b/docs-ui/src/components/Table/styles.module.css index 5a5ebab450..8f38ac6979 100644 --- a/docs-ui/src/components/Table/styles.module.css +++ b/docs-ui/src/components/Table/styles.module.css @@ -17,7 +17,7 @@ padding: 12px 16px !important; border: none !important; text-align: left; - background-color: var(--panel) !important; + background-color: var(--bg) !important; font-size: 16px; & p { diff --git a/docs-ui/src/components/TableOfContents/TableOfContents.module.css b/docs-ui/src/components/TableOfContents/TableOfContents.module.css new file mode 100644 index 0000000000..7275c24520 --- /dev/null +++ b/docs-ui/src/components/TableOfContents/TableOfContents.module.css @@ -0,0 +1,93 @@ +.container { + position: sticky; + top: 24px; + max-height: calc(100vh - 48px); + overflow-y: auto; + padding: 16px 0; +} + +.list { + list-style: none; + padding: 0; + margin: 0; + position: relative; +} + +.list::before { + content: ''; + position: absolute; + left: 0; + top: 0; + bottom: 0; + width: 2px; + background-color: var(--border); +} + +.indicator { + position: absolute; + left: 0; + width: 2px; + background-color: var(--primary); + transition: top 0.3s cubic-bezier(0.4, 0, 0.2, 1), + height 0.3s cubic-bezier(0.4, 0, 0.2, 1); + pointer-events: none; + z-index: 1; +} + +.item { + position: relative; + margin: 0; +} + +.itemNested { + padding-left: 16px; +} + +.link { + display: block; + width: 100%; + text-align: left; + background: none; + border: none; + padding: 6px 16px; + font-size: 0.875rem; + color: var(--text-secondary); + cursor: pointer; + transition: color 0.2s ease; + line-height: 1.4; + position: relative; +} + +.link:hover { + color: var(--text); +} + +.itemActive .link { + color: var(--primary); + font-weight: 500; +} + +/* Hide scrollbar for webkit browsers */ +.container::-webkit-scrollbar { + width: 4px; +} + +.container::-webkit-scrollbar-track { + background: transparent; +} + +.container::-webkit-scrollbar-thumb { + background: var(--border); + border-radius: 2px; +} + +.container::-webkit-scrollbar-thumb:hover { + background: var(--text-secondary); +} + +/* Responsive: hide on smaller screens */ +@media (max-width: 1280px) { + .container { + display: none; + } +} diff --git a/docs-ui/src/components/TableOfContents/TableOfContents.tsx b/docs-ui/src/components/TableOfContents/TableOfContents.tsx new file mode 100644 index 0000000000..d18265415b --- /dev/null +++ b/docs-ui/src/components/TableOfContents/TableOfContents.tsx @@ -0,0 +1,132 @@ +'use client'; + +import { useEffect, useState } from 'react'; +import { usePathname } from 'next/navigation'; +import styles from './TableOfContents.module.css'; + +interface Heading { + id: string; + text: string; + level: number; +} + +export function TableOfContents() { + const [headings, setHeadings] = useState([]); + const [activeId, setActiveId] = useState(''); + const [indicatorTop, setIndicatorTop] = useState(0); + const [indicatorHeight, setIndicatorHeight] = useState(0); + const pathname = usePathname(); + + useEffect(() => { + // Extract all H2 and H3 headings from the document + const elements = Array.from( + document.querySelectorAll('h2[id], h3[id]'), + ) as HTMLHeadingElement[]; + + const headingData: Heading[] = elements.map(element => ({ + id: element.id, + text: (element.textContent || '').replace('#', '').trim(), + level: parseInt(element.tagName.substring(1)), + })); + + setHeadings(headingData); + + // Set initial active heading (first visible heading or first heading) + if (headingData.length > 0) { + const viewportTop = window.scrollY + 100; // offset for header + const visibleHeading = elements.find(element => { + const rect = element.getBoundingClientRect(); + return rect.top + window.scrollY >= viewportTop - 200; + }); + setActiveId(visibleHeading?.id || headingData[0].id); + } + + // Set up IntersectionObserver to track visible headings + const observerOptions = { + rootMargin: '-80px 0px -80% 0px', + threshold: 1, + }; + + const observerCallback = (entries: IntersectionObserverEntry[]) => { + entries.forEach(entry => { + if (entry.isIntersecting) { + setActiveId(entry.target.id); + } + }); + }; + + const observer = new IntersectionObserver( + observerCallback, + observerOptions, + ); + + elements.forEach(element => observer.observe(element)); + + return () => { + elements.forEach(element => observer.unobserve(element)); + }; + }, [pathname]); + + // Update indicator position when activeId changes + useEffect(() => { + if (activeId) { + const activeElement = document.querySelector( + `[data-toc-id="${activeId}"]`, + ) as HTMLElement; + if (activeElement) { + const list = activeElement.closest('ul'); + if (list) { + const listRect = list.getBoundingClientRect(); + const elementRect = activeElement.getBoundingClientRect(); + setIndicatorTop(elementRect.top - listRect.top); + setIndicatorHeight(elementRect.height); + } + } + } + }, [activeId, headings]); + + const handleClick = (id: string) => { + const element = document.getElementById(id); + if (element) { + element.scrollIntoView({ behavior: 'smooth', block: 'start' }); + // Update URL without scrolling (already handled above) + window.history.pushState(null, '', `#${id}`); + } + }; + + if (headings.length === 0) { + return null; + } + + return ( + + ); +} diff --git a/docs-ui/src/components/TableOfContents/index.ts b/docs-ui/src/components/TableOfContents/index.ts new file mode 100644 index 0000000000..ffe4593ca7 --- /dev/null +++ b/docs-ui/src/components/TableOfContents/index.ts @@ -0,0 +1 @@ +export { TableOfContents } from './TableOfContents'; diff --git a/docs-ui/src/components/Toolbar/Toolbar.module.css b/docs-ui/src/components/Toolbar/Toolbar.module.css index f0e58f2edb..581fc599b3 100644 --- a/docs-ui/src/components/Toolbar/Toolbar.module.css +++ b/docs-ui/src/components/Toolbar/Toolbar.module.css @@ -1,76 +1,77 @@ .toolbar { - position: fixed; display: flex; - top: 0; - left: 32px; - right: 32px; - height: 112px; - z-index: 10; + height: 80px; align-items: center; justify-content: space-between; - padding-left: 0.25rem; -} + margin-bottom: 24px; -.left { - /* width: 296px; */ - display: flex; - align-items: center; - gap: 0.5rem; - padding-right: 20px; -} - -.right { - flex: 1; - display: flex; - align-items: center; - justify-content: flex-end; -} - -.actions { - display: none; -} - -.version { - display: none; -} - -.versionLinks { - display: flex; - align-items: center; - - a { - width: 48px; - height: 48px; - display: flex; - align-items: center; - justify-content: center; - color: var(--secondary); - transition: color 0.2s ease-in-out, background-color 0.2s ease-in-out; - border-radius: 48px; - - &:hover { - color: var(--primary); - background-color: var(--action); - } - } -} - -@media (min-width: 600px) { - .actions { - display: flex; - align-items: center; - gap: 1rem; - } -} - -@media (min-width: 768px) { - .toolbar { + @media (min-width: 768px) { right: 40px; } } +.breadcrumb { + display: flex; + align-items: center; + gap: 0.5rem; + font-size: 0.875rem; + font-weight: 500; +} + +.logoMobile { + display: block; + + @media (min-width: 768px) { + display: none; + } +} + +.breadcrumbDesktop { + display: none; + + @media (min-width: 768px) { + display: flex; + align-items: center; + gap: 0.5rem; + } +} + +.breadcrumbLink { + color: var(--secondary); + text-decoration: none; + cursor: pointer; + + &:hover { + color: var(--primary); + text-decoration: underline; + transition: color 0.2s ease-in-out; + text-decoration-thickness: 1px; + text-underline-offset: 4px; + } +} + +.breadcrumbSeparator { + color: var(--secondary); + flex-shrink: 0; +} + +.breadcrumbCurrent { + color: var(--primary); +} + +.actions { + display: none; + + @media (min-width: 320px) { + display: flex; + display: flex; + align-items: center; + gap: 0.5rem; + } +} + @media (min-width: 820px) { - .right { + .content { justify-content: space-between; } @@ -79,18 +80,164 @@ align-items: center; justify-content: center; /* background-color: var(--action); */ - border: 1px solid var(--border2); - border-radius: 48px; - padding-inline: 20px; - color: var(--primary); - font-size: 0.875rem; - font-weight: 500; - height: 48px; } } -@media (min-width: 960px) { - .left { - width: 296px; +.bubble { + display: flex; + align-items: center; + justify-content: center; + background-color: var(--bg); + border: 1px solid var(--border); + border-radius: 32px; + padding-inline: 16px; + color: var(--primary); + font-size: 0.875rem; + font-weight: 500; + height: 32px; + cursor: pointer; + gap: 4px; + + &[data-selected] { + background-color: var(--action); + color: var(--primary); + } + + &:hover { + background-color: var(--action); + transition: background-color 0.2s ease-in-out; + } + + &[data-hide-tablet] { + display: none; + } + + @media (min-width: 1024px) { + &[data-hide-tablet] { + display: flex; + } + } +} + +.buttonGroup { + display: flex; + align-items: center; + gap: 4px; + border: 1px solid var(--border); + border-radius: 32px; + padding-inline: 4px; + height: 32px; + display: none; + + @media (min-width: 480px) { + display: flex; + } +} + +.buttonGroup button { + height: 24px; + background-color: var(--bg); + border: none; + border-radius: 28px; + padding-inline: 16px; + color: var(--primary); + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + gap: 4px; + + &[data-selected] { + background-color: var(--action); + color: var(--primary); + } + + &:hover { + background-color: var(--action); + transition: background-color 0.2s ease-in-out; + } +} + +.Popup { + box-sizing: border-box; + padding-block: 0.25rem; + border-radius: 0.375rem; + background-color: var(--bg); + color: var(--color-gray-900); + border: 1px solid var(--border); + padding-inline: 0.25rem; + transform-origin: var(--transform-origin); + transition: transform 150ms, opacity 150ms; + + &[data-starting-style], + &[data-ending-style] { + opacity: 0; + transform: scale(0.9); + } + + &[data-side='none'] { + transition: none; + transform: none; + opacity: 1; + } + + @media (prefers-color-scheme: light) { + outline: 1px solid var(--color-gray-200); + box-shadow: 0px 10px 15px -3px var(--color-gray-200), + 0px 4px 6px -4px var(--color-gray-200); + } + + @media (prefers-color-scheme: dark) { + outline: 1px solid var(--color-gray-300); + outline-offset: -1px; + } +} + +.Popup[data-trigger='Select'] { + min-width: var(--trigger-width); + + .ListBox { + display: block; + width: unset; + max-height: inherit; + min-height: unset; + border: none; + + .react-aria-Header { + padding-left: 1.571rem; + } + } + + .Item { + position: relative; + padding: 0 0.571rem 0 1.571rem; + height: 2rem; + display: flex; + align-items: center; + + &[data-focus-visible] { + outline: none; + } + + &[data-selected] { + font-weight: 600; + background: unset; + color: var(--text-color); + + &::before { + content: '✓'; + content: '✓' / ''; + alt: ' '; + position: absolute; + left: 4px; + } + } + + &[data-focused], + &[data-pressed] { + background: var(--bg); + color: var(--primary); + cursor: pointer; + } } } diff --git a/docs-ui/src/components/Toolbar/Toolbar.tsx b/docs-ui/src/components/Toolbar/Toolbar.tsx index 29570739f8..e7defd5e8a 100644 --- a/docs-ui/src/components/Toolbar/Toolbar.tsx +++ b/docs-ui/src/components/Toolbar/Toolbar.tsx @@ -1,52 +1,168 @@ 'use client'; -import { RiGithubLine, RiNpmjsLine } from '@remixicon/react'; -import { motion, useScroll, useTransform } from 'framer-motion'; -import { useRef } from 'react'; -import { Logo } from './Logo'; -import { ThemeSelector } from './theme'; -import { ThemeNameSelector } from './theme-name'; +import { + RiArrowDownSLine, + RiArrowRightSLine, + RiGithubLine, + RiMoonLine, + RiSunLine, +} from '@remixicon/react'; +import { + Button, + ListBox, + ListBoxItem, + Popover, + Select, + SelectValue, + ToggleButton, + ToggleButtonGroup, +} from 'react-aria-components'; import styles from './Toolbar.module.css'; +import { usePlayground } from '@/utils/playground-context'; +import { usePathname } from 'next/navigation'; +import Link from 'next/link'; +import { components, layoutComponents } from '@/utils/data'; +import { Logo } from '@/components/Sidebar/Logo'; interface ToolbarProps { version: string; } -export const Toolbar = ({ version }: ToolbarProps) => { - const containerRef = useRef(null); - const { scrollY } = useScroll(); +const themes = [ + { name: 'Backstage', value: 'backstage' }, + { name: 'Spotify', value: 'spotify' }, + { name: 'Custom theme', value: 'custom' }, +]; - // Transform scroll velocity to vertical movement - const y = useTransform(scrollY, [0, 100], [0, -20], { - clamp: false, - }); +export const Toolbar = ({ version }: ToolbarProps) => { + const { + selectedTheme, + setSelectedTheme, + selectedThemeName, + setSelectedThemeName, + } = usePlayground(); + + const pathname = usePathname(); + + // Determine breadcrumb content based on current path + const getBreadcrumb = () => { + const allComponents = [...components, ...layoutComponents]; + + // Root page + if (pathname === '/') { + return { section: null, title: 'Getting Started' }; + } + + // Components index page + if (pathname === '/components') { + return { section: null, title: 'Components' }; + } + + // Component detail pages + if (pathname?.startsWith('/components/')) { + const slug = pathname.split('/components/')[1]; + const component = allComponents.find(c => c.slug === slug); + return { + section: 'Components', + sectionLink: '/components', + title: component?.title || slug, + }; + } + + // Tokens page + if (pathname === '/tokens') { + return { section: null, title: 'Tokens' }; + } + + // Changelog page + if (pathname === '/changelog') { + return { section: null, title: 'Changelog' }; + } + + return { section: null, title: '' }; + }; + + const breadcrumb = getBreadcrumb(); return ( -
-
- -
- -
Version {version} - Alpha
-
- - - +
+
+
+
- +
+ {breadcrumb.section && breadcrumb.sectionLink ? ( + <> + + {breadcrumb.section} + + + + {breadcrumb.title} + + + ) : ( + {breadcrumb.title} + )} +
+
+
+ + + Version {version} + + + + + + + + + + +
); }; diff --git a/docs-ui/src/components/Toolbar/theme-name.module.css b/docs-ui/src/components/Toolbar/theme-name.module.css deleted file mode 100644 index 1e1943b886..0000000000 --- a/docs-ui/src/components/Toolbar/theme-name.module.css +++ /dev/null @@ -1,110 +0,0 @@ -.Select { - box-sizing: border-box; - display: flex; - align-items: center; - justify-content: space-between; - gap: 0.75rem; - height: 3rem; - padding-left: 1.25rem; - padding-right: 1rem; - margin: 0; - outline: 0; - border: 1px solid var(--color-gray-200); - border-radius: 3rem; - font-family: inherit; - font-size: 1rem; - line-height: 1.5rem; - color: var(--color-gray-900); - cursor: pointer; - user-select: none; - background-color: var(--action); - - &:focus-visible { - outline: 2px solid var(--color-blue); - outline-offset: -1px; - } -} - -.Popup { - box-sizing: border-box; - padding-block: 0.25rem; - border-radius: 0.375rem; - background-color: var(--panel); - color: var(--color-gray-900); - border: 1px solid var(--border); - padding-inline: 0.25rem; - transform-origin: var(--transform-origin); - transition: transform 150ms, opacity 150ms; - - &[data-starting-style], - &[data-ending-style] { - opacity: 0; - transform: scale(0.9); - } - - &[data-side='none'] { - transition: none; - transform: none; - opacity: 1; - } - - @media (prefers-color-scheme: light) { - outline: 1px solid var(--color-gray-200); - box-shadow: 0px 10px 15px -3px var(--color-gray-200), - 0px 4px 6px -4px var(--color-gray-200); - } - - @media (prefers-color-scheme: dark) { - outline: 1px solid var(--color-gray-300); - outline-offset: -1px; - } -} - -.Popup[data-trigger='Select'] { - min-width: var(--trigger-width); - - .ListBox { - display: block; - width: unset; - max-height: inherit; - min-height: unset; - border: none; - - .react-aria-Header { - padding-left: 1.571rem; - } - } - - .Item { - position: relative; - padding: 0 0.571rem 0 1.571rem; - height: 2rem; - display: flex; - align-items: center; - - &[data-focus-visible] { - outline: none; - } - - &[data-selected] { - font-weight: 600; - background: unset; - color: var(--text-color); - - &::before { - content: '✓'; - content: '✓' / ''; - alt: ' '; - position: absolute; - left: 4px; - } - } - - &[data-focused], - &[data-pressed] { - background: var(--bg); - color: var(--primary); - cursor: pointer; - } - } -} diff --git a/docs-ui/src/components/Toolbar/theme-name.tsx b/docs-ui/src/components/Toolbar/theme-name.tsx deleted file mode 100644 index 06c8ec66ec..0000000000 --- a/docs-ui/src/components/Toolbar/theme-name.tsx +++ /dev/null @@ -1,44 +0,0 @@ -'use client'; - -import { - Button, - ListBox, - ListBoxItem, - Popover, - Select, - SelectValue, -} from 'react-aria-components'; -import styles from './theme-name.module.css'; -import { Icon } from '@backstage/ui'; -import { usePlayground } from '@/utils/playground-context'; - -const themes = [ - { name: 'Backstage', value: 'backstage' }, - { name: 'Spotify', value: 'spotify' }, - { name: 'Custom theme', value: 'custom' }, -]; - -export const ThemeNameSelector = () => { - const { selectedThemeName, setSelectedThemeName } = usePlayground(); - - return ( - - ); -}; diff --git a/docs-ui/src/components/Toolbar/theme.module.css b/docs-ui/src/components/Toolbar/theme.module.css deleted file mode 100644 index c5490a72d7..0000000000 --- a/docs-ui/src/components/Toolbar/theme.module.css +++ /dev/null @@ -1,79 +0,0 @@ -.tabs { - border-radius: 0.375rem; - width: 100%; -} - -.tabsTheme { - width: 100px; - border-radius: 0.375rem; -} - -.list { - display: flex; - position: relative; - z-index: 0; - gap: 0.25rem; -} - -.tab { - display: flex; - align-items: center; - justify-content: center; - border: 0; - margin: 0; - outline: 0; - background: none; - appearance: none; - color: var(--secondary); - user-select: none; - flex: 1; - cursor: pointer; - width: 3rem; - height: 3rem; - - &[data-selected] { - color: var(--primary); - - & p { - color: var(--primary); - } - } - - @media (hover: hover) { - &:hover { - color: var(--primary); - } - } - - &:focus-visible { - position: relative; - - &::before { - content: ''; - position: absolute; - inset: 0.25rem 0; - border-radius: 0.25rem; - outline: 2px solid var(--action); - outline-offset: -1px; - } - } -} - -.tab p { - color: var(--secondary) !important; -} - -.indicator { - position: absolute; - z-index: -1; - left: 0; - top: 50%; - translate: var(--active-tab-left) -50%; - width: var(--active-tab-width); - height: 3rem; - border-radius: 3rem; - background-color: var(--action); - transition-property: translate, width; - transition-duration: 200ms; - transition-timing-function: ease-in-out; -} diff --git a/docs-ui/src/components/Toolbar/theme.tsx b/docs-ui/src/components/Toolbar/theme.tsx deleted file mode 100644 index ef3d842056..0000000000 --- a/docs-ui/src/components/Toolbar/theme.tsx +++ /dev/null @@ -1,28 +0,0 @@ -'use client'; - -import { Tabs } from '@base-ui-components/react/tabs'; -import { Icon } from '@backstage/ui'; -import { usePlayground } from '@/utils/playground-context'; -import styles from './theme.module.css'; - -export const ThemeSelector = () => { - const { selectedTheme, setSelectedTheme } = usePlayground(); - - return ( - - - - - - - - - - - - ); -}; diff --git a/docs-ui/src/content/accordion.mdx b/docs-ui/src/content/accordion.mdx new file mode 100644 index 0000000000..2694f8d144 --- /dev/null +++ b/docs-ui/src/content/accordion.mdx @@ -0,0 +1,128 @@ +import { PropsTable } from '@/components/PropsTable'; +import { Snippet } from '@/components/Snippet'; +import { CodeBlock } from '@/components/CodeBlock'; +import { AccordionSnippet } from '@/snippets/stories-snippets'; +import { + accordionPropDefs, + accordionTriggerPropDefs, + accordionPanelPropDefs, + accordionGroupPropDefs, + accordionUsageSnippet, + accordionWithSubtitleSnippet, + accordionCustomTriggerSnippet, + accordionDefaultExpandedSnippet, + accordionGroupSingleOpenSnippet, + accordionGroupMultipleOpenSnippet, +} from './accordion.props'; +import { PageTitle } from '@/components/PageTitle'; +import { Theming } from '@/components/Theming'; +import { ChangelogComponent } from '@/components/ChangelogComponent'; + + + +} + code={accordionUsageSnippet} +/> + +## Usage + + + +## API reference + +### Accordion + +Root container for the accordion. Renders a `
` element. + + + +### AccordionTrigger + +Trigger component with built-in animated chevron icon. Renders a heading element (defaults to `

`, configurable via `level` prop) wrapping a ``; + export const buttonAsLinkSnippet = `import { ButtonLink } from '@backstage/ui'; diff --git a/docs-ui/src/content/components/card.mdx b/docs-ui/src/content/card.mdx similarity index 100% rename from docs-ui/src/content/components/card.mdx rename to docs-ui/src/content/card.mdx diff --git a/docs-ui/src/content/components/card.props.ts b/docs-ui/src/content/card.props.ts similarity index 100% rename from docs-ui/src/content/components/card.props.ts rename to docs-ui/src/content/card.props.ts diff --git a/docs-ui/src/content/components/checkbox.mdx b/docs-ui/src/content/checkbox.mdx similarity index 100% rename from docs-ui/src/content/components/checkbox.mdx rename to docs-ui/src/content/checkbox.mdx diff --git a/docs-ui/src/content/components/checkbox.props.ts b/docs-ui/src/content/checkbox.props.ts similarity index 53% rename from docs-ui/src/content/components/checkbox.props.ts rename to docs-ui/src/content/checkbox.props.ts index c7ee69280e..ce0f1033ff 100644 --- a/docs-ui/src/content/components/checkbox.props.ts +++ b/docs-ui/src/content/checkbox.props.ts @@ -5,31 +5,32 @@ import { } from '@/utils/propDefs'; export const checkboxPropDefs: Record = { - label: { - type: 'string', - responsive: false, - }, - defaultChecked: { + children: { type: 'enum', - values: ['boolean', "'indeterminate'"], + values: ['React.ReactNode'], responsive: false, }, - checked: { - type: 'enum', - values: ['boolean', "'indeterminate'"], - responsive: false, - }, - onChange: { - type: 'enum', - values: ["(checked: boolean | 'indeterminate') => void"], - responsive: false, - }, - disabled: { + isSelected: { type: 'enum', values: ['boolean'], responsive: false, }, - required: { + defaultSelected: { + type: 'enum', + values: ['boolean'], + responsive: false, + }, + onChange: { + type: 'enum', + values: ['(isSelected: boolean) => void'], + responsive: false, + }, + isDisabled: { + type: 'enum', + values: ['boolean'], + responsive: false, + }, + isRequired: { type: 'enum', values: ['boolean'], responsive: false, @@ -48,13 +49,13 @@ export const checkboxPropDefs: Record = { export const checkboxUsageSnippet = `import { Checkbox } from '@backstage/ui'; -`; +Accept terms`; -export const checkboxDefaultSnippet = ``; +export const checkboxDefaultSnippet = `Accept terms and conditions`; -export const checkboxVariantsSnippet = ` - - - - -`; +export const checkboxVariantsSnippet = ` + Unchecked + Checked + Disabled + Checked & Disabled +`; diff --git a/docs-ui/src/content/components/avatar.props.ts b/docs-ui/src/content/components/avatar.props.ts deleted file mode 100644 index 8cc53eeff3..0000000000 --- a/docs-ui/src/content/components/avatar.props.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { classNamePropDefs, stylePropDefs } from '@/utils/propDefs'; -import type { PropDef } from '@/utils/propDefs'; - -export const avatarPropDefs: Record = { - src: { - type: 'string', - }, - name: { - type: 'string', - }, - size: { - type: 'enum', - values: ['small', 'medium', 'large'], - default: 'medium', - responsive: true, - }, - ...classNamePropDefs, - ...stylePropDefs, -}; - -export const snippetUsage = `import { Avatar } from '@backstage/ui'; - -`; - -export const snippetSizes = ` - - - -`; - -export const snippetFallback = ``; diff --git a/docs-ui/src/content/components/collapsible.mdx b/docs-ui/src/content/components/collapsible.mdx deleted file mode 100644 index 948da323fe..0000000000 --- a/docs-ui/src/content/components/collapsible.mdx +++ /dev/null @@ -1,72 +0,0 @@ -import { PropsTable } from '@/components/PropsTable'; -import { Snippet } from '@/components/Snippet'; -import { CodeBlock } from '@/components/CodeBlock'; -import { CollapsibleSnippet } from '@/snippets/stories-snippets'; -import { - collapsibleRootPropDefs, - collapsibleTriggerPropDefs, - collapsiblePanelPropDefs, - collapsibleUsageSnippet, - collapsibleDefaultSnippet, - collapsibleTriggerSnippet, - collapsibleOpenSnippet, -} from './collapsible.props'; -import { PageTitle } from '@/components/PageTitle'; -import { Theming } from '@/components/Theming'; -import { ChangelogComponent } from '@/components/ChangelogComponent'; - - - -} - code={collapsibleDefaultSnippet} -/> - -## Usage - - - -## API reference - -### Collapsible.Root - -Groups all parts of the collapsible. Renders a `
` element. - - - -### Collapsible.Trigger - -The trigger by default render a simple unstyled button. Because menus can be rendered in different ways, we recommend -using the `render` prop to render a custom trigger. - - - - - -### Collapsible.Panel - -A panel with the collapsible contents. Renders a `
` element. - - - -## Examples - -Open the panel by default by setting the `defaultOpen` prop to `true`. - -} - code={collapsibleOpenSnippet} -/> - - - - diff --git a/docs-ui/src/content/components/collapsible.props.ts b/docs-ui/src/content/components/collapsible.props.ts deleted file mode 100644 index 55e80f6689..0000000000 --- a/docs-ui/src/content/components/collapsible.props.ts +++ /dev/null @@ -1,86 +0,0 @@ -import { - classNamePropDefs, - stylePropDefs, - renderPropDefs, - type PropDef, -} from '@/utils/propDefs'; - -export const collapsibleRootPropDefs: Record = { - defaultOpen: { - type: 'boolean', - default: 'false', - }, - open: { - type: 'boolean', - }, - onOpenChange: { - type: 'enum', - values: ['(open) => void'], - }, - ...renderPropDefs, - ...classNamePropDefs, - ...stylePropDefs, -}; - -export const collapsibleTriggerPropDefs: Record = { - ...renderPropDefs, - ...classNamePropDefs, - ...stylePropDefs, -}; - -export const collapsiblePanelPropDefs: Record = { - hiddenUntilFound: { - type: 'boolean', - default: 'false', - }, - keepMounted: { - type: 'boolean', - default: 'false', - }, - ...renderPropDefs, - ...classNamePropDefs, - ...stylePropDefs, -}; - -export const collapsibleUsageSnippet = `import { Collapsible } from '@backstage/ui'; - - - ( - - )} /> - Your content -`; - -export const collapsibleDefaultSnippet = ` - ( - - )} /> - - - It's the edge of the world and all of Western civilization - The sun may rise in the East, at least it settled in a final location - It's understood that Hollywood sells Californication - - -`; - -export const collapsibleTriggerSnippet = ` - )} /> - - - It's the edge of the world and all of Western civilization - The sun may rise in the East, at least it settled in a final location - It's understood that Hollywood sells Californication - - -`; diff --git a/docs-ui/src/content/components/icon.mdx b/docs-ui/src/content/components/icon.mdx deleted file mode 100644 index 91860a7e5e..0000000000 --- a/docs-ui/src/content/components/icon.mdx +++ /dev/null @@ -1,36 +0,0 @@ -import { PropsTable } from '@/components/PropsTable'; -import { IconSnippet } from '@/snippets/stories-snippets'; -import { Snippet } from '@/components/Snippet'; -import { CodeBlock } from '@/components/CodeBlock'; -import { - iconPropDefs, - iconUsageSnippet, - iconDefaultSnippet, -} from './icon.props'; -import { PageTitle } from '@/components/PageTitle'; -import { Theming } from '@/components/Theming'; -import { ChangelogComponent } from '@/components/ChangelogComponent'; - - - -} - code={iconDefaultSnippet} -/> - -## Usage - - - -## API reference - - - - - - diff --git a/docs-ui/src/content/components/icon.props.ts b/docs-ui/src/content/components/icon.props.ts deleted file mode 100644 index 7bec0980f6..0000000000 --- a/docs-ui/src/content/components/icon.props.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { - classNamePropDefs, - stylePropDefs, - type PropDef, -} from '@/utils/propDefs'; - -export const iconPropDefs: Record = { - name: { - type: 'enum', - values: 'icon', - responsive: false, - }, - size: { - type: 'number', - responsive: false, - }, - ...classNamePropDefs, - ...stylePropDefs, -}; - -export const iconUsageSnippet = `import { Icon } from '@backstage/ui'; - -`; - -export const iconDefaultSnippet = ``; diff --git a/docs-ui/src/content/components/container.mdx b/docs-ui/src/content/container.mdx similarity index 100% rename from docs-ui/src/content/components/container.mdx rename to docs-ui/src/content/container.mdx diff --git a/docs-ui/src/content/components/container.props.ts b/docs-ui/src/content/container.props.ts similarity index 100% rename from docs-ui/src/content/components/container.props.ts rename to docs-ui/src/content/container.props.ts diff --git a/docs-ui/src/content/dialog.mdx b/docs-ui/src/content/dialog.mdx new file mode 100644 index 0000000000..4aa43c69de --- /dev/null +++ b/docs-ui/src/content/dialog.mdx @@ -0,0 +1,107 @@ +import { PropsTable } from '@/components/PropsTable'; +import { Snippet } from '@/components/Snippet'; +import { CodeBlock } from '@/components/CodeBlock'; +import { DialogSnippet } from '@/snippets/stories-snippets'; +import { + dialogPropDefs, + dialogTriggerPropDefs, + dialogHeaderPropDefs, + dialogBodyPropDefs, + dialogFooterPropDefs, + dialogClosePropDefs, + dialogUsageSnippet, + dialogDefaultSnippet, + dialogFixedWidthAndHeightSnippet, + dialogWithFormSnippet, + dialogWithNoTriggerSnippet, + dialogCloseSnippet, +} from './dialog.props'; +import { PageTitle } from '@/components/PageTitle'; +import { Theming } from '@/components/Theming'; +import { ChangelogComponent } from '@/components/ChangelogComponent'; + + + +} + code={dialogDefaultSnippet} +/> + +## Usage + + + +## API reference + +### DialogTrigger + +Wraps a trigger element and the dialog content to handle open/close state. + + + +### Dialog + +The main dialog container that renders as a modal overlay. + + + +### DialogHeader + +Displays the dialog title with a built-in close button. + + + +### DialogBody + +The main content area of the dialog with optional scrolling. + + + +### DialogFooter + +Contains action buttons or other footer content. + + + +If you want to close the dialog while pressing a button, you can use the `slot="close"` prop on the button. + + + +## Examples + +### Fixed Width and Height + +Dialog with a fixed height body that scrolls when content overflows. + +} + code={dialogFixedWidthAndHeightSnippet} +/> + +### Dialog with Form + +Dialog containing form elements for user input. + +} + code={dialogWithFormSnippet} +/> + +### Dialog with no trigger and controlled by props + +You can also control the dialog using your own states. + + + + + + diff --git a/docs-ui/src/content/dialog.props.ts b/docs-ui/src/content/dialog.props.ts new file mode 100644 index 0000000000..162ec307dc --- /dev/null +++ b/docs-ui/src/content/dialog.props.ts @@ -0,0 +1,172 @@ +import { + classNamePropDefs, + stylePropDefs, + type PropDef, +} from '@/utils/propDefs'; + +export const dialogTriggerPropDefs: Record = { + children: { type: 'enum', values: ['ReactNode'], responsive: false }, + isOpen: { + type: 'boolean', + description: 'Whether the overlay is open by default (controlled).', + }, + defaultOpen: { + type: 'boolean', + description: 'Whether the overlay is open by default (uncontrolled).', + }, + onOpenChange: { + type: 'enum', + values: ['(isOpen: boolean) => void'], + description: + "Handler that is called when the overlay's open state changes.", + }, +}; + +export const dialogPropDefs: Record = { + children: { type: 'enum', values: ['ReactNode'], responsive: false }, + isOpen: { + type: 'boolean', + description: 'Whether the overlay is open by default (controlled).', + }, + defaultOpen: { + type: 'boolean', + description: 'Whether the overlay is open by default (uncontrolled).', + }, + onOpenChange: { + type: 'enum', + values: ['(isOpen: boolean) => void'], + description: + "Handler that is called when the overlay's open state changes.", + }, + width: { + type: 'enum', + values: ['number', 'string'], + responsive: false, + }, + height: { + type: 'enum', + values: ['number', 'string'], + responsive: false, + }, + ...classNamePropDefs, + ...stylePropDefs, +}; + +export const dialogHeaderPropDefs: Record = { + children: { type: 'enum', values: ['ReactNode'], responsive: false }, + ...classNamePropDefs, + ...stylePropDefs, +}; + +export const dialogBodyPropDefs: Record = { + children: { type: 'enum', values: ['ReactNode'], responsive: false }, + height: { + type: 'enum', + values: ['number', 'string'], + responsive: false, + }, + ...classNamePropDefs, + ...stylePropDefs, +}; + +export const dialogFooterPropDefs: Record = { + children: { type: 'enum', values: ['ReactNode'], responsive: false }, + ...classNamePropDefs, + ...stylePropDefs, +}; + +export const dialogClosePropDefs: Record = { + variant: { + type: 'enum', + values: ['primary', 'secondary', 'tertiary'], + default: 'secondary', + responsive: false, + }, + children: { type: 'enum', values: ['ReactNode'], responsive: false }, + ...classNamePropDefs, + ...stylePropDefs, +}; + +export const dialogUsageSnippet = `import { + Dialog, + DialogTrigger, + DialogHeader, + DialogBody, + DialogFooter, +} from '@backstage/ui'; + + + + + Title + Content + + + + +`; + +export const dialogDefaultSnippet = ` + + + Example Dialog + + This is a basic dialog example. + + + + + + +`; + +export const dialogFixedWidthAndHeightSnippet = ` + + + Long Content Dialog + + ... + + + + + + +`; + +export const dialogWithFormSnippet = ` + + + Create New User + + + + + + + + + + + + +`; + +export const dialogWithNoTriggerSnippet = `const [isOpen, setIsOpen] = useState(false); + + + Create New User + + Your content + + + + + +`; + +export const dialogCloseSnippet = ``; diff --git a/docs-ui/src/content/components/flex.mdx b/docs-ui/src/content/flex.mdx similarity index 100% rename from docs-ui/src/content/components/flex.mdx rename to docs-ui/src/content/flex.mdx diff --git a/docs-ui/src/content/components/flex.props.ts b/docs-ui/src/content/flex.props.ts similarity index 100% rename from docs-ui/src/content/components/flex.props.ts rename to docs-ui/src/content/flex.props.ts diff --git a/docs-ui/src/content/components/grid.mdx b/docs-ui/src/content/grid.mdx similarity index 100% rename from docs-ui/src/content/components/grid.mdx rename to docs-ui/src/content/grid.mdx diff --git a/docs-ui/src/content/components/grid.props.ts b/docs-ui/src/content/grid.props.ts similarity index 64% rename from docs-ui/src/content/components/grid.props.ts rename to docs-ui/src/content/grid.props.ts index 34d89785cc..6f037cd64f 100644 --- a/docs-ui/src/content/components/grid.props.ts +++ b/docs-ui/src/content/grid.props.ts @@ -43,12 +43,12 @@ export const gridItemPropDefs: Record = { values: [...columnsValues, 'full'], responsive: true, }, - start: { + colStart: { type: 'enum | string', values: [...columnsValues, 'auto'], responsive: true, }, - end: { + colEnd: { type: 'enum | string', values: [...columnsValues, 'auto'], responsive: true, @@ -60,7 +60,7 @@ export const gridItemPropDefs: Record = { export const gridUsageSnippet = `import { Grid } from '@backstage/ui'; -`; +`; export const gridDefaultSnippet = ` @@ -68,44 +68,44 @@ export const gridDefaultSnippet = ` `; -export const gridSimpleSnippet = ` +export const gridSimpleSnippet = ` Hello World Hello World Hello World -`; +`; -export const gridComplexSnippet = ` - - Hello World +export const gridComplexSnippet = ` + + Hello World - - Hello World + + Hello World -`; +`; -export const gridMixingRowsSnippet = ` - - Hello World +export const gridMixingRowsSnippet = ` + + Hello World - - Hello World + + Hello World - - Hello World + + Hello World -`; +`; -export const gridResponsiveSnippet = ` +export const gridResponsiveSnippet = ` - Hello World + - Hello World + Hello World -`; +`; -export const gridStartEndSnippet = ` - - Hello World +export const gridStartEndSnippet = ` + + Hello World -`; +`; diff --git a/docs-ui/src/content/components/header-page.mdx b/docs-ui/src/content/header-page.mdx similarity index 100% rename from docs-ui/src/content/components/header-page.mdx rename to docs-ui/src/content/header-page.mdx diff --git a/docs-ui/src/content/components/header-page.props.ts b/docs-ui/src/content/header-page.props.ts similarity index 100% rename from docs-ui/src/content/components/header-page.props.ts rename to docs-ui/src/content/header-page.props.ts diff --git a/docs-ui/src/content/components/header.mdx b/docs-ui/src/content/header.mdx similarity index 100% rename from docs-ui/src/content/components/header.mdx rename to docs-ui/src/content/header.mdx diff --git a/docs-ui/src/content/components/header.props.ts b/docs-ui/src/content/header.props.ts similarity index 100% rename from docs-ui/src/content/components/header.props.ts rename to docs-ui/src/content/header.props.ts diff --git a/docs-ui/src/content/components/link.mdx b/docs-ui/src/content/link.mdx similarity index 100% rename from docs-ui/src/content/components/link.mdx rename to docs-ui/src/content/link.mdx diff --git a/docs-ui/src/content/components/link.props.ts b/docs-ui/src/content/link.props.ts similarity index 100% rename from docs-ui/src/content/components/link.props.ts rename to docs-ui/src/content/link.props.ts diff --git a/docs-ui/src/content/components/menu.mdx b/docs-ui/src/content/menu.mdx similarity index 100% rename from docs-ui/src/content/components/menu.mdx rename to docs-ui/src/content/menu.mdx diff --git a/docs-ui/src/content/components/menu.props.ts b/docs-ui/src/content/menu.props.ts similarity index 93% rename from docs-ui/src/content/components/menu.props.ts rename to docs-ui/src/content/menu.props.ts index 46b0c11c39..2c7d76dd5d 100644 --- a/docs-ui/src/content/components/menu.props.ts +++ b/docs-ui/src/content/menu.props.ts @@ -70,6 +70,16 @@ export const menuPropDefs: Record = { type: 'enum', values: placementValues, }, + virtualized: { + type: 'boolean', + default: 'false', + }, + maxWidth: { + type: 'number', + }, + maxHeight: { + type: 'number', + }, ...classNamePropDefs, ...stylePropDefs, }; @@ -95,6 +105,16 @@ export const menuListBoxPropDefs: Record = { type: 'enum', values: placementValues, }, + virtualized: { + type: 'boolean', + default: 'false', + }, + maxWidth: { + type: 'number', + }, + maxHeight: { + type: 'number', + }, ...classNamePropDefs, ...stylePropDefs, }; @@ -104,6 +124,16 @@ export const menuAutocompletePropDefs: Record = { type: 'enum', values: placementValues, }, + virtualized: { + type: 'boolean', + default: 'false', + }, + maxWidth: { + type: 'number', + }, + maxHeight: { + type: 'number', + }, ...classNamePropDefs, ...stylePropDefs, }; @@ -113,6 +143,16 @@ export const menuAutocompleteListboxPropDefs: Record = { type: 'enum', values: placementValues, }, + virtualized: { + type: 'boolean', + default: 'false', + }, + maxWidth: { + type: 'number', + }, + maxHeight: { + type: 'number', + }, ...classNamePropDefs, ...stylePropDefs, }; diff --git a/docs-ui/src/content/password-field.mdx b/docs-ui/src/content/password-field.mdx new file mode 100644 index 0000000000..14fbc9e34d --- /dev/null +++ b/docs-ui/src/content/password-field.mdx @@ -0,0 +1,64 @@ +import { PropsTable } from '@/components/PropsTable'; +import { Snippet } from '@/components/Snippet'; +import { PasswordFieldSnippet } from '@/snippets/stories-snippets'; +import { + inputPropDefs, + passwordFieldUsageSnippet, + passwordFieldDefaultSnippet, + passwordFieldSizesSnippet, + passwordFieldDescriptionSnippet, +} from './password-field.props'; +import { PageTitle } from '@/components/PageTitle'; +import { Theming } from '@/components/Theming'; +import { ChangelogComponent } from '@/components/ChangelogComponent'; +import { CodeBlock } from '@/components/CodeBlock'; + + + +} + code={passwordFieldDefaultSnippet} +/> + +## Usage + + + +## API reference + + + +## Examples + +### Sizes + +We support two different sizes: `small`, `medium`. + +} + code={passwordFieldSizesSnippet} +/> + +### With description + +Here's a simple PasswordField with a description. + +} + code={passwordFieldDescriptionSnippet} +/> + + + + diff --git a/docs-ui/src/content/password-field.props.ts b/docs-ui/src/content/password-field.props.ts new file mode 100644 index 0000000000..493405b5fd --- /dev/null +++ b/docs-ui/src/content/password-field.props.ts @@ -0,0 +1,43 @@ +import { + classNamePropDefs, + stylePropDefs, + type PropDef, +} from '@/utils/propDefs'; + +export const inputPropDefs: Record = { + size: { + type: 'enum', + values: ['small', 'medium'], + default: 'small', + responsive: true, + }, + label: { + type: 'string', + }, + icon: { + type: 'enum', + values: ['ReactNode'], + }, + description: { + type: 'string', + }, + name: { + type: 'string', + required: true, + }, + ...classNamePropDefs, + ...stylePropDefs, +}; + +export const passwordFieldUsageSnippet = `import { PasswordField } from '@backstage/ui'; + +`; + +export const passwordFieldDefaultSnippet = ``; + +export const passwordFieldSizesSnippet = ` + } /> + } /> +`; + +export const passwordFieldDescriptionSnippet = ``; diff --git a/docs-ui/src/content/components/radio-group.mdx b/docs-ui/src/content/radio-group.mdx similarity index 100% rename from docs-ui/src/content/components/radio-group.mdx rename to docs-ui/src/content/radio-group.mdx diff --git a/docs-ui/src/content/components/radio-group.props.ts b/docs-ui/src/content/radio-group.props.ts similarity index 100% rename from docs-ui/src/content/components/radio-group.props.ts rename to docs-ui/src/content/radio-group.props.ts diff --git a/docs-ui/src/content/components/search-field.mdx b/docs-ui/src/content/search-field.mdx similarity index 100% rename from docs-ui/src/content/components/search-field.mdx rename to docs-ui/src/content/search-field.mdx diff --git a/docs-ui/src/content/components/search-field.props.ts b/docs-ui/src/content/search-field.props.ts similarity index 100% rename from docs-ui/src/content/components/search-field.props.ts rename to docs-ui/src/content/search-field.props.ts diff --git a/docs-ui/src/content/components/select.mdx b/docs-ui/src/content/select.mdx similarity index 71% rename from docs-ui/src/content/components/select.mdx rename to docs-ui/src/content/select.mdx index 09a852e871..eb53e9ea35 100644 --- a/docs-ui/src/content/components/select.mdx +++ b/docs-ui/src/content/select.mdx @@ -11,6 +11,9 @@ import { selectDisabledSnippet, selectResponsiveSnippet, selectIconSnippet, + selectSearchableSnippet, + selectMultipleSnippet, + selectSearchableMultipleSnippet, } from './select.props'; import { PageTitle } from '@/components/PageTitle'; import { Theming } from '@/components/Theming'; @@ -87,6 +90,42 @@ Here's a view when the select is disabled. code={selectDisabledSnippet} /> +### Searchable + +Here's a view when the select has searchable filtering. + +} + code={selectSearchableSnippet} +/> + +### Multiple Selection + +Here's a view when the select allows multiple selections. + +} + code={selectMultipleSnippet} +/> + +### Searchable with Multiple Selection + +Here's a view when the select combines search and multiple selection. + +} + code={selectSearchableMultipleSnippet} +/> + ### Responsive Here's a view when the select is responsive. diff --git a/docs-ui/src/content/components/select.props.ts b/docs-ui/src/content/select.props.ts similarity index 59% rename from docs-ui/src/content/components/select.props.ts rename to docs-ui/src/content/select.props.ts index a93f448bd8..05f0d895c9 100644 --- a/docs-ui/src/content/components/select.props.ts +++ b/docs-ui/src/content/select.props.ts @@ -23,6 +23,12 @@ export const selectPropDefs: Record = { values: ['Array<{ value: string, label: string }>'], required: true, }, + selectionMode: { + type: 'enum', + values: ['single', 'multiple'], + default: 'single', + responsive: false, + }, placeholder: { type: 'string', default: 'Select an item', @@ -34,17 +40,23 @@ export const selectPropDefs: Record = { responsive: false, }, value: { - type: 'string', + type: 'enum', + values: ['string', 'string[]'], responsive: false, + description: + 'Selected value (controlled). String for single selection, array for multiple.', }, defaultValue: { - type: 'string', + type: 'enum', + values: ['string', 'string[]'], responsive: false, + description: + 'Initial value (uncontrolled). String for single selection, array for multiple.', }, size: { type: 'enum', values: ['small', 'medium'], - default: 'medium', + default: 'small', responsive: true, }, isOpen: { @@ -57,7 +69,7 @@ export const selectPropDefs: Record = { }, disabledKeys: { type: 'enum', - values: ['string[]'], + values: ['Iterable'], responsive: false, }, isDisabled: { @@ -72,14 +84,6 @@ export const selectPropDefs: Record = { type: 'boolean', responsive: false, }, - selectedKey: { - type: 'string', - responsive: false, - }, - defaultSelectedKey: { - type: 'string', - responsive: false, - }, onOpenChange: { type: 'enum', values: ['(isOpen: boolean) => void'], @@ -87,7 +91,19 @@ export const selectPropDefs: Record = { }, onSelectionChange: { type: 'enum', - values: ['(key: Key | null) => void'], + values: ['(key: Key | null) => void', '(keys: Selection) => void'], + responsive: false, + description: + 'Handler called when selection changes. Single mode: receives Key | null. Multiple mode: receives Selection.', + }, + searchable: { + type: 'boolean', + default: 'false', + responsive: false, + }, + searchPlaceholder: { + type: 'string', + default: 'Search...', responsive: false, }, ...classNamePropDefs, @@ -151,3 +167,45 @@ export const selectResponsiveSnippet = ``; + +export const selectMultipleSnippet = ``; diff --git a/docs-ui/src/content/components/skeleton.mdx b/docs-ui/src/content/skeleton.mdx similarity index 100% rename from docs-ui/src/content/components/skeleton.mdx rename to docs-ui/src/content/skeleton.mdx diff --git a/docs-ui/src/content/components/skeleton.props.ts b/docs-ui/src/content/skeleton.props.ts similarity index 100% rename from docs-ui/src/content/components/skeleton.props.ts rename to docs-ui/src/content/skeleton.props.ts diff --git a/docs-ui/src/content/components/switch.mdx b/docs-ui/src/content/switch.mdx similarity index 100% rename from docs-ui/src/content/components/switch.mdx rename to docs-ui/src/content/switch.mdx diff --git a/docs-ui/src/content/components/switch.props.ts b/docs-ui/src/content/switch.props.ts similarity index 100% rename from docs-ui/src/content/components/switch.props.ts rename to docs-ui/src/content/switch.props.ts diff --git a/docs-ui/src/content/components/table.mdx b/docs-ui/src/content/table.mdx similarity index 100% rename from docs-ui/src/content/components/table.mdx rename to docs-ui/src/content/table.mdx diff --git a/docs-ui/src/content/components/table.props.ts b/docs-ui/src/content/table.props.ts similarity index 98% rename from docs-ui/src/content/components/table.props.ts rename to docs-ui/src/content/table.props.ts index 1b40055849..443526182e 100644 --- a/docs-ui/src/content/components/table.props.ts +++ b/docs-ui/src/content/table.props.ts @@ -2,7 +2,7 @@ import { classNamePropDefs, stylePropDefs, type PropDef, -} from '../../utils/propDefs'; +} from '@/utils/propDefs'; export const tablePropDefs: Record = { selectionBehavior: { @@ -212,6 +212,11 @@ export const cellPropDefs: Record = { description: "A string representation of the cell's contents, used for features like typeahead.", }, + leadingIcon: { + type: 'enum', + values: ['ReactNode'], + description: 'Optional icon to display before the cell content.', + }, ...classNamePropDefs, ...stylePropDefs, }; diff --git a/docs-ui/src/content/components/tabs.mdx b/docs-ui/src/content/tabs.mdx similarity index 100% rename from docs-ui/src/content/components/tabs.mdx rename to docs-ui/src/content/tabs.mdx diff --git a/docs-ui/src/content/components/tabs.props.ts b/docs-ui/src/content/tabs.props.ts similarity index 100% rename from docs-ui/src/content/components/tabs.props.ts rename to docs-ui/src/content/tabs.props.ts diff --git a/docs-ui/src/content/components/tag-group.mdx b/docs-ui/src/content/tag-group.mdx similarity index 100% rename from docs-ui/src/content/components/tag-group.mdx rename to docs-ui/src/content/tag-group.mdx diff --git a/docs-ui/src/content/components/tag-group.props.ts b/docs-ui/src/content/tag-group.props.ts similarity index 100% rename from docs-ui/src/content/components/tag-group.props.ts rename to docs-ui/src/content/tag-group.props.ts diff --git a/docs-ui/src/content/components/text-field.mdx b/docs-ui/src/content/text-field.mdx similarity index 100% rename from docs-ui/src/content/components/text-field.mdx rename to docs-ui/src/content/text-field.mdx diff --git a/docs-ui/src/content/components/text-field.props.ts b/docs-ui/src/content/text-field.props.ts similarity index 100% rename from docs-ui/src/content/components/text-field.props.ts rename to docs-ui/src/content/text-field.props.ts diff --git a/docs-ui/src/content/components/text.mdx b/docs-ui/src/content/text.mdx similarity index 100% rename from docs-ui/src/content/components/text.mdx rename to docs-ui/src/content/text.mdx diff --git a/docs-ui/src/content/components/text.props.ts b/docs-ui/src/content/text.props.ts similarity index 100% rename from docs-ui/src/content/components/text.props.ts rename to docs-ui/src/content/text.props.ts diff --git a/docs-ui/src/content/components/tooltip.mdx b/docs-ui/src/content/tooltip.mdx similarity index 100% rename from docs-ui/src/content/components/tooltip.mdx rename to docs-ui/src/content/tooltip.mdx diff --git a/docs-ui/src/content/components/tooltip.props.ts b/docs-ui/src/content/tooltip.props.ts similarity index 100% rename from docs-ui/src/content/components/tooltip.props.ts rename to docs-ui/src/content/tooltip.props.ts diff --git a/docs-ui/src/content/visually-hidden.mdx b/docs-ui/src/content/visually-hidden.mdx new file mode 100644 index 0000000000..26a609b245 --- /dev/null +++ b/docs-ui/src/content/visually-hidden.mdx @@ -0,0 +1,51 @@ +import { PropsTable } from '@/components/PropsTable'; +import { Snippet } from '@/components/Snippet'; +import { CodeBlock } from '@/components/CodeBlock'; +import { VisuallyHiddenSnippet } from '@/snippets/stories-snippets'; +import { + visuallyHiddenPropDefs, + visuallyHiddenUsageSnippet, + visuallyHiddenDefaultSnippet, + visuallyHiddenExampleUsageSnippet, +} from './visually-hidden.props'; +import { PageTitle } from '@/components/PageTitle'; +import { Theming } from '@/components/Theming'; +import { ChangelogComponent } from '@/components/ChangelogComponent'; + + + +} + code={visuallyHiddenDefaultSnippet} +/> + +## Usage + + + +## API reference + + + +## Examples + +### Example Usage + +Here's an example of providing screen reader context for a list of links in a footer. + +} + code={visuallyHiddenExampleUsageSnippet} + open +/> + + + + diff --git a/docs-ui/src/content/visually-hidden.props.ts b/docs-ui/src/content/visually-hidden.props.ts new file mode 100644 index 0000000000..18a1c503d1 --- /dev/null +++ b/docs-ui/src/content/visually-hidden.props.ts @@ -0,0 +1,47 @@ +import { + classNamePropDefs, + stylePropDefs, + type PropDef, +} from '@/utils/propDefs'; + +export const visuallyHiddenPropDefs: Record = { + children: { + type: 'enum', + values: ['ReactNode'], + responsive: false, + }, + ...classNamePropDefs, + ...stylePropDefs, +}; + +export const visuallyHiddenUsageSnippet = `import { VisuallyHidden } from '@backstage/ui'; + + + This content is visually hidden but accessible to screen readers +`; + +export const visuallyHiddenDefaultSnippet = ` + + This text is followed by a paragraph that is visually hidden but + accessible to screen readers. Try using a screen reader to hear it, or + inspect the DOM to see it's there. + + + This content is visually hidden but accessible to screen readers + +`; + +export const visuallyHiddenExampleUsageSnippet = ` + + Footer links + + + About us + + + Jobs + + + Terms and Conditions + +`; diff --git a/docs-ui/src/css/globals.css b/docs-ui/src/css/globals.css index f94dd74610..dcc348a9ee 100644 --- a/docs-ui/src/css/globals.css +++ b/docs-ui/src/css/globals.css @@ -1,10 +1,9 @@ :root { - --bg: #f4f4f4; - --panel: #fff; - --panel-hover: #fafafa; + --bg: #ffffff; + --bg-hover: #fafafa; --primary: #000; --secondary: #929292; - --action: #fff; + --action: #f3f3f3; --link: #4f5ce0; --font-regular: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', @@ -16,12 +15,15 @@ --border: #e5e5e5; --border2: #cdcdcd; --surface-1: #f4f4f4; + --code-bg: #3e444f; + --code-title: #505865; } [data-theme-mode='dark'] { - --bg: #000; - --panel: #181818; - --panel-hover: #202020; + --bg: #000000; + --bg-hover: #202020; + --code-bg: #202020; + --code-title: #292929; --primary: #fff; --secondary: #818181; --action: #202020; @@ -48,10 +50,11 @@ iframe { .shiki, .shiki span { - background-color: transparent !important; + background-color: transparent; font-family: var(--font-mono); font-size: 0.875rem; line-height: 1.7; + padding-block: 0.2rem; } [data-theme-mode='dark'] .shiki, @@ -63,6 +66,36 @@ iframe { text-decoration: var(--shiki-dark-text-decoration) !important; } +/* Add styles for diff notation */ +pre.shiki.has-diff .line.diff { + position: relative; + padding-left: 1.5rem; + border-radius: 0.25rem; +} + +pre.shiki.has-diff .line.diff::before { + position: absolute; + left: 0.5rem; +} + +pre.shiki.has-diff .line.diff.add { + background-color: rgba(46, 160, 67, 0.15); +} + +pre.shiki.has-diff .line.diff.add::before { + content: '+'; + color: #2ea043; +} + +pre.shiki.has-diff .line.diff.remove { + background-color: rgba(248, 81, 73, 0.15); +} + +pre.shiki.has-diff .line.diff.remove::before { + content: '-'; + color: #f85149; +} + .cm-editor { transition: background-color 0.2s ease-in-out; } diff --git a/docs-ui/src/css/mdx.module.css b/docs-ui/src/css/mdx.module.css index 6a65995003..7cc2422ecd 100644 --- a/docs-ui/src/css/mdx.module.css +++ b/docs-ui/src/css/mdx.module.css @@ -49,3 +49,27 @@ .a:hover { text-decoration: underline; } + +.headingWithAnchor { + position: relative; + scroll-margin-top: 80px; +} + +.anchorLink { + color: inherit; + text-decoration: none; + display: inline-flex; + align-items: center; + gap: 0.5rem; +} + +.anchorHash { + opacity: 0; + transition: opacity 0.2s; + font-size: 0.8em; + color: var(--text-secondary); +} + +.anchorLink:hover .anchorHash { + opacity: 1; +} diff --git a/docs-ui/src/mdx-components.tsx b/docs-ui/src/mdx-components.tsx index 75dbc81df8..bae798bf7c 100644 --- a/docs-ui/src/mdx-components.tsx +++ b/docs-ui/src/mdx-components.tsx @@ -4,10 +4,61 @@ import Image, { ImageProps } from 'next/image'; import { CodeBlock } from '@/components/CodeBlock'; import styles from './css/mdx.module.css'; +// Utility function to generate slug from heading text +function slugify(text: string): string { + return text + .toString() + .toLocaleLowerCase('en-US') + .trim() + .replace(/\s+/g, '-') + .replace(/[^\w\-]+/g, '') + .replace(/\-\-+/g, '-') + .replace(/^-+/, '') + .replace(/-+$/, ''); +} + +// Component for heading with anchor link +function HeadingWithAnchor({ + level, + children, + className, +}: { + level: number; + children: ReactNode; + className: string; +}) { + const text = + typeof children === 'string' + ? children + : Array.isArray(children) + ? children.join('') + : ''; + const id = slugify(text); + + const Tag = `h${level}` as keyof JSX.IntrinsicElements; + + return ( + + + {children} + # + + + ); +} + export const formattedMDXComponents: MDXComponents = { h1: ({ children }) =>

{children as ReactNode}

, - h2: ({ children }) =>

{children as ReactNode}

, - h3: ({ children }) =>

{children as ReactNode}

, + h2: ({ children }) => ( + + {children as ReactNode} + + ), + h3: ({ children }) => ( + + {children as ReactNode} + + ), p: ({ children }) =>

{children as ReactNode}

, a: ({ children, href }) => ( @@ -27,7 +78,7 @@ export const formattedMDXComponents: MDXComponents = { p.slug === slug); - if (overviewPage) { - return overviewPage.title; - } - - // Search in core concepts array - const coreConcept = coreConcepts.find(c => c.slug === slug); - if (coreConcept) { - return coreConcept.title; - } - // Search in components array const component = components.find(c => c.slug === slug); if (component) { diff --git a/docs-ui/src/utils/playground-context.tsx b/docs-ui/src/utils/playground-context.tsx index 56ed9dc48a..a1cee6ecba 100644 --- a/docs-ui/src/utils/playground-context.tsx +++ b/docs-ui/src/utils/playground-context.tsx @@ -8,7 +8,7 @@ import { import { components } from './data'; type Theme = 'light' | 'dark'; -type ThemeName = 'backstage' | 'spotify'; +type ThemeName = 'backstage' | 'spotify' | 'custom'; // Create a context with an empty array as the default value const PlaygroundContext = createContext<{ @@ -16,8 +16,8 @@ const PlaygroundContext = createContext<{ setSelectedScreenSizes: (screenSizes: string[]) => void; selectedComponents: string[]; setSelectedComponents: (components: string[]) => void; - selectedTheme: Theme; - setSelectedTheme: (theme: Theme) => void; + selectedTheme: Set; + setSelectedTheme: (keys: Set) => void; selectedThemeName: ThemeName; setSelectedThemeName: (themeName: ThemeName) => void; }>({ @@ -25,7 +25,7 @@ const PlaygroundContext = createContext<{ setSelectedScreenSizes: () => {}, selectedComponents: [], setSelectedComponents: () => {}, - selectedTheme: 'light', + selectedTheme: new Set(['light']), setSelectedTheme: () => {}, selectedThemeName: 'backstage', setSelectedThemeName: () => {}, @@ -40,16 +40,24 @@ export const PlaygroundProvider = ({ children }: { children: ReactNode }) => { const [selectedComponents, setSelectedComponents] = useState( components.map(component => component.slug), ); - const [selectedTheme, setSelectedTheme] = useState('light'); + const [selectedTheme, setSelectedTheme] = useState>( + new Set(['light']), + ); const [selectedThemeName, setSelectedThemeName] = useState('backstage'); // Load saved theme from localStorage after hydration useEffect(() => { if (isBrowser) { - const savedTheme = localStorage.getItem('theme-mode') as Theme; - if (savedTheme) { - setSelectedTheme(savedTheme); + const savedThemeString = localStorage.getItem('theme-mode'); + if (savedThemeString) { + // Parse the comma-separated string back into a Set + const themeArray = savedThemeString + .split(',') + .filter(Boolean) as Theme[]; + setSelectedTheme(new Set(themeArray)); + } else { + setSelectedTheme(new Set(['light'])); } } }, [isBrowser]); @@ -68,9 +76,9 @@ export const PlaygroundProvider = ({ children }: { children: ReactNode }) => { if (isBrowser) { document.documentElement.setAttribute( 'data-theme-mode', - selectedTheme || 'light', + Array.from(selectedTheme).join(','), ); - localStorage.setItem('theme-mode', selectedTheme || 'light'); + localStorage.setItem('theme-mode', Array.from(selectedTheme).join(',')); } }, [selectedTheme, isBrowser]); diff --git a/docs-ui/yarn.lock b/docs-ui/yarn.lock index 0969bf0ffa..480f7592f8 100644 --- a/docs-ui/yarn.lock +++ b/docs-ui/yarn.lock @@ -23,10 +23,51 @@ __metadata: languageName: node linkType: hard -"@babel/runtime@npm:^7.18.6, @babel/runtime@npm:^7.21.0": - version: 7.28.3 - resolution: "@babel/runtime@npm:7.28.3" - checksum: 10/f2415e4dbface7496f6fc561d640b44be203071fb0dfb63fbe338c7d2d2047419cb054ef13d1ebb8fc11e35d2b55aa3045def4b985e8b82aea5d7e58e1133e52 +"@babel/runtime@npm:^7.18.6, @babel/runtime@npm:^7.28.4": + version: 7.28.4 + resolution: "@babel/runtime@npm:7.28.4" + checksum: 10/6c9a70452322ea80b3c9b2a412bcf60771819213a67576c8cec41e88a95bb7bf01fc983754cda35dc19603eef52df22203ccbf7777b9d6316932f9fb77c25163 + languageName: node + linkType: hard + +"@base-ui-components/react@npm:^1.0.0-beta.4": + version: 1.0.0-beta.4 + resolution: "@base-ui-components/react@npm:1.0.0-beta.4" + dependencies: + "@babel/runtime": "npm:^7.28.4" + "@base-ui-components/utils": "npm:0.1.2" + "@floating-ui/react-dom": "npm:^2.1.6" + "@floating-ui/utils": "npm:^0.2.10" + reselect: "npm:^5.1.1" + tabbable: "npm:^6.2.0" + use-sync-external-store: "npm:^1.5.0" + peerDependencies: + "@types/react": ^17 || ^18 || ^19 + react: ^17 || ^18 || ^19 + react-dom: ^17 || ^18 || ^19 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 10/d2c6a779aaefa3454cc130244e839f0428460997f3ca8bbcd9636056bc5a4c77436cc0ceac064f34bdc7cb01489a4cbc4d8b0bf9668a023fa754e5a24bd3af4e + languageName: node + linkType: hard + +"@base-ui-components/utils@npm:0.1.2": + version: 0.1.2 + resolution: "@base-ui-components/utils@npm:0.1.2" + dependencies: + "@babel/runtime": "npm:^7.28.4" + "@floating-ui/utils": "npm:^0.2.10" + reselect: "npm:^5.1.1" + use-sync-external-store: "npm:^1.5.0" + peerDependencies: + "@types/react": ^17 || ^18 || ^19 + react: ^17 || ^18 || ^19 + react-dom: ^17 || ^18 || ^19 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 10/b8408f5ad55886f793fb7580ffe2bb9565ff0713bec324f726b88ee1d135d75b26a1312338b19af743adf52e5b9d46892a47fb225e6abfe1077e75d32e88fb6e languageName: node linkType: hard @@ -138,14 +179,14 @@ __metadata: linkType: hard "@codemirror/view@npm:^6.0.0, @codemirror/view@npm:^6.17.0, @codemirror/view@npm:^6.23.0, @codemirror/view@npm:^6.27.0, @codemirror/view@npm:^6.34.4, @codemirror/view@npm:^6.35.0": - version: 6.38.1 - resolution: "@codemirror/view@npm:6.38.1" + version: 6.38.6 + resolution: "@codemirror/view@npm:6.38.6" dependencies: "@codemirror/state": "npm:^6.5.0" crelt: "npm:^1.0.6" style-mod: "npm:^4.1.0" w3c-keyname: "npm:^2.2.4" - checksum: 10/e0c5a365608749dd096ba7a930c8393f316bf4c2cacd1465a47a057d0a9f9868ff372a0bb6eb696c926f88411139f79a97a05f8c884bcc380145445cc61e68c8 + checksum: 10/5a047337a98de111817ce8c8d39e6429c90ca0b0a4d2678d6e161e9e5961b1d476a891f447ab7a05cac395d4a93530e7c68bedd93191285265f0742a308ad00b languageName: node linkType: hard @@ -401,6 +442,44 @@ __metadata: languageName: node linkType: hard +"@floating-ui/core@npm:^1.7.3": + version: 1.7.3 + resolution: "@floating-ui/core@npm:1.7.3" + dependencies: + "@floating-ui/utils": "npm:^0.2.10" + checksum: 10/a8952ff2673ddf28f12feeb86d90c54949e45bcb1af5758b7672850ac0dadb36d4bd61aa45dad1b6a35ba40d4756d3573afac6610b90502639d7266b91e0864e + languageName: node + linkType: hard + +"@floating-ui/dom@npm:^1.7.4": + version: 1.7.4 + resolution: "@floating-ui/dom@npm:1.7.4" + dependencies: + "@floating-ui/core": "npm:^1.7.3" + "@floating-ui/utils": "npm:^0.2.10" + checksum: 10/d3d6a23e7b9804ba56338c7c666590258683af14b6026270d32afc1202f72b5b82cca359004bdc7830bf2463a045da6c7bd4e7d5351218cf270ff94206197971 + languageName: node + linkType: hard + +"@floating-ui/react-dom@npm:^2.1.6": + version: 2.1.6 + resolution: "@floating-ui/react-dom@npm:2.1.6" + dependencies: + "@floating-ui/dom": "npm:^1.7.4" + peerDependencies: + react: ">=16.8.0" + react-dom: ">=16.8.0" + checksum: 10/fbfd3319b42edb9c156e4e872f500d2edb112bc9cfd1b45892bff16ccf21c2484ddc9c416f7631c2aaaadec1b2f98b205db8a3f89eb78ca870905fcfe3917c35 + languageName: node + linkType: hard + +"@floating-ui/utils@npm:^0.2.10": + version: 0.2.10 + resolution: "@floating-ui/utils@npm:0.2.10" + checksum: 10/b635ea865a8be2484b608b7157f5abf9ed439f351011a74b7e988439e2898199a9a8b790f52291e05bdcf119088160dc782d98cff45cc98c5a271bc6f51327ae + languageName: node + linkType: hard + "@humanwhocodes/config-array@npm:^0.13.0": version: 0.13.0 resolution: "@humanwhocodes/config-array@npm:0.13.0" @@ -650,10 +729,10 @@ __metadata: languageName: node linkType: hard -"@lezer/common@npm:^1.0.0, @lezer/common@npm:^1.0.2, @lezer/common@npm:^1.1.0, @lezer/common@npm:^1.2.0": - version: 1.2.3 - resolution: "@lezer/common@npm:1.2.3" - checksum: 10/dad24e353e4e67d88b203191361ca1dff26c01c2b7b4ae829b668a1d115929334d077217367683e39180c0556510ed2066ea8ddba2b079be7c08a7152208cc87 +"@lezer/common@npm:^1.0.0, @lezer/common@npm:^1.0.2, @lezer/common@npm:^1.1.0, @lezer/common@npm:^1.2.0, @lezer/common@npm:^1.3.0": + version: 1.3.0 + resolution: "@lezer/common@npm:1.3.0" + checksum: 10/8e195a8e426bc18d4339b3f2a1a7ad39c3b2cfa740c7108657a241985f63bdee5255a5f5cf8d863b878881744288bcb679d16170f0e5bcebb141188b53cfd8c0 languageName: node linkType: hard @@ -669,11 +748,11 @@ __metadata: linkType: hard "@lezer/highlight@npm:^1.0.0, @lezer/highlight@npm:^1.2.1": - version: 1.2.1 - resolution: "@lezer/highlight@npm:1.2.1" + version: 1.2.3 + resolution: "@lezer/highlight@npm:1.2.3" dependencies: - "@lezer/common": "npm:^1.0.0" - checksum: 10/fec3082419ee87fb265039b680fbac6796f862d8e3042dcb860e8c5a34291503a74927302b568ff1a626f0d2b5cf8dae02a51cfd200084eb329e5fd1236c3163 + "@lezer/common": "npm:^1.3.0" + checksum: 10/8f787d464f8a036f117a0b23e73ac034d224a57d72501c6559089098a28f127c9e495b90ac7d132acc86199e0b64d4c038f75f9293a37c7c61add52fa1acdb4e languageName: node linkType: hard @@ -705,8 +784,8 @@ __metadata: linkType: hard "@mdx-js/loader@npm:^3.1.0": - version: 3.1.0 - resolution: "@mdx-js/loader@npm:3.1.0" + version: 3.1.1 + resolution: "@mdx-js/loader@npm:3.1.1" dependencies: "@mdx-js/mdx": "npm:^3.0.0" source-map: "npm:^0.7.0" @@ -715,18 +794,19 @@ __metadata: peerDependenciesMeta: webpack: optional: true - checksum: 10/983b8d4949f4d214a17f4ad8597a50de36ace75e93292d2493b5fb6077d69d6abc8230b62f88b818c04f12a01d281d63edb6a0738937f7e259406a73ce998134 + checksum: 10/b78afe06e49b5c0bec9e5b232fbc874ffa721ff6b022bdaa7a29e2b5a44fa9f8282aa2b34f74701ca0b8750b33a775cd3182aaba5202e209f3d66961600c930f languageName: node linkType: hard -"@mdx-js/mdx@npm:^3.0.0, @mdx-js/mdx@npm:^3.1.0": - version: 3.1.0 - resolution: "@mdx-js/mdx@npm:3.1.0" +"@mdx-js/mdx@npm:^3.0.0, @mdx-js/mdx@npm:^3.1.1": + version: 3.1.1 + resolution: "@mdx-js/mdx@npm:3.1.1" dependencies: "@types/estree": "npm:^1.0.0" "@types/estree-jsx": "npm:^1.0.0" "@types/hast": "npm:^3.0.0" "@types/mdx": "npm:^2.0.0" + acorn: "npm:^8.0.0" collapse-white-space: "npm:^2.0.0" devlop: "npm:^1.0.0" estree-util-is-identifier-name: "npm:^3.0.0" @@ -747,19 +827,19 @@ __metadata: unist-util-stringify-position: "npm:^4.0.0" unist-util-visit: "npm:^5.0.0" vfile: "npm:^6.0.0" - checksum: 10/4bd4e1160e2b2bc9ea2b5b93246ce0e34a11ac5fd420ec025e82fb1120a72b80025d9cb205cce6394bb5f0013f209b9ea453cbda4c0ca4f97a2169df60084742 + checksum: 10/b871da2497f6e0f11da1574fe772a50b09b7c177de8df0f821f708bf162febe76c01a572a5c68e860960189209fd66f768c2e747fdb3a1f497c5c32e11890c11 languageName: node linkType: hard -"@mdx-js/react@npm:^3.1.0": - version: 3.1.0 - resolution: "@mdx-js/react@npm:3.1.0" +"@mdx-js/react@npm:^3.1.0, @mdx-js/react@npm:^3.1.1": + version: 3.1.1 + resolution: "@mdx-js/react@npm:3.1.1" dependencies: "@types/mdx": "npm:^2.0.0" peerDependencies: "@types/react": ">=16" react: ">=16" - checksum: 10/cf89d6392c76091622fb647f205e1ab5cbdf5edd4401dde7092138cefc9fbb6d61428aa63557de0bccca3695d5a8854dd4a93b34a27cb8e27369da7eaeaa3e73 + checksum: 10/52a740e2f37761694fa94d4704b7825084b4055616a95c8b8f4c1676190d399ddc5cdbb399ffc45b550beecd30497a7224c2e5b05bf43ecb668c7473641037d1 languageName: node linkType: hard @@ -949,70 +1029,78 @@ __metadata: languageName: node linkType: hard -"@shikijs/core@npm:1.29.2": - version: 1.29.2 - resolution: "@shikijs/core@npm:1.29.2" +"@shikijs/core@npm:3.13.0": + version: 3.13.0 + resolution: "@shikijs/core@npm:3.13.0" dependencies: - "@shikijs/engine-javascript": "npm:1.29.2" - "@shikijs/engine-oniguruma": "npm:1.29.2" - "@shikijs/types": "npm:1.29.2" - "@shikijs/vscode-textmate": "npm:^10.0.1" + "@shikijs/types": "npm:3.13.0" + "@shikijs/vscode-textmate": "npm:^10.0.2" "@types/hast": "npm:^3.0.4" - hast-util-to-html: "npm:^9.0.4" - checksum: 10/83dc5e86efc587d513268175ff43e8273567d2c6616d725bff15b08cf243d90f9371d6fa76ba49bb0e9823fc9947c8d8c650c81cfb19c3eb726178326f639a55 + hast-util-to-html: "npm:^9.0.5" + checksum: 10/c9b301a170cd76fa2ce446adee26b539b19204107a2e60207f3f5e7d96773df322367f6035e2d689d43f97a3e9f741793e976c366015d8e3a9709dcac1399af2 languageName: node linkType: hard -"@shikijs/engine-javascript@npm:1.29.2": - version: 1.29.2 - resolution: "@shikijs/engine-javascript@npm:1.29.2" +"@shikijs/engine-javascript@npm:3.13.0": + version: 3.13.0 + resolution: "@shikijs/engine-javascript@npm:3.13.0" dependencies: - "@shikijs/types": "npm:1.29.2" - "@shikijs/vscode-textmate": "npm:^10.0.1" - oniguruma-to-es: "npm:^2.2.0" - checksum: 10/b49461ff7152650ffdbd77332d1c70e24a2ff1abe869e1038694b410194c6403fe5e8fce104fdd305d10c18702a50c1edbdb87172aa09f11340bc1203ed38488 + "@shikijs/types": "npm:3.13.0" + "@shikijs/vscode-textmate": "npm:^10.0.2" + oniguruma-to-es: "npm:^4.3.3" + checksum: 10/acd4e78fdc732738c6a3a9f254011b7d8995a2149f2f72f7396d5e4514f3b0c3fdc767d52bf8852d65066e613dd8d1157bc4900442ab42684db7ef7396a44871 languageName: node linkType: hard -"@shikijs/engine-oniguruma@npm:1.29.2": - version: 1.29.2 - resolution: "@shikijs/engine-oniguruma@npm:1.29.2" +"@shikijs/engine-oniguruma@npm:3.13.0": + version: 3.13.0 + resolution: "@shikijs/engine-oniguruma@npm:3.13.0" dependencies: - "@shikijs/types": "npm:1.29.2" - "@shikijs/vscode-textmate": "npm:^10.0.1" - checksum: 10/bb3e2c01da84d573251ebc289b1ecf815261024dea5bddb93ad56c3504a71cde3630db070be401ed3bbcd23a8a839ec78984a82317f9c9d0bba58daed935b781 + "@shikijs/types": "npm:3.13.0" + "@shikijs/vscode-textmate": "npm:^10.0.2" + checksum: 10/bc7f1c69640ccece9a3ac3f3adc9cebde9c24bcd722747cc464c338a32725af2b9087a42646cd0f56fbea98d99414918c558f2ca0beadc13db0a740183e7707f languageName: node linkType: hard -"@shikijs/langs@npm:1.29.2": - version: 1.29.2 - resolution: "@shikijs/langs@npm:1.29.2" +"@shikijs/langs@npm:3.13.0": + version: 3.13.0 + resolution: "@shikijs/langs@npm:3.13.0" dependencies: - "@shikijs/types": "npm:1.29.2" - checksum: 10/01f62d31c653c718a992918357e54d2d312c8da407997565fc19056fbf47f0fadc0f9f4b5fe1e1ba7b7d08e3984fb1f962159503ef0edd81fab5ee8bfdbf9080 + "@shikijs/types": "npm:3.13.0" + checksum: 10/2a0478246ce61745d9012cf5051c9790ef5afad6c89fe5716be4a81d04d7dfd9ffc90af0bad0be0061111bfc001a3bc0c55ffd4fc0b82bc1c09a4c33d7b593ca languageName: node linkType: hard -"@shikijs/themes@npm:1.29.2": - version: 1.29.2 - resolution: "@shikijs/themes@npm:1.29.2" +"@shikijs/themes@npm:3.13.0": + version: 3.13.0 + resolution: "@shikijs/themes@npm:3.13.0" dependencies: - "@shikijs/types": "npm:1.29.2" - checksum: 10/b81606dd882136e3fd751d0829133b5e66b10b8e32bd52ce16e7eac8755891c23e43f3ce06e65b97a75d2bc3b17e7fdb9115e9812679bb820ab163915868fd8b + "@shikijs/types": "npm:3.13.0" + checksum: 10/4ab0ecf1ddb35e387cef22e267cdf3ed7548a84f24ad58885d19401f75cc01a5a10152ee0196222e01cead8202f20012615aa0076f798599f7dfc2bb7beaa617 languageName: node linkType: hard -"@shikijs/types@npm:1.29.2": - version: 1.29.2 - resolution: "@shikijs/types@npm:1.29.2" +"@shikijs/transformers@npm:^3.13.0": + version: 3.13.0 + resolution: "@shikijs/transformers@npm:3.13.0" dependencies: - "@shikijs/vscode-textmate": "npm:^10.0.1" + "@shikijs/core": "npm:3.13.0" + "@shikijs/types": "npm:3.13.0" + checksum: 10/30f3e66334b60320dc976b9f645f823ba13b312aead4c891ae6db23d076f1570dad488443f603fb5eecd5937973ba7c2907da6678ccae2363a66e0c5b173bb02 + languageName: node + linkType: hard + +"@shikijs/types@npm:3.13.0": + version: 3.13.0 + resolution: "@shikijs/types@npm:3.13.0" + dependencies: + "@shikijs/vscode-textmate": "npm:^10.0.2" "@types/hast": "npm:^3.0.4" - checksum: 10/579e64b6e8cb83023232b8060b08f51cff3909b199d0d1a0c58ed500c898dd34b74bf0457336fa2e6bee1005889e198d7d924347ad616eee30c6ae4c89a67ab8 + checksum: 10/a57cc95847edd84134166de1f11a044e356581834b2dfd75dcd67f2221aef6f20f505839c090a76b7c8e0a79d7d5cee829de50d261f363a633ae9afca29546c4 languageName: node linkType: hard -"@shikijs/vscode-textmate@npm:^10.0.1": +"@shikijs/vscode-textmate@npm:^10.0.2": version: 10.0.2 resolution: "@shikijs/vscode-textmate@npm:10.0.2" checksum: 10/d924cba8a01cd9ca12f56ba097d628fdb81455abb85884c8d8a5ae85b628a37dd5907e7691019b97107bd6608c866adf91ba04a1c3bba391281c88e386c044ea @@ -1982,7 +2070,7 @@ __metadata: languageName: node linkType: hard -"chalk@npm:^4.0.0, chalk@npm:^4.1.2": +"chalk@npm:^4.0.0": version: 4.1.2 resolution: "chalk@npm:4.1.2" dependencies: @@ -2053,14 +2141,10 @@ __metadata: languageName: node linkType: hard -"cliui@npm:^8.0.1": - version: 8.0.1 - resolution: "cliui@npm:8.0.1" - dependencies: - string-width: "npm:^4.2.0" - strip-ansi: "npm:^6.0.1" - wrap-ansi: "npm:^7.0.0" - checksum: 10/eaa5561aeb3135c2cddf7a3b3f562fc4238ff3b3fc666869ef2adf264be0f372136702f16add9299087fb1907c2e4ec5dbfe83bd24bce815c70a80c6c1a2e950 +"clsx@npm:^2.1.1": + version: 2.1.1 + resolution: "clsx@npm:2.1.1" + checksum: 10/cdfb57fa6c7649bbff98d9028c2f0de2f91c86f551179541cf784b1cfdc1562dcb951955f46d54d930a3879931a980e32a46b598acaea274728dbe068deca919 languageName: node linkType: hard @@ -2136,26 +2220,6 @@ __metadata: languageName: node linkType: hard -"concurrently@npm:^8.2.2": - version: 8.2.2 - resolution: "concurrently@npm:8.2.2" - dependencies: - chalk: "npm:^4.1.2" - date-fns: "npm:^2.30.0" - lodash: "npm:^4.17.21" - rxjs: "npm:^7.8.1" - shell-quote: "npm:^1.8.1" - spawn-command: "npm:0.0.2" - supports-color: "npm:^8.1.1" - tree-kill: "npm:^1.2.2" - yargs: "npm:^17.7.2" - bin: - conc: dist/bin/concurrently.js - concurrently: dist/bin/concurrently.js - checksum: 10/dcb1aa69d9c611a7bda9d4fc0fe1e388f971d1744acec7e0d52dffa2ef55743f1266ec9292f414c5789b9f61734b3fce772bd005d4de9564a949fb121b97bae1 - languageName: node - linkType: hard - "crelt@npm:^1.0.5, crelt@npm:^1.0.6": version: 1.0.6 resolution: "crelt@npm:1.0.6" @@ -2221,15 +2285,6 @@ __metadata: languageName: node linkType: hard -"date-fns@npm:^2.30.0": - version: 2.30.0 - resolution: "date-fns@npm:2.30.0" - dependencies: - "@babel/runtime": "npm:^7.21.0" - checksum: 10/70b3e8ea7aaaaeaa2cd80bd889622a4bcb5d8028b4de9162cbcda359db06e16ff6e9309e54eead5341e71031818497f19aaf9839c87d1aba1e27bb4796e758a9 - languageName: node - linkType: hard - "debug@npm:4, debug@npm:^4.0.0, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.4, debug@npm:^4.4.0": version: 4.4.1 resolution: "debug@npm:4.4.1" @@ -2323,6 +2378,7 @@ __metadata: version: 0.0.0-use.local resolution: "docs-ui@workspace:." dependencies: + "@base-ui-components/react": "npm:^1.0.0-beta.4" "@codemirror/lang-sass": "npm:^6.0.2" "@codemirror/view": "npm:^6.34.4" "@lezer/highlight": "npm:^1.2.1" @@ -2330,6 +2386,7 @@ __metadata: "@mdx-js/react": "npm:^3.1.0" "@next/mdx": "npm:15.3.4" "@remixicon/react": "npm:^4.6.0" + "@shikijs/transformers": "npm:^3.13.0" "@storybook/react": "npm:^8.6.12" "@types/mdx": "npm:^2.0.13" "@types/node": "npm:^20" @@ -2338,7 +2395,7 @@ __metadata: "@uiw/codemirror-themes": "npm:^4.23.7" "@uiw/react-codemirror": "npm:^4.23.7" chokidar: "npm:^3.6.0" - concurrently: "npm:^8.2.2" + clsx: "npm:^2.1.1" eslint: "npm:^8" eslint-config-next: "npm:15.3.4" html-react-parser: "npm:^5.2.5" @@ -2349,7 +2406,7 @@ __metadata: prop-types: "npm:^15.8.1" react: "npm:19.1.1" react-dom: "npm:19.1.1" - shiki: "npm:^1.26.1" + shiki: "npm:^3.13.0" storybook: "npm:^8.6.12" typescript: "npm:^5" languageName: unknown @@ -2429,13 +2486,6 @@ __metadata: languageName: node linkType: hard -"emoji-regex-xs@npm:^1.0.0": - version: 1.0.0 - resolution: "emoji-regex-xs@npm:1.0.0" - checksum: 10/e216ec4270f765e1097cefc1b9518a7166b872b4424c60a85d79765f318d989cd458e036c76c13e9ce2ed1fe1bb5935a7fd5c1fab7600668bc8e92a789045b3c - languageName: node - linkType: hard - "emoji-regex@npm:^8.0.0": version: 8.0.0 resolution: "emoji-regex@npm:8.0.0" @@ -2752,13 +2802,6 @@ __metadata: languageName: node linkType: hard -"escalade@npm:^3.1.1": - version: 3.2.0 - resolution: "escalade@npm:3.2.0" - checksum: 10/9d7169e3965b2f9ae46971afa392f6e5a25545ea30f2e2dd99c9b0a95a3f52b5653681a84f5b2911a413ddad2d7a93d3514165072f349b5ffc59c75a899970d6 - languageName: node - linkType: hard - "escape-string-regexp@npm:^4.0.0": version: 4.0.0 resolution: "escape-string-regexp@npm:4.0.0" @@ -3268,11 +3311,11 @@ __metadata: languageName: node linkType: hard -"framer-motion@npm:^12.23.12": - version: 12.23.12 - resolution: "framer-motion@npm:12.23.12" +"framer-motion@npm:^12.23.24": + version: 12.23.24 + resolution: "framer-motion@npm:12.23.24" dependencies: - motion-dom: "npm:^12.23.12" + motion-dom: "npm:^12.23.23" motion-utils: "npm:^12.23.6" tslib: "npm:^2.4.0" peerDependencies: @@ -3286,7 +3329,7 @@ __metadata: optional: true react-dom: optional: true - checksum: 10/c15af3dc22476bf10d7509b9a336d113ed0fbeba57fca81ce5a6bf07f0d659ebc3588c3a839475cbdfc9601b64e2fc7551911e1c806dd2b896603c8a2c0c3870 + checksum: 10/c46015626d140557f3f225ffeaea14146988a42a99b33e46e3efbab88814343867a63014544169283b9518e1a3abed231ccefd890e601cf66b6c43425b0465e2 languageName: node linkType: hard @@ -3353,13 +3396,6 @@ __metadata: languageName: node linkType: hard -"get-caller-file@npm:^2.0.5": - version: 2.0.5 - resolution: "get-caller-file@npm:2.0.5" - checksum: 10/b9769a836d2a98c3ee734a88ba712e62703f1df31b94b784762c433c27a386dd6029ff55c2a920c392e33657d80191edbf18c61487e198844844516f843496b9 - languageName: node - linkType: hard - "get-intrinsic@npm:^1.2.4, get-intrinsic@npm:^1.2.5, get-intrinsic@npm:^1.2.6, get-intrinsic@npm:^1.2.7, get-intrinsic@npm:^1.3.0": version: 1.3.0 resolution: "get-intrinsic@npm:1.3.0" @@ -3577,7 +3613,7 @@ __metadata: languageName: node linkType: hard -"hast-util-to-html@npm:^9.0.4": +"hast-util-to-html@npm:^9.0.5": version: 9.0.5 resolution: "hast-util-to-html@npm:9.0.5" dependencies: @@ -3639,20 +3675,20 @@ __metadata: linkType: hard "html-react-parser@npm:^5.2.5": - version: 5.2.6 - resolution: "html-react-parser@npm:5.2.6" + version: 5.2.8 + resolution: "html-react-parser@npm:5.2.8" dependencies: domhandler: "npm:5.0.3" html-dom-parser: "npm:5.1.1" react-property: "npm:2.0.2" - style-to-js: "npm:1.1.17" + style-to-js: "npm:1.1.19" peerDependencies: "@types/react": 0.14 || 15 || 16 || 17 || 18 || 19 react: 0.14 || 15 || 16 || 17 || 18 || 19 peerDependenciesMeta: "@types/react": optional: true - checksum: 10/be2903fd932d44ff6cae66bd18b025d318879e16eb1e67c0ae6dc640e5c77509da41b61948c0d404a43b5362b57b7cfa3963df242b9773d4c7a16f410db7f2ac + checksum: 10/492d44f65c2a8647bb7d6db3865363739a00696687024b978f5f5e29475c40bd470c91e9ddefe0f1a846c85761f046e9a92fa3fe0e5581df43ac587787703b31 languageName: node linkType: hard @@ -3759,10 +3795,10 @@ __metadata: languageName: node linkType: hard -"inline-style-parser@npm:0.2.4": - version: 0.2.4 - resolution: "inline-style-parser@npm:0.2.4" - checksum: 10/80814479d1f3c9cbd102f9de4cd6558cf43cc2e48640e81c4371c3634f1e8b6dfeb2f21063cfa31d46cc83e834c20cd59ed9eeed9bfd45ef5bc02187ad941faf +"inline-style-parser@npm:0.2.6": + version: 0.2.6 + resolution: "inline-style-parser@npm:0.2.6" + checksum: 10/0be8356fcd766b7b170991293c9ad953f057ac9719960f51f4eb54af1a79c34638f591691f2ab43f57bef2e28e9c1239b8ae5f6b72bff2c652f828ccc621c08c languageName: node linkType: hard @@ -4273,92 +4309,102 @@ __metadata: languageName: node linkType: hard -"lightningcss-darwin-arm64@npm:1.30.1": - version: 1.30.1 - resolution: "lightningcss-darwin-arm64@npm:1.30.1" +"lightningcss-android-arm64@npm:1.30.2": + version: 1.30.2 + resolution: "lightningcss-android-arm64@npm:1.30.2" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"lightningcss-darwin-arm64@npm:1.30.2": + version: 1.30.2 + resolution: "lightningcss-darwin-arm64@npm:1.30.2" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"lightningcss-darwin-x64@npm:1.30.1": - version: 1.30.1 - resolution: "lightningcss-darwin-x64@npm:1.30.1" +"lightningcss-darwin-x64@npm:1.30.2": + version: 1.30.2 + resolution: "lightningcss-darwin-x64@npm:1.30.2" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"lightningcss-freebsd-x64@npm:1.30.1": - version: 1.30.1 - resolution: "lightningcss-freebsd-x64@npm:1.30.1" +"lightningcss-freebsd-x64@npm:1.30.2": + version: 1.30.2 + resolution: "lightningcss-freebsd-x64@npm:1.30.2" conditions: os=freebsd & cpu=x64 languageName: node linkType: hard -"lightningcss-linux-arm-gnueabihf@npm:1.30.1": - version: 1.30.1 - resolution: "lightningcss-linux-arm-gnueabihf@npm:1.30.1" +"lightningcss-linux-arm-gnueabihf@npm:1.30.2": + version: 1.30.2 + resolution: "lightningcss-linux-arm-gnueabihf@npm:1.30.2" conditions: os=linux & cpu=arm languageName: node linkType: hard -"lightningcss-linux-arm64-gnu@npm:1.30.1": - version: 1.30.1 - resolution: "lightningcss-linux-arm64-gnu@npm:1.30.1" +"lightningcss-linux-arm64-gnu@npm:1.30.2": + version: 1.30.2 + resolution: "lightningcss-linux-arm64-gnu@npm:1.30.2" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"lightningcss-linux-arm64-musl@npm:1.30.1": - version: 1.30.1 - resolution: "lightningcss-linux-arm64-musl@npm:1.30.1" +"lightningcss-linux-arm64-musl@npm:1.30.2": + version: 1.30.2 + resolution: "lightningcss-linux-arm64-musl@npm:1.30.2" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"lightningcss-linux-x64-gnu@npm:1.30.1": - version: 1.30.1 - resolution: "lightningcss-linux-x64-gnu@npm:1.30.1" +"lightningcss-linux-x64-gnu@npm:1.30.2": + version: 1.30.2 + resolution: "lightningcss-linux-x64-gnu@npm:1.30.2" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"lightningcss-linux-x64-musl@npm:1.30.1": - version: 1.30.1 - resolution: "lightningcss-linux-x64-musl@npm:1.30.1" +"lightningcss-linux-x64-musl@npm:1.30.2": + version: 1.30.2 + resolution: "lightningcss-linux-x64-musl@npm:1.30.2" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"lightningcss-win32-arm64-msvc@npm:1.30.1": - version: 1.30.1 - resolution: "lightningcss-win32-arm64-msvc@npm:1.30.1" +"lightningcss-win32-arm64-msvc@npm:1.30.2": + version: 1.30.2 + resolution: "lightningcss-win32-arm64-msvc@npm:1.30.2" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"lightningcss-win32-x64-msvc@npm:1.30.1": - version: 1.30.1 - resolution: "lightningcss-win32-x64-msvc@npm:1.30.1" +"lightningcss-win32-x64-msvc@npm:1.30.2": + version: 1.30.2 + resolution: "lightningcss-win32-x64-msvc@npm:1.30.2" conditions: os=win32 & cpu=x64 languageName: node linkType: hard "lightningcss@npm:^1.28.2": - version: 1.30.1 - resolution: "lightningcss@npm:1.30.1" + version: 1.30.2 + resolution: "lightningcss@npm:1.30.2" dependencies: detect-libc: "npm:^2.0.3" - lightningcss-darwin-arm64: "npm:1.30.1" - lightningcss-darwin-x64: "npm:1.30.1" - lightningcss-freebsd-x64: "npm:1.30.1" - lightningcss-linux-arm-gnueabihf: "npm:1.30.1" - lightningcss-linux-arm64-gnu: "npm:1.30.1" - lightningcss-linux-arm64-musl: "npm:1.30.1" - lightningcss-linux-x64-gnu: "npm:1.30.1" - lightningcss-linux-x64-musl: "npm:1.30.1" - lightningcss-win32-arm64-msvc: "npm:1.30.1" - lightningcss-win32-x64-msvc: "npm:1.30.1" + lightningcss-android-arm64: "npm:1.30.2" + lightningcss-darwin-arm64: "npm:1.30.2" + lightningcss-darwin-x64: "npm:1.30.2" + lightningcss-freebsd-x64: "npm:1.30.2" + lightningcss-linux-arm-gnueabihf: "npm:1.30.2" + lightningcss-linux-arm64-gnu: "npm:1.30.2" + lightningcss-linux-arm64-musl: "npm:1.30.2" + lightningcss-linux-x64-gnu: "npm:1.30.2" + lightningcss-linux-x64-musl: "npm:1.30.2" + lightningcss-win32-arm64-msvc: "npm:1.30.2" + lightningcss-win32-x64-msvc: "npm:1.30.2" dependenciesMeta: + lightningcss-android-arm64: + optional: true lightningcss-darwin-arm64: optional: true lightningcss-darwin-x64: @@ -4379,7 +4425,7 @@ __metadata: optional: true lightningcss-win32-x64-msvc: optional: true - checksum: 10/6c921135216cc498dfcb87e35dffe8432e99306cbd58009c598b1daf20c81cc14535abbd4c1066e5d1faf4080ed44a2995e8ecc343633db4897a2d041b76fb05 + checksum: 10/d6cc06d9bac295589a49446e9c45a241dfa16f4f81a7318c26cbc0be3e189003ec0da5d9a0fd9bdffc63a3ce05878cc7329277eaac77a826e8b68c73dc96cfda languageName: node linkType: hard @@ -4399,13 +4445,6 @@ __metadata: languageName: node linkType: hard -"lodash@npm:^4.17.21": - version: 4.17.21 - resolution: "lodash@npm:4.17.21" - checksum: 10/c08619c038846ea6ac754abd6dd29d2568aa705feb69339e836dfa8d8b09abbb2f859371e86863eda41848221f9af43714491467b5b0299122431e202bb0c532 - languageName: node - linkType: hard - "longest-streak@npm:^3.0.0": version: 3.1.0 resolution: "longest-streak@npm:3.1.0" @@ -5069,12 +5108,12 @@ __metadata: languageName: node linkType: hard -"motion-dom@npm:^12.23.12": - version: 12.23.12 - resolution: "motion-dom@npm:12.23.12" +"motion-dom@npm:^12.23.23": + version: 12.23.23 + resolution: "motion-dom@npm:12.23.23" dependencies: motion-utils: "npm:^12.23.6" - checksum: 10/e13cbd1f32ad30dc642ebc83f7f35d0fb76c4d93902daebdbbea2cdb85bb07481fb4fe315b3e2c899076dab5490fb611ff803c334b336bc4fcd59b9a1aa6d7ff + checksum: 10/6bec34c28b478622bd23ce919c0d64e7f372cbc3f88e665f1e34fbf590e9bb53eebea086fb28f4e0c31941bd5274e086dc9b5360b68c4976891b94c4e92ca01a languageName: node linkType: hard @@ -5086,10 +5125,10 @@ __metadata: linkType: hard "motion@npm:^12.4.1": - version: 12.23.12 - resolution: "motion@npm:12.23.12" + version: 12.23.24 + resolution: "motion@npm:12.23.24" dependencies: - framer-motion: "npm:^12.23.12" + framer-motion: "npm:^12.23.24" tslib: "npm:^2.4.0" peerDependencies: "@emotion/is-prop-valid": "*" @@ -5102,7 +5141,7 @@ __metadata: optional: true react-dom: optional: true - checksum: 10/95670351c14d6925e891fac3d12719ed2e46000366abd38096870c46271071e3f22d0e9fa523809e614ee58aaeb1865fc19398dc8b408c2594512dc0eef392db + checksum: 10/d63d951676058001c6f234a6bdf8ed5a8fd222a38c1871ef209ed1fd87f2a288ec0e267fa616d80ac8837d0836a5f93ae85a80d773314517f19f304f25b81c3d languageName: node linkType: hard @@ -5146,20 +5185,20 @@ __metadata: linkType: hard "next-mdx-remote-client@npm:^2.1.2": - version: 2.1.3 - resolution: "next-mdx-remote-client@npm:2.1.3" + version: 2.1.7 + resolution: "next-mdx-remote-client@npm:2.1.7" dependencies: "@babel/code-frame": "npm:^7.27.1" - "@mdx-js/mdx": "npm:^3.1.0" - "@mdx-js/react": "npm:^3.1.0" - remark-mdx-remove-esm: "npm:^1.2.0" + "@mdx-js/mdx": "npm:^3.1.1" + "@mdx-js/react": "npm:^3.1.1" + remark-mdx-remove-esm: "npm:^1.2.1" serialize-error: "npm:^12.0.0" vfile: "npm:^6.0.3" vfile-matter: "npm:^5.0.1" peerDependencies: - react: ^19.1.0 - react-dom: ^19.1.0 - checksum: 10/3837c63edf7d707de2eea9b4c565eeb7e505a769e93c2673f746ec9993c644f7cd0aacb8bad911eff9ea63190f48c2c16ffe98d47c39591c5dac29c8bf9a9e0c + react: ^19.1 + react-dom: ^19.1 + checksum: 10/8aaf6c2f11b0ed398703165c35c0355d1f058521780191684e6fe7e45698effe50ba68b154bae9db84b13e82dff98f380224d588acca6e389aeb73062ecdd31d languageName: node linkType: hard @@ -5351,14 +5390,21 @@ __metadata: languageName: node linkType: hard -"oniguruma-to-es@npm:^2.2.0": - version: 2.3.0 - resolution: "oniguruma-to-es@npm:2.3.0" +"oniguruma-parser@npm:^0.12.1": + version: 0.12.1 + resolution: "oniguruma-parser@npm:0.12.1" + checksum: 10/2e7e308ec222f377b4be21e87f009729ce1cc014511d35a50440e7270a17413e4859b8c7ad1985123abe1089bda5ad2d0feccc75f7de25d9cb272b7cc34b4f3c + languageName: node + linkType: hard + +"oniguruma-to-es@npm:^4.3.3": + version: 4.3.3 + resolution: "oniguruma-to-es@npm:4.3.3" dependencies: - emoji-regex-xs: "npm:^1.0.0" - regex: "npm:^5.1.1" - regex-recursion: "npm:^5.1.1" - checksum: 10/7ac89fe04791650c21a146e58c985de394597e0ab3eb7d7c46699d09f87d6aca63d9f2470f1473c5adfa16961c360df6858e6a24b2429ebd7c73620ca68aec98 + oniguruma-parser: "npm:^0.12.1" + regex: "npm:^6.0.1" + regex-recursion: "npm:^6.0.2" + checksum: 10/49b372569d335077c32bda066ac1da4a3f15dd25b717025cf43417fabd71d56e1152debcd8a832596e180d721b808822c88e65eb12abb26665ab8fe017f3c861 languageName: node linkType: hard @@ -5714,13 +5760,12 @@ __metadata: languageName: node linkType: hard -"regex-recursion@npm:^5.1.1": - version: 5.1.1 - resolution: "regex-recursion@npm:5.1.1" +"regex-recursion@npm:^6.0.2": + version: 6.0.2 + resolution: "regex-recursion@npm:6.0.2" dependencies: - regex: "npm:^5.1.1" regex-utilities: "npm:^2.3.0" - checksum: 10/9ec7e677743e00c14c3d5c63de1d15e5126a69c58da43e60b2c49abf940981f0269442c919e03f66d6837d0ac55ccf7981581e9d1be2a332a968c016edee226e + checksum: 10/ce25d54bdf79e38ae663c26a7f265754047b918e8b771b4ed3c871e9a926b85b195dcd0dd7a6444c82d2c9db9b2cbcaf51a353a807b469a3ad7a2172f27f21d4 languageName: node linkType: hard @@ -5731,12 +5776,12 @@ __metadata: languageName: node linkType: hard -"regex@npm:^5.1.1": - version: 5.1.1 - resolution: "regex@npm:5.1.1" +"regex@npm:^6.0.1": + version: 6.0.1 + resolution: "regex@npm:6.0.1" dependencies: regex-utilities: "npm:^2.3.0" - checksum: 10/956d0f34afa8086551dc9a0e2c575cb6d7ed5122d6d440a72cfbcd1f9adb5a3dcce4dcb61ec69eaecfcbc508cfd25075ac60d011bab5f28f1b2a36220e09a245 + checksum: 10/8f8c35ce0a74b65ce29495b8c18767a4b2724ca4a4b4acfc4d45a2851a5afdbc4daae447a1a762f8faf06a1efe3d7dd8cfc4ebbee9f7c55411d4bd30f6976026 languageName: node linkType: hard @@ -5765,16 +5810,16 @@ __metadata: languageName: node linkType: hard -"remark-mdx-remove-esm@npm:^1.2.0": - version: 1.2.0 - resolution: "remark-mdx-remove-esm@npm:1.2.0" +"remark-mdx-remove-esm@npm:^1.2.1": + version: 1.2.1 + resolution: "remark-mdx-remove-esm@npm:1.2.1" dependencies: "@types/mdast": "npm:^4.0.4" mdast-util-mdxjs-esm: "npm:^2.0.1" unist-util-remove: "npm:^4.0.0" peerDependencies: unified: ^11 - checksum: 10/0a1155e08395ab73f877948455fc04d7457864f7556617efabecae7ea1b441cec2ca7ee9672e16a6cd56f2a16c57a4778f901e77d19052d1dd245de5bdf4618f + checksum: 10/092e95ca0649cfe08c47900d9922172b531e1d60b0f90955f51a7282620a1ae53a95dddde3f8d789cdd114a2d515a9372bc72b3e46200ff6f930bd89f0985982 languageName: node linkType: hard @@ -5813,10 +5858,10 @@ __metadata: languageName: node linkType: hard -"require-directory@npm:^2.1.1": - version: 2.1.1 - resolution: "require-directory@npm:2.1.1" - checksum: 10/a72468e2589270d91f06c7d36ec97a88db53ae5d6fe3787fadc943f0b0276b10347f89b363b2a82285f650bdcc135ad4a257c61bdd4d00d6df1fa24875b0ddaf +"reselect@npm:^5.1.1": + version: 5.1.1 + resolution: "reselect@npm:5.1.1" + checksum: 10/1fdae11a39ed9c8d85a24df19517c8372ee24fefea9cce3fae9eaad8e9cefbba5a3d4940c6fe31296b6addf76e035588c55798f7e6e147e1b7c0855f119e7fa5 languageName: node linkType: hard @@ -5920,15 +5965,6 @@ __metadata: languageName: node linkType: hard -"rxjs@npm:^7.8.1": - version: 7.8.2 - resolution: "rxjs@npm:7.8.2" - dependencies: - tslib: "npm:^2.1.0" - checksum: 10/03dff09191356b2b87d94fbc1e97c4e9eb3c09d4452399dddd451b09c2f1ba8d56925a40af114282d7bc0c6fe7514a2236ca09f903cf70e4bbf156650dddb49d - languageName: node - linkType: hard - "safe-array-concat@npm:^1.1.3": version: 1.1.3 resolution: "safe-array-concat@npm:1.1.3" @@ -6135,26 +6171,19 @@ __metadata: languageName: node linkType: hard -"shell-quote@npm:^1.8.1": - version: 1.8.3 - resolution: "shell-quote@npm:1.8.3" - checksum: 10/5473e354637c2bd698911224129c9a8961697486cff1fb221f234d71c153fc377674029b0223d1d3c953a68d451d79366abfe53d1a0b46ee1f28eb9ade928f4c - languageName: node - linkType: hard - -"shiki@npm:^1.26.1": - version: 1.29.2 - resolution: "shiki@npm:1.29.2" +"shiki@npm:^3.13.0": + version: 3.13.0 + resolution: "shiki@npm:3.13.0" dependencies: - "@shikijs/core": "npm:1.29.2" - "@shikijs/engine-javascript": "npm:1.29.2" - "@shikijs/engine-oniguruma": "npm:1.29.2" - "@shikijs/langs": "npm:1.29.2" - "@shikijs/themes": "npm:1.29.2" - "@shikijs/types": "npm:1.29.2" - "@shikijs/vscode-textmate": "npm:^10.0.1" + "@shikijs/core": "npm:3.13.0" + "@shikijs/engine-javascript": "npm:3.13.0" + "@shikijs/engine-oniguruma": "npm:3.13.0" + "@shikijs/langs": "npm:3.13.0" + "@shikijs/themes": "npm:3.13.0" + "@shikijs/types": "npm:3.13.0" + "@shikijs/vscode-textmate": "npm:^10.0.2" "@types/hast": "npm:^3.0.4" - checksum: 10/93de1ebc768ff497ba36720f3b385bff65a853248fdfc0afcb2aea81a43560b1bdc7db9e8417df11a87e106b0de0943989c6301daf62a43d09bd9ac7bc776bf3 + checksum: 10/8d7cd039ac8df548f724047b4ba81cd84c09e8b96f411eb7c781d3d105c96ff2ec6f64e1bac724fd6fc56ac2926322bc2466124ea279d04c5ef668d5a04219bb languageName: node linkType: hard @@ -6278,13 +6307,6 @@ __metadata: languageName: node linkType: hard -"spawn-command@npm:0.0.2": - version: 0.0.2 - resolution: "spawn-command@npm:0.0.2" - checksum: 10/f13e8c3c63abd4a0b52fb567eba5f7940d480c5ed3ec61781d38a1850f179b1196c39e6efa2bbd301f82c1bf1cd7807abc8fbd8fc8e44bcaa3975a124c0d1657 - languageName: node - linkType: hard - "ssri@npm:^12.0.0": version: 12.0.0 resolution: "ssri@npm:12.0.0" @@ -6329,7 +6351,7 @@ __metadata: languageName: node linkType: hard -"string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^4.1.0, string-width@npm:^4.2.0, string-width@npm:^4.2.3": +"string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^4.1.0": version: 4.2.3 resolution: "string-width@npm:4.2.3" dependencies: @@ -6480,21 +6502,21 @@ __metadata: languageName: node linkType: hard -"style-to-js@npm:1.1.17, style-to-js@npm:^1.0.0": - version: 1.1.17 - resolution: "style-to-js@npm:1.1.17" +"style-to-js@npm:1.1.19, style-to-js@npm:^1.0.0": + version: 1.1.19 + resolution: "style-to-js@npm:1.1.19" dependencies: - style-to-object: "npm:1.0.9" - checksum: 10/431f2fca8a55a61939a83ff0f58638e2996621ad93a97cf93f2be5115f411330d4e506ccf18621bd45607ec161546b763bb6961ad08238ad939b6261ff377230 + style-to-object: "npm:1.0.12" + checksum: 10/c25f3a96368f258c90a886ec20b8a15ae035664e46eeea6c36f105fbc1e099ee81eaac355b7f5f948d8bb9d103c048a97b3d09e785c680ff2b4dd6d77cb76a95 languageName: node linkType: hard -"style-to-object@npm:1.0.9": - version: 1.0.9 - resolution: "style-to-object@npm:1.0.9" +"style-to-object@npm:1.0.12": + version: 1.0.12 + resolution: "style-to-object@npm:1.0.12" dependencies: - inline-style-parser: "npm:0.2.4" - checksum: 10/fd0c131a83103fe4025afd8e0fd90c605054d485ad80f2ab402e7afa79f482f4b05fff40b6aa661cb1b835e5c56bb0644dc38cbf9b3d2982fc552435db3dae50 + inline-style-parser: "npm:0.2.6" + checksum: 10/97b91c2056f4fedb573224accba080aaa96b48a7b120a007b83415c9b18b727d0d6588b9168a1b85da06dd1abacc328f6dd774e907721901e40503a2e7a78c89 languageName: node linkType: hard @@ -6523,15 +6545,6 @@ __metadata: languageName: node linkType: hard -"supports-color@npm:^8.1.1": - version: 8.1.1 - resolution: "supports-color@npm:8.1.1" - dependencies: - has-flag: "npm:^4.0.0" - checksum: 10/157b534df88e39c5518c5e78c35580c1eca848d7dbaf31bbe06cdfc048e22c7ff1a9d046ae17b25691128f631a51d9ec373c1b740c12ae4f0de6e292037e4282 - languageName: node - linkType: hard - "supports-preserve-symlinks-flag@npm:^1.0.0": version: 1.0.0 resolution: "supports-preserve-symlinks-flag@npm:1.0.0" @@ -6539,6 +6552,13 @@ __metadata: languageName: node linkType: hard +"tabbable@npm:^6.2.0": + version: 6.3.0 + resolution: "tabbable@npm:6.3.0" + checksum: 10/3e54a0b770d26bc20c3de5837652be19f5efa8bfa869f580af24bcf60de934506e9401a577213186b5e86ebcf6b5290a5429d354cc3041471815f5095e44e51a + languageName: node + linkType: hard + "tar@npm:^7.4.3": version: 7.4.3 resolution: "tar@npm:7.4.3" @@ -6586,15 +6606,6 @@ __metadata: languageName: node linkType: hard -"tree-kill@npm:^1.2.2": - version: 1.2.2 - resolution: "tree-kill@npm:1.2.2" - bin: - tree-kill: cli.js - checksum: 10/49117f5f410d19c84b0464d29afb9642c863bc5ba40fcb9a245d474c6d5cc64d1b177a6e6713129eb346b40aebb9d4631d967517f9fbe8251c35b21b13cd96c7 - languageName: node - linkType: hard - "trim-lines@npm:^3.0.0": version: 3.0.1 resolution: "trim-lines@npm:3.0.1" @@ -6630,7 +6641,7 @@ __metadata: languageName: node linkType: hard -"tslib@npm:^2.0.1, tslib@npm:^2.1.0, tslib@npm:^2.4.0, tslib@npm:^2.8.0": +"tslib@npm:^2.0.1, tslib@npm:^2.4.0, tslib@npm:^2.8.0": version: 2.8.1 resolution: "tslib@npm:2.8.1" checksum: 10/3e2e043d5c2316461cb54e5c7fe02c30ef6dccb3384717ca22ae5c6b5bc95232a6241df19c622d9c73b809bea33b187f6dbc73030963e29950c2141bc32a79f7 @@ -6714,22 +6725,22 @@ __metadata: linkType: hard "typescript@npm:^5": - version: 5.9.2 - resolution: "typescript@npm:5.9.2" + version: 5.9.3 + resolution: "typescript@npm:5.9.3" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 10/cc2fe6c822819de5d453fa25aa9f32096bf70dde215d481faa1ad84a283dfb264e33988ed8f6d36bc803dd0b16dbe943efa311a798ef76d5b3892a05dfbfd628 + checksum: 10/c089d9d3da2729fd4ac517f9b0e0485914c4b3c26f80dc0cffcb5de1719a17951e92425d55db59515c1a7ddab65808466debb864d0d56dcf43f27007d0709594 languageName: node linkType: hard "typescript@patch:typescript@npm%3A^5#optional!builtin": - version: 5.9.2 - resolution: "typescript@patch:typescript@npm%3A5.9.2#optional!builtin::version=5.9.2&hash=5786d5" + version: 5.9.3 + resolution: "typescript@patch:typescript@npm%3A5.9.3#optional!builtin::version=5.9.3&hash=5786d5" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 10/bd810ab13e8e557225a8b5122370385440b933e4e077d5c7641a8afd207fdc8be9c346e3c678adba934b64e0e70b0acf5eef9493ea05170a48ce22bef845fdc7 + checksum: 10/696e1b017bc2635f4e0c94eb4435357701008e2f272f553d06e35b494b8ddc60aa221145e286c28ace0c89ee32827a28c2040e3a69bdc108b1a5dc8fb40b72e3 languageName: node linkType: hard @@ -6929,6 +6940,15 @@ __metadata: languageName: node linkType: hard +"use-sync-external-store@npm:^1.5.0": + version: 1.6.0 + resolution: "use-sync-external-store@npm:1.6.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + checksum: 10/b40ad2847ba220695bff2d4ba4f4d60391c0fb4fb012faa7a4c18eb38b69181936f5edc55a522c4d20a788d1a879b73c3810952c9d0fd128d01cb3f22042c09e + languageName: node + linkType: hard + "util@npm:^0.12.5": version: 0.12.5 resolution: "util@npm:0.12.5" @@ -7069,7 +7089,7 @@ __metadata: languageName: node linkType: hard -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0, wrap-ansi@npm:^7.0.0": +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version: 7.0.0 resolution: "wrap-ansi@npm:7.0.0" dependencies: @@ -7113,13 +7133,6 @@ __metadata: languageName: node linkType: hard -"y18n@npm:^5.0.5": - version: 5.0.8 - resolution: "y18n@npm:5.0.8" - checksum: 10/5f1b5f95e3775de4514edbb142398a2c37849ccfaf04a015be5d75521e9629d3be29bd4432d23c57f37e5b61ade592fb0197022e9993f81a06a5afbdcda9346d - languageName: node - linkType: hard - "yallist@npm:^4.0.0": version: 4.0.0 resolution: "yallist@npm:4.0.0" @@ -7143,28 +7156,6 @@ __metadata: languageName: node linkType: hard -"yargs-parser@npm:^21.1.1": - version: 21.1.1 - resolution: "yargs-parser@npm:21.1.1" - checksum: 10/9dc2c217ea3bf8d858041252d43e074f7166b53f3d010a8c711275e09cd3d62a002969a39858b92bbda2a6a63a585c7127014534a560b9c69ed2d923d113406e - languageName: node - linkType: hard - -"yargs@npm:^17.7.2": - version: 17.7.2 - resolution: "yargs@npm:17.7.2" - dependencies: - cliui: "npm:^8.0.1" - escalade: "npm:^3.1.1" - get-caller-file: "npm:^2.0.5" - require-directory: "npm:^2.1.1" - string-width: "npm:^4.2.3" - y18n: "npm:^5.0.5" - yargs-parser: "npm:^21.1.1" - checksum: 10/abb3e37678d6e38ea85485ed86ebe0d1e3464c640d7d9069805ea0da12f69d5a32df8e5625e370f9c96dd1c2dc088ab2d0a4dd32af18222ef3c4224a19471576 - languageName: node - linkType: hard - "yocto-queue@npm:^0.1.0": version: 0.1.0 resolution: "yocto-queue@npm:0.1.0" diff --git a/docs/assets/architecture-overview/backstage-front-back-arch.jpeg b/docs/assets/architecture-overview/backstage-front-back-arch.jpeg new file mode 100644 index 0000000000..1cca115c95 Binary files /dev/null and b/docs/assets/architecture-overview/backstage-front-back-arch.jpeg differ diff --git a/docs/assets/architecture-overview/simplified-service-based-plugin-architecture.jpeg b/docs/assets/architecture-overview/simplified-service-based-plugin-architecture.jpeg new file mode 100644 index 0000000000..e8f5144c9a Binary files /dev/null and b/docs/assets/architecture-overview/simplified-service-based-plugin-architecture.jpeg differ diff --git a/docs/assets/architecture-overview/simplified-standalone-plugin-architecture.jpeg b/docs/assets/architecture-overview/simplified-standalone-plugin-architecture.jpeg new file mode 100644 index 0000000000..bf96367b17 Binary files /dev/null and b/docs/assets/architecture-overview/simplified-standalone-plugin-architecture.jpeg differ diff --git a/docs/assets/architecture-overview/simplified-third-party-plugin-architecture.jpeg b/docs/assets/architecture-overview/simplified-third-party-plugin-architecture.jpeg new file mode 100644 index 0000000000..4b9fd1d35f Binary files /dev/null and b/docs/assets/architecture-overview/simplified-third-party-plugin-architecture.jpeg differ diff --git a/docs/assets/technical-overview/backstage-ui-group-ownership.png b/docs/assets/technical-overview/backstage-ui-group-ownership.png new file mode 100644 index 0000000000..607aa7ad47 Binary files /dev/null and b/docs/assets/technical-overview/backstage-ui-group-ownership.png differ diff --git a/docs/assets/user-interface/css-classname-structure.png b/docs/assets/user-interface/css-classname-structure.png new file mode 100644 index 0000000000..c241b182cc Binary files /dev/null and b/docs/assets/user-interface/css-classname-structure.png differ diff --git a/docs/auth/oidc.md b/docs/auth/oidc.md index 250736cec7..7c4b09d9bf 100644 --- a/docs/auth/oidc.md +++ b/docs/auth/oidc.md @@ -1,7 +1,7 @@ --- id: oidc title: OIDC provider from scratch -description: This section shows how to use an OIDC provider from scratch, same steps apply for custom providers. +description: This section shows how to enable and use the Backstage OIDC provider. --- :::info @@ -11,78 +11,65 @@ system, you may want to read [its own article](https://github.com/backstage/back instead, and [consider migrating](../backend-system/building-backends/08-migrating.md)! ::: -This section shows how to use an OIDC provider from scratch, same steps apply for custom -providers. Please note these steps are for using a provider, not how to implement one, -and Backstage recommends creating custom providers specific to the IDP, so we'll use a -`azureOIDC` provider throughout this example, feel free to change any of those refs -to your provider name. +This section shows how to enable and use the Backstage OIDC provider. ## Summary -To add providers not enabled by default like OIDC, we need to follow some steps, we -assume you already have a sign-in page to which we'll add the provider so users can -sign in through the provider. In simple steps here's how you enable the provider: +OIDC is a protocol which has numerous implementations. It's likely that many of your users won't know what the OIDC **protocol** is, but they will recognise your OIDC **implementation**. Backstage supplies a generic `oidc` authorization strategy. You should re-badge this with the name and branding of your OIDC implementation, so that your users will recognise it on the Backstage sign-in page. + +For example, if your organization uses [Keycloak](https://www.keycloak.org), you would re-badge the OIDC provider as `Keycloak` and tell users to `Sign In using Keycloak`. + +## Steps + +The Backstage OIDC provider is not enabled by default. You need to manually enable the provider, and tell it which OIDC server you want to use. + +To enable the Backstage OIDC provider: - Create an API reference to identify the provider. - Create the API factory that will handle the authentication. - Add or reuse an auth provider so you can authenticate. - Add or reuse a resolver to handle the result from the authentication. - Configure the provider to access your 3rd party auth solution. -- Add the provider to sign in page so users can login with it. +- Add the provider to the Backstage sign-in page. + +For simplicity, we assume that you only have a single OIDC provider in your Backstage installation. (If you need to have multiple OIDC providers in Backstage, the steps will be different.) We'll explain each step more in detail next. -### The API reference +### The API Reference -An API reference exist for the sake of **Dependency Injection**, check [Utility APIs][4] -for extended explanation. +An API reference exists to enable **Dependency Injection**. (See [Utility APIs][4] for an extended explanation.) -In this OIDC example, we'll create the API reference directly in the -`packages/app/src/apis.ts` file, it is not a requirement to put the reference in this -file. Any location will do as long as it's available to be imported to where the API -factory is, as well as easily accessible to the rest of the application so any package -and plugin can inject the API instance when necessary. - -An example of such would be when you use an auth provider from a library installed with -NPM, or any other library repository, you would import the API ref from the library. +In this example, we'll create the API ref directly in the `packages/app/src/apis.ts` file. It is not a requirement to put the ref in this file. Any location will do as long as it's available to be imported to where the API factory is, as well as easily accessible to the rest of the application so any package and plugin can inject the API instance when necessary. ```ts -export const azureOIDCAuthApiRef: ApiRef< +export const keycloakAuthApiRef: ApiRef< OpenIdConnectApi & ProfileInfoApi & BackstageIdentityApi & SessionApi > = createApiRef({ - id: 'auth.my-custom-provider', + id: 'auth.keycloak', }); ``` -Please note a few things, the ID can be anything you want as long as it doesn't conflict -with other refs, backstage recommends to use a custom name that references your custom -provider, for example we are using OIDC protocol with Azure, so we could use something -like `auth.azure.oidc` as well. +The `id` of the API ref can be anything you want, as long as it doesn't conflict with other refs. Backstage recommends to use a custom name that references your custom provider. -Also we're exporting this reference, as well as the `typings`, we need to -be able to import this reference anywhere in the app, and the `typings` will tell typescript +:::note TypeScript Note +As we're exporting this API reference, as well as the TypeScript types, we need to +be able to import this reference anywhere in the app. The types will tell TypeScript what instance we're getting from DI when injecting the API. In this case we are defining an API for authentication, so we tell TS that this instance complies with 4 API interfaces: -- The OICD API that will handle authentication. +- The OIDC API that will handle authentication. - Profile API for requesting user profile info from the auth provider in question. - Backstage identity API to handle and associate the user profile with backstage identity. -- Session API, to handle the session the user will have while logged in. +- Session API, to handle the session the user will have while signed in. + ::: -### The API Factory +### The API Factory (and auth provider) -A factory is a function that can take some parameters or dependencies and return an -instance of something, in our case it will be a function that requests some backstage -APIs and use them to create an instance of an OIDC API provider. +The Backstage API factories are part of the Backstage Dependency Injection system. The factory function runs once, when something in your Backstage app first attempts to use an instance of the API it provides. The instance is then cached by the DI system for subsequent lookups. -Please note that this function only runs (creates the instance) when somewhere else in -the app you request the DI to give you an instance of the OIDC provider using the API ref -defined above, and the DI will only run this function the first time, from then on any -other DI injection will just receive the same instance created the first time, basically -the instance is cached by the DI library, a singleton. - -Let's add our OIDC API factory to the APIs array in the `packages/app/src/apis.ts` file: +Let's add a new API factory to the `apis` array in the `packages/app/src/apis.ts` file. We will tell it to use the OIDC auth provider internally. ```ts title="packages/app/src/apis.ts" /* highlight-add-next-line */ @@ -91,26 +78,29 @@ import { OAuth2 } from '@backstage/core-app-api'; export const apis: AnyApiFactory[] = [ /* highlight-add-start */ createApiFactory({ - api: azureOIDCAuthApiRef, + api: keycloakAuthApiRef, deps: { discoveryApi: discoveryApiRef, oauthRequestApi: oauthRequestApiRef, configApi: configApiRef, }, factory: ({ discoveryApi, oauthRequestApi, configApi }) => + // delegate auth to the OAuth2 strategy OAuth2.create({ configApi, discoveryApi, oauthRequestApi, provider: { - id: 'my-auth-provider', - title: 'My custom auth provider', + // this value MUST be 'oidc' + // it maps our Keycloak-branded sign-in provider onto Backstage's generic OIDC auth strategy + id: 'oidc', + title: 'Keycloak', icon: () => null, }, environment: configApi.getOptionalString('auth.environment'), defaultScopes: ['openid', 'profile', 'email'], popupOptions: { - // optional, used to customize login in popup size + // optional, used to customize sign-in window size size: { fullscreen: true, }, @@ -125,27 +115,14 @@ export const apis: AnyApiFactory[] = [ }), }), /* highlight-add-end */ - // .. ]; ``` -Please note we're importing the `OAuth2` class from `@backstage/core-app-api` effectively -delegating the authentication to it. Also we're using the `my-auth-provider` ID to tell -`OAuth2` to use the auth provider we'll define in the next section, and added the default -scopes to request ID, profile, email and user read permissions. - -## The Auth Provider - -The Auth Provider is responsible for authenticating with the 3rd party service, and give -us back the credentials, here's where you pick which protocol to use, be it Auth0, OAuth2, -OIDC, SAML or any other that your 3rd party IDP provider supports. - ### The Resolver -Resolvers exist to map user identity from the 3rd party (in this case an azure IDP -provider) to the backstage user identity. +Resolvers exist to map the user identity from the 3rd party (in this case Keycloak) to the Backstage user identity. -The default OIDC provider has built-in resolvers, here is how you configure them: +The default OIDC provider has a choice of built-in resolvers, here is how you configure them: ```yaml title="app-config.yaml" auth: @@ -159,7 +136,7 @@ auth: - resolver: emailMatchingUserEntityProfileEmail ``` -But you can also write a custom resolver as well, see an example below: +If none of the built-in resolvers are suitable, you can alternatively write a custom resolver. See an example below: ```ts title="in packages/backend/src/index.ts" /* highlight-add-start */ @@ -174,15 +151,15 @@ const myAuthProviderModule = createBackendModule({ // This ID must be exactly "auth" because that's the plugin it targets pluginId: 'auth', // This ID must be unique, but can be anything - moduleId: 'my-auth-provider', + moduleId: 'keycloak-auth-provider', register(reg) { reg.registerInit({ deps: { providers: authProvidersExtensionPoint }, async init({ providers }) { providers.registerProvider({ // This ID must match the actual provider config, e.g. addressing - // auth.providers.azure means that this must be "azure". - providerId: 'my-auth-provider', + // auth.providers.keycloak means that this must be "keycloak". + providerId: 'keycloak', // Use createProxyAuthProviderFactory instead if it's one of the proxy // based providers rather than an OAuth based one factory: createOAuthProviderFactory({ @@ -215,76 +192,85 @@ backend.add(myAuthProviderModule); //... ``` -For a more a detailed explanation about resolvers check the -[Identity Resolver][1] page. +For a more detailed explanation about resolvers check the [Identity Resolver][1] page. -### The configuration +### The Configuration -Since we are using our custom OIDC Auth Provider, we need to add a configuration based -on the provider used, in this case based on OIDC protocol (remember the 3rd party has to -support the protocol). +We will now configure our Keycloak-branded OIDC Auth Provider in Backstage, so that it can talk to our Keycloak server. -In this example we'll configure OIDC with `my-auth-provider`, to do so we need to -[Create app registration][2] in the Azure console, the only difference is that the -`http://localhost:7007/api/auth/microsoft/handler/frame` URL needs to change to -`http://localhost:7007/api/auth/my-auth-provider/handler/frame`. +The first step is to register an OIDC client app for Backstage in your Keycloak server. -Then we need to configure the env variables for the provider, based on the provider's code -in `plugins/auth-backend/src/providers/oidc/provider.ts` we need the following variables -in the `app-config.yaml`: +Then we need to configure the provider. Based on the provider's code in `plugins/auth-backend/src/providers/oidc/provider.ts` we need the following parameters in the `app-config.yaml`: ```yaml title="app-config.yaml" auth: environment: development - ### Providing an auth.session.secret will enable session support in the auth-backend session: - secret: ${SESSION_SECRET} + secret: ${AUTH_SESSION_SECRET} providers: - my-auth-provider: + oidc: development: metadataUrl: https://example.com/.well-known/openid-configuration - clientId: ${AUTH_MY_CLIENT_ID} - clientSecret: ${AUTH_MY_CLIENT_SECRET} + clientId: ${AUTH_OIDC_CLIENT_ID} + clientSecret: ${AUTH_OIDC_CLIENT_SECRET} ``` -Anything enclosed in `${}` can be replaced directly in the yaml, or provided as -environment variables, the way you obtain all these except `scope` and `prompt` is to -check the App Registration you created: +Anything enclosed in `${}` can be replaced directly in the YAML, or provided as environment variables. + +#### Required Parameters + +These parameters must always be set. - `clientId`: Grab from the Overview page. - `clientSecret`: Can only be seen when creating the secret, if you lose it you'll need a new secret. - `metadataUrl`: In Overview > Endpoints tab, grab OpenID Connect metadata document URL. + +The OIDC provider **also** requires the `auth.session.secret` to be set. + +#### Optional Parameters + +These parameters have implicit default values. Don't override them unless you know what you're doing. + - `authorizationUrl` and `tokenUrl`: Open the `metadataUrl` in a browser, that json will hold these 2 urls somewhere in there. -- `tokenEndpointAuthMethod`: Don't define it, use the default unless you know what it does. -- `tokenSignedResponseAlg`: Don't define it, use the default unless you know what it does. +- `tokenEndpointAuthMethod` +- `tokenSignedResponseAlg` - `scope`: Only used if we didn't specify `defaultScopes` in the provider's factory, basically the same thing. -- `prompt`: Recommended to use `auto` so the browser will request login to the IDP if the +- `prompt`: Recommended to use `auto` so the browser will request sign-in to the IDP if the user has no active session. -- `sessionDuration` (optional): Lifespan of the user session. +- `sessionDuration`: Lifespan of the user session. -Note that for the time being, any change in this yaml file requires a restart of the app, -also you need to have the `session.secret` part to use OIDC (some other providers might -need this as well) to support user sessions. +:::note Config Reloading +Backstage does not yet support hot reloading of auth provider configuration. Any changes to this YAML file require a restart of Backstage. +::: -### The Sign In provider +### The Sign-In Page -The last step is to add the provider to the `SignInPage` so users can sign in with your -new provider, please follow the [Sign In Configuration][3] docs, here's where you import -and use the API reference we defined earlier. +The last step is to add the provider to the sign-in page, so users can sign in with your new provider. + +If you are using the standard Backstage [`SignInPage`][3] component, you can just add it to the `providers` array like this: + +```ts title="in packages/app/src/identityProviders.ts" +export const providers = [ + // other providers... + { + id: 'keycloak-auth-provider', + title: 'Keycloak', + message: 'Sign In using Keycloak', + apiRef: keycloakAuthApiRef, + }, +]; +``` :::note Note - -These steps apply to most if not all the providers, including custom providers, the main -difference between different providers will be the contents of the API factory, the code +These steps apply to most auth providers. The main +difference between providers will be the contents of the API factory, the code in the Auth Provider Factory, the resolver, and the different variables each provider needs in the YAML config or env variables. - ::: [1]: https://backstage.io/docs/auth/identity-resolver -[2]: https://backstage.io/docs/auth/microsoft/provider#create-an-app-registration-on-azure [3]: https://backstage.io/docs/auth/#sign-in-configuration [4]: https://backstage.io/docs/api/utility-apis diff --git a/docs/auth/service-to-service-auth.md b/docs/auth/service-to-service-auth.md index d35aed364d..1423fa92f7 100644 --- a/docs/auth/service-to-service-auth.md +++ b/docs/auth/service-to-service-auth.md @@ -414,9 +414,13 @@ Each entry has one or more of the following fields: ## Adding custom or logic for validation and issuing of tokens -The `pluginTokenHandlerDecoratorServiceRef` can be used to decorate the existing token handler without having to re-implement the entire `AuthService` implementation. +The `pluginTokenHandlerDecoratorServiceRef` and `externalTokenHandlersServiceRef` can be used to extend the existing token handler without having to re-implement the entire `AuthService` implementation. This is particularly useful when you want to add additional logic to the handler, such as logging or metrics or custom token validation. +### PluginTokenHandler decoration + +The `pluginTokenHandlerDecoratorServiceRef` can be used to decorate the default PluginTokenHandler used for create and verify tokens from plugins. + The `PluginTokenHandler` interface has two methods: - `issueToken`: This method is used to issue a token for a plugin. It takes in the `pluginId` and `targetPluginId` as arguments, and an optional `limitedUserToken` object which can be used to issue a token on behalf of another user. The method returns a promise that resolves to an object containing the issued token. @@ -439,3 +443,109 @@ const decoratedPluginTokenHandler = createServiceFactory({ }, }); ``` + +### Adding custom ExternalTokenHandler + +The `externalTokenHandlersServiceRef` can be used to add custom external token handlers to the default implementation. + +Your service factory must return an object with a `type` property that matches the token type in your configuration (e.g., 'custom', 'api-key'). When Backstage encounters tokens of this type, it calls your `initialize` method with all the configuration entries that match this type. Your factory can return either a single token handler or an array of handlers to process and validate these tokens. + +:::note Note + +During token verification, all the token handlers are tested. Consider this when adding many token handlers, as it may impact performance. + +::: + +For example, if we want to add a custom external token handler for the `custom` type: + +our config would look like this: + +```yaml title="in e.g. app-config.production.yaml" +backend: + auth: + externalAccess: + - type: custom + options: + customOptions: additional-value + accessRestrictions: + - plugin: events + - type: custom + options: + customOptions: another-value + accessRestrictions: + - plugin: events +``` + +And we can implement the custom token handler like this: + +```ts +import { + ExternalTokenHandler, + externalTokenHandlersServiceRef, + createExternalTokenHandler, +} from '@backstage/backend-defaults/auth'; +import { createServiceFactory } from '@backstage/backend-plugin-api'; + +const customExternalTokenHandlers = createServiceFactory({ + service: externalTokenHandlersServiceRef, + deps: {}, + async factory() { + return createExternalTokenHandler({ + type: 'custom', + initialize({ options }) { + // Initialize your handler context from config + const customOptions = options.getString('customOptions'); + return { customOptions }; + }, + async verifyToken(token, context) { + // Your custom token validation logic here + // Return undefined if token is invalid + // Return { subject: 'your-subject' } if token is valid + + if (token === 'valid-token') { + return { subject: `custom:${context.customOptions}` }; + } + return undefined; + }, + }); + }, +}); +``` + +The `createExternalTokenHandler` helper simplifies creating external token handlers with the new API: + +- **`type`**: A string identifier for your token handler type that matches the config +- **`initialize`**: Called once for each config entry of this type, receives the config options and returns a context object that will be passed to `verifyToken` +- **`verifyToken`**: Called for each token verification with the token and context, returns the subject if valid or `undefined` if not + +```ts +// Example of a more complex handler with external API call +const apiTokenHandler = createExternalTokenHandler({ + type: 'api-validation', + initialize({ options }) { + const apiBaseUrl = options.getString('apiBaseUrl'); + const apiKey = options.getString('apiKey'); + return { apiBaseUrl, apiKey }; + }, + async verifyToken(token, { apiBaseUrl, apiKey }) { + try { + const response = await fetch(`${apiBaseUrl}/validate-token`, { + method: 'POST', + headers: { + Authorization: `Bearer ${apiKey}`, + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ token }), + }); + + if (response.ok) { + const { userId } = await response.json(); + return { subject: `api:${userId}` }; + } + } catch (error) { + // Log error but don't throw - return undefined for invalid tokens + } + return undefined; + }, +}); +``` diff --git a/docs/backend-system/architecture/03-services.md b/docs/backend-system/architecture/03-services.md index cfcd606d92..022aab202b 100644 --- a/docs/backend-system/architecture/03-services.md +++ b/docs/backend-system/architecture/03-services.md @@ -223,6 +223,35 @@ export const customFooServiceFactory = createServiceFactory({ This allows you to provide more advanced options for the service implementation that couldn't be expressed through static configuration. It also gives users of the service implementation access to other services through dependency injection, which can be useful for their customizations. +## Multiton + +By default the service reference will point to a singleton instance of the service. This mean if a new service factory uses this reference it will override the previous one. This is the most common use-case, but in some cases you may want to have multiple instances of the same service. +For some services, it is desirable to extend the functionality instead of overriding it. For example, some services could have many handlers to address specific events, and you may want to add a new handler instead of overriding the previous one. In this case, you can use the `multiton` option when creating the service reference: + +```ts +// example-service-ref.ts +import { createServiceRef } from '@backstage/backend-plugin-api'; + +export interface FooService { + foo(options: FooOptions): Promise; +} + +export const fooServiceRef = createServiceRef({ + id: 'example.foo', + multiton: true, // this service ref will be an array of instances +}); +``` + +When adding this `serviceRef` as a dependency to a factory, the factory will receive an array of instances instead of a single instance: + +```ts +deps: {fooServices: fooServiceRef}, + factory(fooServices) { + // fooServices is an array of instances + return new Bar(fooServices); + }, +``` + ## Service Factory Options Pattern :::note Note diff --git a/docs/backend-system/building-plugins-and-modules/02-testing.md b/docs/backend-system/building-plugins-and-modules/02-testing.md index 6000c14a45..67fb3d4f22 100644 --- a/docs/backend-system/building-plugins-and-modules/02-testing.md +++ b/docs/backend-system/building-plugins-and-modules/02-testing.md @@ -189,7 +189,7 @@ describe('MyDatabaseClass', () => { // "physical" databases to test against is much costlier than creating the // "logical" databases within them that the individual tests use. const databases = TestDatabases.create({ - ids: ['POSTGRES_17', 'POSTGRES_13', 'SQLITE_3', 'MYSQL_8'], + ids: ['POSTGRES_18', 'POSTGRES_14', 'SQLITE_3', 'MYSQL_8'], }); // Just an example of how to conveniently bundle up the setup code diff --git a/docs/backend-system/core-services/http-auth.md b/docs/backend-system/core-services/http-auth.md index f3ce96fb25..ea667b144c 100644 --- a/docs/backend-system/core-services/http-auth.md +++ b/docs/backend-system/core-services/http-auth.md @@ -104,7 +104,7 @@ or [contribute](https://github.com/backstage/backstage/blob/master/CONTRIBUTING. ### Custom token extraction logic -In some cases, you might want to customize how tokens are extracted from incoming requests. It might be that you want to extract tokens from a different location in the request. To support this you can supply your own slightly modified httpAuth service. The `DefaultHttpAuthService` class is exported from `@backstage/backend-defaults/httpAuth` and it's static `create` method can be used to pass in a custom `getTokenFromRequest` extraction function. +In some cases, you might want to customize how tokens are extracted from incoming requests. It might be that you want to extract tokens from a different location in the request. To support this you can supply your own slightly modified `httpAuth` service. The `DefaultHttpAuthService` class is exported from `@backstage/backend-defaults/httpAuth` and it's static `create` method can be used to pass in a custom `getTokenFromRequest` extraction function. ```ts import { DefaultHttpAuthService } from '@backstage/backend-defaults/httpAuth'; diff --git a/docs/backend-system/core-services/http-router.md b/docs/backend-system/core-services/http-router.md index 5e065152a0..6de394f549 100644 --- a/docs/backend-system/core-services/http-router.md +++ b/docs/backend-system/core-services/http-router.md @@ -134,7 +134,13 @@ import { createAuthIntegrationRouter, createRateLimitMiddleware, } from '@backstage/backend-defaults/httpRouter'; -import { createServiceFactory } from '@backstage/backend-plugin-api'; +import PromiseRouter from 'express-promise-router'; +import { Handler } from 'express'; +import { + createServiceFactory, + coreServices, + HttpRouterServiceAuthPolicy, +} from '@backstage/backend-plugin-api'; const backend = createBackend(); diff --git a/docs/backend-system/core-services/root-http-router.md b/docs/backend-system/core-services/root-http-router.md index 1bda95a990..9c2c60957f 100644 --- a/docs/backend-system/core-services/root-http-router.md +++ b/docs/backend-system/core-services/root-http-router.md @@ -162,7 +162,7 @@ backend.add( Note that requests towards `/api/*` will never be handled by the `routes` handler unless a matching plugin exists, and will instead typically falling through to the `middleware.notFound()` handler. That is the case regardless of whether there is a configured `indexPath` or not. -The root HTTP Router service also allows for configuration of the underlying Node.js HTTP server object. This is useful for modifying settings on the HTTP server itself, such as server [timeout](https://nodejs.org/api/http.html#servertimeout), [keepAliveTimeout](https://nodejs.org/api/http.html#serverkeepalivetimeout), and [headersTimeout](https://nodejs.org/api/http.html#serverheaderstimeout). +The root HTTP Router service also allows for configuration of the underlying Node.js HTTP server object. This is useful for modifying settings on the HTTP server itself, such as server [`timeout`](https://nodejs.org/api/http.html#servertimeout), [`keepAliveTimeout`](https://nodejs.org/api/http.html#serverkeepalivetimeout), and [`headersTimeout`](https://nodejs.org/api/http.html#serverheaderstimeout). A `applyDefaults` helper is also made available to use the default app/router configuration while still enabling custom server configuration diff --git a/docs/backend-system/core-services/root-instance-metadata.md b/docs/backend-system/core-services/root-instance-metadata.md new file mode 100644 index 0000000000..109042f1a5 --- /dev/null +++ b/docs/backend-system/core-services/root-instance-metadata.md @@ -0,0 +1,44 @@ +--- +id: root-instance-metadata +title: Root Instance Metadata Service +sidebar_label: Root Instance Metadata +description: Documentation for the Root Instance Metadata service +--- + +The root instance metadata service provides information about the running Backstage backend instance. Currently, it provides a list of all installed backend plugins. + +:::note Note + +The root instance metadata service only provides information about the specific Backstage instance you're running on. In more complex deployments with multiple Backstage instances, this service will not provide a complete list of all plugins across all instances. + +::: + +## Using the service + +The following example shows how to use the root instance metadata service in your `example` backend plugin to access the list of installed backend plugins. + +```ts +import { + coreServices, + createBackendPlugin, +} from '@backstage/backend-plugin-api'; + +createBackendPlugin({ + pluginId: 'example', + register(env) { + env.registerInit({ + deps: { + instanceMetadata: coreServices.rootInstanceMetadata, + }, + async init({ instanceMetadata }) { + const plugins = instanceMetadata.getInstalledPlugins(); + console.log('Installed plugins:', plugins); + }, + }); + }, +}); +``` + +## Dynamic plugin registration + +The root instance metadata service picks up plugins that are registered at start time through a `backend.start()` call. You need to restart the running backend instance to pick up newly installed plugins. diff --git a/docs/backend-system/core-services/root-logger.md b/docs/backend-system/core-services/root-logger.md index ba2ab05197..d2405bce0a 100644 --- a/docs/backend-system/core-services/root-logger.md +++ b/docs/backend-system/core-services/root-logger.md @@ -52,9 +52,9 @@ backend: type: incomingRequest level: warn - # Ignore logs starting with "Task worker starting", unless they're warnings or errors + # Ignore logs starting with "Registered scheduled task", unless they're warnings or errors - matchers: - message: ['/^Task worker starting/'] + message: ['/^Registered scheduled task/'] level: warn ``` diff --git a/docs/conf/user-interface/icons.md b/docs/conf/user-interface/icons.md new file mode 100644 index 0000000000..26a6f11096 --- /dev/null +++ b/docs/conf/user-interface/icons.md @@ -0,0 +1,124 @@ +--- +id: icons +title: Customizing Icons +sidebar_label: Icons +description: Customizing Icons +--- + +So far you've seen how to create your own theme and add your own logo, in the following sections you'll be shown how to override the existing icons and how to add more icons + +## Custom Icons + +You can also customize the Project's _default_ icons. + +You can change the following [icons](https://github.com/backstage/backstage/blob/master/packages/app-defaults/src/defaults/icons.tsx). + +### Requirements + +- Files in `.svg` format +- React components created for the icons + +### Create React Component + +In your front-end application, locate the `src` folder. We suggest creating the `assets/icons` directory and `customIcons.tsx` file. + +```tsx title="customIcons.tsx" +import { SvgIcon, SvgIconProps } from '@material-ui/core'; + +export const ExampleIcon = (props: SvgIconProps) => ( + + + +); +``` + +### Using the custom icon + +Supply your custom icon in `packages/app/src/App.tsx` + +```tsx title="packages/app/src/App.tsx" +/* highlight-add-next-line */ +import { ExampleIcon } from './assets/icons/CustomIcons' + + +const app = createApp({ + apis, + components: { + {/* ... */} + }, + themes: [ + {/* ... */} + ], + /* highlight-add-start */ + icons: { + github: ExampleIcon, + }, + /* highlight-add-end */ + bindRoutes({ bind }) { + {/* ... */} + } +}) +``` + +## Adding Icons + +You can add more icons, if the [default icons](https://github.com/backstage/backstage/blob/master/packages/app-defaults/src/defaults/icons.tsx) do not fit your needs, so that they can be used in other places like for Links in your entities. For this example we'll be using icons from[Material UI](https://v4.mui.com/components/material-icons/) and specifically the `AlarmIcon`. Here's how to do that: + +1. First you will want to open your `App.tsx` in `/packages/app/src` +2. Then you want to import your icon, add this to the rest of your imports: `import AlarmIcon from '@material-ui/icons/Alarm';` +3. Next you want to add the icon like this to your `createApp`: + + ```tsx title="packages/app/src/App.tsx" + const app = createApp({ + apis: ..., + plugins: ..., + /* highlight-add-start */ + icons: { + alert: AlarmIcon, + }, + /* highlight-add-end */ + themes: ..., + components: ..., + }); + ``` + +4. Now we can reference `alert` for our icon in our entity links like this: + + ```yaml + apiVersion: backstage.io/v1alpha1 + kind: Component + metadata: + name: artist-lookup + description: Artist Lookup + links: + - url: https://example.com/alert + title: Alerts + icon: alert + ``` + + And this is the result: + + ![Example Link with Alert icon](../../assets/getting-started/add-icons-links-example.png) + + Another way you can use these icons is from the `AppContext` like this: + + ```ts + import { useApp } from '@backstage/core-plugin-api'; + + const app = useApp(); + const alertIcon = app.getSystemIcon('alert'); + ``` + + You might want to use this method if you have an icon you want to use in several locations. + +:::note Note + +If the icon is not available as one of the default icons or one you've added then it will fall back to Material UI's `LanguageIcon` + +::: diff --git a/docs/conf/user-interface/index.md b/docs/conf/user-interface/index.md new file mode 100644 index 0000000000..81acfdf720 --- /dev/null +++ b/docs/conf/user-interface/index.md @@ -0,0 +1,621 @@ +--- +id: index +title: Customizing Your App's UI +sidebar_label: Introduction +description: Learn how to customize the look and feel of your Backstage app, including theming and branding options. +--- + +Backstage offers built-in support for both light and dark themes, making it easy to get started with a professional look and feel. But many teams want to go further—tailoring the interface to reflect their organization’s unique brand, identity, and experience. + +This section explores the different ways you can customize the appearance of your Backstage instance. You'll learn how the theming system is structured today, how to work with the two coexisting UI systems, and how to define themes that align with your visual language. + +## Theming architecture overview + +Backstage currently supports two parallel UI systems. The original theming and component model is built on Material UI (MUI), a popular React-based framework. More recently, Backstage introduced Backstage UI (BUI), a custom-designed, CSS-first system developed to meet the platform’s evolving needs. Both systems are supported today, with many parts of the ecosystem still using MUI while new components adopt BUI. + + + +:::info +We recognize that maintaining two separate theming systems is not ideal. Because of the fundamental architectural differences between MUI and Backstage UI, it can be challenging to automate theme updates or know exactly which theme to modify for a given component. Our recommendation is to inspect the component’s code and check its class names: if you see a class name starting with `bui`, you should use the Backstage UI theming approach to style it. +::: + +## Creating custom themes + +During the transition to Backstage UI, you will need to maintain themes in two places: some components and plugins still rely on MUI, while others use Backstage UI. We are working on a plugin that will make help you convert your existing MUI theme into a Backstage UI CSS file you can add to your application. We'll update this page when the plugin is available but for now you can follow progress on this PR [#31140](https://github.com/backstage/backstage/pull/31140). + +```tsx title="packages/app/src/App.tsx" +/* highlight-add-start */ +import { lightTheme, darkTheme } from './themes'; // MUI themes +import './styles.css'; // Backstage UI (BUI) theme +/* highlight-add-end */ + +const app = createApp({ + apis, + components, + /* highlight-add-start */ + themes: [ + { + id: 'light', + title: 'Light theme', + variant: 'light', + icon: , + Provider: ({ children }) => ( + + ), + }, + { + id: 'dark', + title: 'Dark theme', + variant: 'dark', + icon: , + Provider: ({ children }) => ( + + ), + }, + ], + /* highlight-add-end */ +}); +``` + +| Name | Description | +| ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `id` | Each theme has a unique `id` | +| `title` | This will be shown in the settings page to select the right theme. | +| `variant` | This can be either `light` or `dark`. This is also referred to as `mode`. On the `body` of your app we are inserting a data attribute to set the theme based on this value: `data-theme-mode="light"`. | +| `icon` | This will be shown in the settings page as a visual element to complement the title. | +| `Provider` | This is needed to set the legacy theme with MUI only. This will be become redundant later on when we fully replace with BUI but for now you need to have it for MUI to work. BUI is based on CSS and don't rely on any global providers. | + +:::note +Your list of custom themes overrides the default themes. If you still want to use the default themes, they are exported as `themes.light` and `themes.dark` from [`@backstage/theme`](https://www.npmjs.com/package/@backstage/theme). Be sure to provide both `light` and `dark` modes so users can choose their preference. +::: + +## Create a theme for Backstage UI (New) + +Backstage UI is built entirely using CSS. By default we are providing a default theme that include all our core CSS variables and component styles. To start customising Backstage UI to match your brand you need to create a new CSS file and import it directly in `packages/app/src/App.tsx`. All styles declared in this file will override the default styles. As your file grow you can organise it the way you want or even import multiple files. + +Backstage UI is using light by default under `:root` but you can target it more specifically using the data attribute for mode + +```css title="packages/app/src/styles.css" +:root { + /* Use :root to set styles for both light and dark themes */ + .bui-Button { + background-color: #000; + color: #fff; + } +} + +[data-theme-mode='light'] { + /* Light theme specific styles */ +  --bui-bg: #f8f8f8; + --bui-fg-primary: #000; +} + +[data-theme-mode='dark'] { + /* Dark theme specific styles */ +  --bui-bg: #333333; + --bui-fg-primary: #fff; +} +``` + +### CSS variables + +By adjusting just a few theme variables, you can easily transform the look and feel of your Backstage instance to align with your brand identity. All colors are defined using these variables, ensuring they adapt seamlessly to both light and dark modes. + +We recommend starting with a core set of CSS variables to quickly achieve a branded experience. You’ll also find a complete list of available variables below, giving you full flexibility to fine-tune the design to your needs. + +And if you’d like to go even further, you can target specific component class names for advanced customization. + +| Token Name | Description | +| -------------------- | ----------------------------------------------------------------------------------- | +| `--bui-bg` | This is used to define the background color of your app. It will only be used once. | +| `--bui-bg-surface-1` | We ar using this color to sit on top of `--bui-bg` mostly for `Card`, `Dialog`, ... | +| `--bui-bg-surface-2` | This is for content inside elevated components. This colour is less common. | +| `--bui-bg-solid` | This is used for main actions like primary buttons. | +| `--bui-fg-solid` | This is for texts or icons on top of a solid backgrounds. | +| `--bui-fg-primary` | Your primary text or icon colours. | +| `--bui-fg-secondary` | Your secondary text or icon colours. | +| `--bui-fg-link` | Used for links. | +| `--bui-border` | Main borders around surfaces like `Card`, `Dialog`, ... | +| `--bui-font-regular` | The main font of your app. | + +
+ All available CSS variables + +#### Base colors + +These colors are used for special purposes like ring, scrollbar, ... + +| Token Name | Description | +| -------------- | ----------------------------------------------------------------------- | +| `--bui-black` | Pure black color. This one should be the same in light and dark themes. | +| `--bui-white` | Pure white color. This one should be the same in light and dark themes. | +| `--bui-gray-1` | You can use these mostly for backgrounds colors. | +| `--bui-gray-2` | You can use these mostly for backgrounds colors. | +| `--bui-gray-3` | You can use these mostly for backgrounds colors. | +| `--bui-gray-4` | You can use these mostly for backgrounds colors. | +| `--bui-gray-5` | You can use these mostly for backgrounds colors. | +| `--bui-gray-6` | You can use these mostly for backgrounds colors. | +| `--bui-gray-7` | You can use these mostly for backgrounds colors. | +| `--bui-gray-8` | You can use these mostly for backgrounds colors. | + +#### Core background colors + +These colors are used for the background of your application. We are mostly using for now a single elevated background for panels. `--bui-bg` should mostly use as the main background color of your app. + +| Token Name | Description | +| ------------------------- | ------------------------------------------------ | +| `--bui-bg` | The background color of your Backstage instance. | +| `--bui-bg-surface-1` | Use for any panels or elevated surfaces. | +| `--bui-bg-surface-2` | Use for any panels or elevated surfaces. | +| `--bui-bg-solid` | Used for solid background colors. | +| `--bui-bg-solid-hover` | Used for solid background colors when hovered. | +| `--bui-bg-solid-pressed` | Used for solid background colors when pressed. | +| `--bui-bg-solid-disabled` | Used for solid background colors when disabled. | +| `--bui-bg-tint` | Used for tint background colors. | +| `--bui-bg-tint-hover` | Used for tint background colors when hovered. | +| `--bui-bg-tint-focus` | Used for tint background colors when active. | +| `--bui-bg-tint-disabled` | Used for tint background colors when disabled. | +| `--bui-bg-danger` | Used to show errors information. | +| `--bui-bg-warning` | Used to show warnings information. | +| `--bui-bg-success` | Used to show success information. | + +#### Foreground colors + +Foreground colours are meant to work in pair with a background colours. Typically this would work for icons, texts, shapes, ... Use a matching name to know what foreground color to use. These colors are prefixed with `fg` to make it easier to identify. + +| Token Name | Description | +| ------------------------ | ----------------------------------------------------------------- | +| `--bui-fg-primary` | It should be used on top of main background surfaces. | +| `--bui-fg-secondary` | It should be used on top of main background surfaces. | +| `--bui-fg-link` | It should be used on top of main background surfaces. | +| `--bui-fg-link-hover` | It should be used on top of main background surfaces. | +| `--bui-fg-disabled` | It should be used on top of main background surfaces. | +| `--bui-fg-solid` | It should be used on top of solid background colors. | +| `--bui-fg-tint` | It should be used on top of tint background colors. | +| `--bui-fg-tint-disabled` | It should be used on top of tint background colors when disabled. | +| `--bui-fg-danger` | It should be used on top of danger background colors. | +| `--bui-fg-warning` | It should be used on top of warning background colors. | +| `--bui-fg-success` | It should be used on top of success background colors. | + +#### Border colors + +These border colors are mostly meant to be used as borders on top of any components with low contrast to help as a separator with the different background colors. + +| Token Name | Description | +| ----------------------- | --------------------------------------------------- | +| `--bui-border` | It should be used on top of `--bui-bg-surface-1`. | +| `--bui-border-hover` | Used when the component is interactive and hovered. | +| `--bui-border-pressed` | Used when the component is interactive and hovered. | +| `--bui-border-disabled` | Used when the component is disabled. | +| `--bui-border-danger` | It should be used on top of `--bui-bg-danger`. | +| `--bui-border-warning` | It should be used on top of `--bui-bg-warning`. | +| `--bui-border-success` | It should be used on top of `--bui-bg-success`. | + +#### Special colors + +These colors are used for special purposes like ring, scrollbar, ... + +| Token Name | Description | +| ----------------------- | --------------------------------- | +| `--bui-ring` | The color of the ring. | +| `--bui-scrollbar` | The color of the scrollbar. | +| `--bui-scrollbar-thumb` | The color of the scrollbar thumb. | + +#### Font families + +We have two fonts that we use across Backstage UI. The first one is the sans-serif font that we use for the body of the application. The second one is the monospace font that we use for code blocks and tables. + +| Token Name | Description | +| -------------------- | ---------------------------------- | +| `--bui-font-regular` | The sans-serif font for the theme. | +| `--bui-font-mono` | The monospace font for the theme. | + +#### Font weights + +We have two font weights that we use across Backstage UI. Regular or Bold. + +| Token Name | Description | +| --------------------------- | -------------------------------------- | +| `--bui-font-weight-regular` | The regular font weight for the theme. | +| `--bui-font-weight-bold` | The bold font weight for the theme. | + +#### Spacing + +We built a spacing system based on a single value `--bui-space`. This value is used to calculate the spacing for all the components. By default if you would like to increase or decrease the spacing between your components you can do it simply by updating `--bui-space` and it will apply to all spacing values. + +`--bui-space` is not used directly in any components but serve as an easy way to calculate the other values. + +| Token Name | Description | +| ------------- | ----------------------------------------------------------------- | +| `--bui-space` | The base unit for the spacing system. Default value is `0.25rem.` | + +#### Radius + +We use a radius system to make sure that the components have a consistent look and feel. + +| Token Name | Description | +| ------------------- | --------------------------------------------------------- | +| `--bui-radius-1` | The radius of the component. Default value is `0.125rem`. | +| `--bui-radius-2` | The radius of the component. Default value is `0.25rem`. | +| `--bui-radius-3` | The radius of the component. Default value is `0.5rem`. | +| `--bui-radius-4` | The radius of the component. Default value is `0.75rem`. | +| `--bui-radius-5` | The radius of the component. Default value is `1rem`. | +| `--bui-radius-6` | The radius of the component. Default value is `1.25rem`. | +| `--bui-radius-full` | The radius of the component. Default value is `9999px`. | + +
+ +### Component class names + +All Backstage UI components come with a set of CSS classes that you can use to style them. To make it easier to identify the class name you can use, we use a specific structure for the class names. + +![classname-structure](../../assets/user-interface/css-classname-structure.png) + +Every component has a unique prefix `.bui-` followed by the component name. Component props are represented using the `data-` attribute. That way, class names are easily identifiable. + +## Create a theme for MUI (Legacy) + +To customize the appearance of your Backstage app using the legacy MUI theming system, you can define your own theme by extending the built-in light or dark themes. This is done using the createUnifiedTheme utility provided by the [`@backstage/theme`](https://www.npmjs.com/package/@backstage/theme) package. This function allows you to override key aspects of the theme—such as color palette, typography, spacing, and shape—while preserving Backstage’s base configuration and component compatibility. + +The example below shows how to create a new theme based on the default light theme: + +```ts title="packages/app/src/themes.ts" +import { + createBaseThemeOptions, + createUnifiedTheme, + palettes, +} from '@backstage/theme'; + +export const lightTheme = createUnifiedTheme({ + ...createBaseThemeOptions({ + palette: palettes.light, + }), + fontFamily: 'Comic Sans MS', + defaultPageTheme: 'home', +}); + +export const darkTheme = createUnifiedTheme({ + ...createBaseThemeOptions({ + palette: palettes.dark, + }), + fontFamily: 'Comic Sans MS', + defaultPageTheme: 'home', +}); +``` + +You can also create a theme from scratch that matches the `BackstageTheme` type exported by [`@backstage/theme`](https://www.npmjs.com/package/@backstage/theme). See the +[Material UI docs on theming](https://material-ui.com/customization/theming/) for more information about how that can be done. + +
+ Example of a custom MUI theme + +For a more complete example of a custom theme including Backstage and Material UI component overrides, see the [Aperture theme](https://github.com/backstage/demo/blob/master/packages/app/src/theme/aperture.ts) from the [Backstage demo site](https://demo.backstage.io). + +```ts title="packages/app/src/themes.ts" +import { + createBaseThemeOptions, + createUnifiedTheme, + genPageTheme, + palettes, + shapes, +} from '@backstage/theme'; + +export const myTheme = createUnifiedTheme({ + ...createBaseThemeOptions({ + palette: { + ...palettes.light, + primary: { + main: '#343b58', + }, + secondary: { + main: '#565a6e', + }, + error: { + main: '#8c4351', + }, + warning: { + main: '#8f5e15', + }, + info: { + main: '#34548a', + }, + success: { + main: '#485e30', + }, + background: { + default: '#d5d6db', + paper: '#d5d6db', + }, + banner: { + info: '#34548a', + error: '#8c4351', + text: '#343b58', + link: '#565a6e', + }, + errorBackground: '#8c4351', + warningBackground: '#8f5e15', + infoBackground: '#343b58', + navigation: { + background: '#343b58', + indicator: '#8f5e15', + color: '#d5d6db', + selectedColor: '#ffffff', + }, + }, + }), + defaultPageTheme: 'home', + fontFamily: 'Comic Sans MS', + /* below drives the header colors */ + pageTheme: { + home: genPageTheme({ colors: ['#8c4351', '#343b58'], shape: shapes.wave }), + documentation: genPageTheme({ + colors: ['#8c4351', '#343b58'], + shape: shapes.wave2, + }), + tool: genPageTheme({ colors: ['#8c4351', '#343b58'], shape: shapes.round }), + service: genPageTheme({ + colors: ['#8c4351', '#343b58'], + shape: shapes.wave, + }), + website: genPageTheme({ + colors: ['#8c4351', '#343b58'], + shape: shapes.wave, + }), + library: genPageTheme({ + colors: ['#8c4351', '#343b58'], + shape: shapes.wave, + }), + other: genPageTheme({ colors: ['#8c4351', '#343b58'], shape: shapes.wave }), + app: genPageTheme({ colors: ['#8c4351', '#343b58'], shape: shapes.wave }), + apis: genPageTheme({ colors: ['#8c4351', '#343b58'], shape: shapes.wave }), + }, +}); +``` + +
+ +
+ Custom Typography + +When creating a custom theme you can also customize various aspects of the default typography, here's an example using simplified theme: + +```ts title="packages/app/src/theme/myTheme.ts" +import { + createBaseThemeOptions, + createUnifiedTheme, + palettes, +} from '@backstage/theme'; + +export const myTheme = createUnifiedTheme({ + ...createBaseThemeOptions({ + palette: palettes.light, + typography: { + htmlFontSize: 16, + fontFamily: 'Arial, sans-serif', + h1: { + fontSize: 54, + fontWeight: 700, + marginBottom: 10, + }, + h2: { + fontSize: 40, + fontWeight: 700, + marginBottom: 8, + }, + h3: { + fontSize: 32, + fontWeight: 700, + marginBottom: 6, + }, + h4: { + fontWeight: 700, + fontSize: 28, + marginBottom: 6, + }, + h5: { + fontWeight: 700, + fontSize: 24, + marginBottom: 4, + }, + h6: { + fontWeight: 700, + fontSize: 20, + marginBottom: 2, + }, + }, + defaultPageTheme: 'home', + }), +}); +``` + +If you wanted to only override a sub-set of the typography setting, for example just `h1` then you would do this: + +```ts title="packages/app/src/theme/myTheme.ts" +import { + createBaseThemeOptions, + createUnifiedTheme, + defaultTypography, + palettes, +} from '@backstage/theme'; + +export const myTheme = createUnifiedTheme({ + ...createBaseThemeOptions({ + palette: palettes.light, + typography: { + ...defaultTypography, + htmlFontSize: 16, + fontFamily: 'Roboto, sans-serif', + h1: { + fontSize: 72, + fontWeight: 700, + marginBottom: 10, + }, + }, + defaultPageTheme: 'home', + }), +}); +``` + +
+ +
+ Custom Fonts + +To add custom fonts, you first need to store the font so that it can be imported. We suggest creating the `assets/fonts` directory in your front-end application `src` folder. + +You can then declare the font style following the `@font-face` syntax from [Material UI Typography](https://mui.com/material-ui/customization/typography/). + +After that you can then utilize the `styleOverrides` of `MuiCssBaseline` under components to add a font to the `@font-face` array. + +```ts title="packages/app/src/theme/myTheme.ts" +import MyCustomFont from '../assets/fonts/My-Custom-Font.woff2'; + +const myCustomFont = { + fontFamily: 'My-Custom-Font', + fontStyle: 'normal', + fontDisplay: 'swap', + fontWeight: 300, + src: ` + local('My-Custom-Font'), + url(${MyCustomFont}) format('woff2'), + `, +}; + +export const myTheme = createUnifiedTheme({ + fontFamily: 'My-Custom-Font', + palette: palettes.light, + components: { + MuiCssBaseline: { + styleOverrides: { + '@font-face': [myCustomFont], + }, + }, + }, +}); +``` + +If you want to utilize different or multiple fonts, then you can set the top level `fontFamily` to what you want for your body, and then override `fontFamily` in `typography` to control fonts for various headings. + +```ts title="packages/app/src/theme/myTheme.ts" +import MyCustomFont from '../assets/fonts/My-Custom-Font.woff2'; +import myAwesomeFont from '../assets/fonts/My-Awesome-Font.woff2'; + +const myCustomFont = { + fontFamily: 'My-Custom-Font', + fontStyle: 'normal', + fontDisplay: 'swap', + fontWeight: 300, + src: ` + local('My-Custom-Font'), + url(${MyCustomFont}) format('woff2'), + `, +}; + +const myAwesomeFont = { + fontFamily: 'My-Awesome-Font', + fontStyle: 'normal', + fontDisplay: 'swap', + fontWeight: 300, + src: ` + local('My-Awesome-Font'), + url(${myAwesomeFont}) format('woff2'), + `, +}; + +export const myTheme = createUnifiedTheme({ + fontFamily: 'My-Custom-Font', + components: { + MuiCssBaseline: { + styleOverrides: { + '@font-face': [myCustomFont, myAwesomeFont], + }, + }, + }, + ...createBaseThemeOptions({ + palette: palettes.light, + typography: { + ...defaultTypography, + htmlFontSize: 16, + fontFamily: 'My-Custom-Font', + h1: { + fontSize: 72, + fontWeight: 700, + marginBottom: 10, + fontFamily: 'My-Awesome-Font', + }, + }, + defaultPageTheme: 'home', + }), +}); +``` + +
+ +
+ Overriding Backstage and Material UI components styles + +When creating a custom theme you would be applying different values to component's CSS rules that use the theme object. For example, a Backstage component's styles might look like this: + +```tsx +const useStyles = makeStyles( + theme => ({ + header: { + padding: theme.spacing(3), + boxShadow: '0 0 8px 3px rgba(20, 20, 20, 0.3)', + backgroundImage: theme.page.backgroundImage, + }, + }), + { name: 'BackstageHeader' }, +); +``` + +Notice how the `padding` is getting its value from `theme.spacing`, that means that setting a value for spacing in your custom theme would affect this component padding property and the same goes for `backgroundImage` which uses `theme.page.backgroundImage`. However, the `boxShadow` property doesn't reference any value from the theme, that means that creating a custom theme wouldn't be enough to alter the `box-shadow` property or to add css rules that aren't already defined like a margin. For these cases you should also create an override. + +Here's how you would do that: + +```ts title="packages/app/src/theme/myTheme.ts" +import { + createBaseThemeOptions, + createUnifiedTheme, + palettes, +} from '@backstage/theme'; + +export const myTheme = createUnifiedTheme({ + ...createBaseThemeOptions({ + palette: palettes.light, + }), + fontFamily: 'Comic Sans MS', + defaultPageTheme: 'home', + components: { + BackstageHeader: { + styleOverrides: { + header: ({ theme }) => ({ + width: 'auto', + margin: '20px', + boxShadow: 'none', + borderBottom: `4px solid ${theme.palette.primary.main}`, + }), + }, + }, + }, +}); +``` + +
diff --git a/docs/conf/user-interface/logo.md b/docs/conf/user-interface/logo.md new file mode 100644 index 0000000000..35789d6330 --- /dev/null +++ b/docs/conf/user-interface/logo.md @@ -0,0 +1,25 @@ +--- +id: logo +title: Customizing Your Logo +sidebar_label: Logo +description: Learn how to customize your logo. +--- + +In addition to a custom theme, you can also customize the logo displayed at the far top left of the site. + +In your frontend app, locate `src/components/Root/` folder. You'll find two components: + +- `LogoFull.tsx` - A larger logo used when the Sidebar navigation is opened. +- `LogoIcon.tsx` - A smaller logo used when the Sidebar navigation is closed. + +To replace the images, you can simply replace the relevant code in those components with raw SVG definitions. + +You can also use another web image format such as PNG by importing it. To do this, place your new image into a new subdirectory such as `src/components/Root/logo/my-company-logo.png`, and then add this code: + +```tsx +import MyCustomLogoFull from './logo/my-company-logo.png'; + +const LogoFull = () => { + return ; +}; +``` diff --git a/docs/conf/user-interface/sidebar.md b/docs/conf/user-interface/sidebar.md new file mode 100644 index 0000000000..f66e6bd106 --- /dev/null +++ b/docs/conf/user-interface/sidebar.md @@ -0,0 +1,93 @@ +--- +id: sidebar +title: Customizing Your Sidebar +sidebar_label: Sidebar +description: Learn how to customize the look and feel of your Sidebar. +--- + +As you've seen there are many ways that you can customize your Backstage app. The following section will show you how you can customize the sidebar. + +## Sidebar Sub-menu + +For this example we'll show you how you can expand the sidebar with a sub-menu: + +1. Open the `Root.tsx` file located in `packages/app/src/components/Root` as this is where the sidebar code lives +2. Then we want to add the following import for `useApp`: + + ```tsx title="packages/app/src/components/Root/Root.tsx" + import { useApp } from '@backstage/core-plugin-api'; + ``` + +3. Then update the `@backstage/core-components` import like this: + + ```tsx title="packages/app/src/components/Root/Root.tsx" + import { + Sidebar, + sidebarConfig, + SidebarDivider, + SidebarGroup, + SidebarItem, + SidebarPage, + SidebarScrollWrapper, + SidebarSpace, + useSidebarOpenState, + Link, + /* highlight-add-start */ + GroupIcon, + SidebarSubmenu, + SidebarSubmenuItem, + /* highlight-add-end */ + } from '@backstage/core-components'; + ``` + +4. Finally replace `` with this: + + ```tsx title="packages/app/src/components/Root/Root.tsx" + + + + + + + + + + + + + + ``` + +When you startup your Backstage app and hover over the Home option on the sidebar you'll now see a nice sub-menu appear with links to the various Kinds in your Catalog. It would look like this: + +![Sidebar sub-menu example](./../../assets/getting-started/sidebar-submenu-example.png) + +You can see more ways to use this in the [Storybook Sidebar examples](https://backstage.io/storybook/?path=/story/layout-sidebar--sample-scalable-sidebar) diff --git a/docs/dls/component-design-guidelines.md b/docs/dls/component-design-guidelines.md index 35e2f987d2..fddffd9fd9 100644 --- a/docs/dls/component-design-guidelines.md +++ b/docs/dls/component-design-guidelines.md @@ -103,6 +103,4 @@ accessibility. [7]: https://v4.mui.com/components/cards/ [8]: https://v4.mui.com/customization/palette/#default-values [9]: https://v4.mui.com/customization/typography/ -[10]: https://backstage.io/docs/getting-started/app-custom-theme -[11]: https://backstage.io/docs/getting-started/app-custom-theme#overriding-backstage-and-material-ui-components-styles -[12]: https://v4.mui.com/customization/default-theme/#explore +[10]: https://backstage.io/docs/conf/user-interface diff --git a/docs/dls/design.md b/docs/dls/design.md index 7e2ae90528..e988d1032f 100644 --- a/docs/dls/design.md +++ b/docs/dls/design.md @@ -127,7 +127,7 @@ your own plugin for Backstage. **[Discord](https://discord.gg/backstage-687207715902193673)** - all design questions should be directed to the _#design_ channel. -**[Customize Backstage's look and feel](https://backstage.io/docs/getting-started/app-custom-theme)** - +**[Customizing Your App's UI](https://backstage.io/docs/conf/user-interface)** - How to customize the look and feel of your Backstage instance by extending the theme. diff --git a/docs/features/kubernetes/configuration.md b/docs/features/kubernetes/configuration.md index 830027f53b..6a3f3760bf 100644 --- a/docs/features/kubernetes/configuration.md +++ b/docs/features/kubernetes/configuration.md @@ -441,7 +441,7 @@ Base64-encoded certificate authority bundle in PEM format. The Kubernetes client will verify that the TLS certificate presented by the API server is signed by this CA. -This value could be obtained via inspecting the kubeconfig file (usually +This value could be obtained via inspecting the `kubeconfig` file (usually at `~/.kube/config`) under `clusters[*].cluster.certificate-authority-data`. For GKE, execute the following command to obtain the value @@ -619,7 +619,7 @@ The custom resource's group. #### `customResources.\*.apiVersion` -The custom resource's apiVersion. +The custom resource's `apiVersion`. #### `customResources.\*.plural` diff --git a/docs/features/search/declarative-integration.md b/docs/features/search/declarative-integration.md index b8f1c6ce5c..74d16d7c57 100644 --- a/docs/features/search/declarative-integration.md +++ b/docs/features/search/declarative-integration.md @@ -161,13 +161,13 @@ The `createSearchResultItemExtension` function returns a Backstage's extension r In this object, you can see exactly what will happen once the custom extension is installed: -- **[1] $$type**: declares that the object represents an extension; -- **[2] id**: Is a unique identification for the extension, the `plugin.search.result.item.techdocs` is the key used to configure the extension in the `app-config.yaml` file; -- **[3] at**: It represents the extension attachment point, so the value `plugin.search.page/items` says that the `TechDocs`'s search result item output will be injected as input on the "items" attachment expected by the search page extension; -- **[4] inputs**: in this case is an empty object because this extension doesn't expect inputs; -- **[5] output**: Object representing the artifact produced by the `TechDocs` result item extension, on the example, it is a react component reference; -- **[6] configSchema**: represents the `TechDocs` search result item configuration definition, this is the same schema that adopters will use for customizing the extension via `app-config.yaml` file; -- **[7] disable**: Says that the result item extension will be enable by default when the `TechDocs` plugin is installed in the app. +- **[1] `$$type`**: declares that the object represents an extension; +- **[2] `id`**: Is a unique identification for the extension, the `plugin.search.result.item.techdocs` is the key used to configure the extension in the `app-config.yaml` file; +- **[3] `at`**: It represents the extension attachment point, so the value `plugin.search.page/items` says that the `TechDocs`'s search result item output will be injected as input on the "items" attachment expected by the search page extension; +- **[4] `inputs`**: in this case is an empty object because this extension doesn't expect inputs; +- **[5] `output`**: Object representing the artifact produced by the `TechDocs` result item extension, on the example, it is a react component reference; +- **[6] `configSchema`**: represents the `TechDocs` search result item configuration definition, this is the same schema that adopters will use for customizing the extension via `app-config.yaml` file; +- **[7] `disable`**: Says that the result item extension will be enable by default when the `TechDocs` plugin is installed in the app. To complete the development cycle for creating a custom search result item extension, we should provide the extension via `TechDocs` plugin: diff --git a/docs/features/search/how-to-guides.md b/docs/features/search/how-to-guides.md index 7055db1138..f0823ac47e 100644 --- a/docs/features/search/how-to-guides.md +++ b/docs/features/search/how-to-guides.md @@ -116,7 +116,7 @@ indexBuilder.addCollator({ The default highlighting styling for matched terms in search results is your browsers default styles for the `` HTML tag. If you want to customize how highlighted terms look you can follow Backstage's guide on how to -[Customize the look-and-feel of your App](https://backstage.io/docs/getting-started/app-custom-theme) +[Customizing Your App's UI](https://backstage.io/docs/conf/user-interface) to create an override with your preferred styling. For example, using the new MUI V4+V5 unified theming method, the following will result diff --git a/docs/features/software-catalog/api.md b/docs/features/software-catalog/api.md index 2a431d337b..96420cd37e 100644 --- a/docs/features/software-catalog/api.md +++ b/docs/features/software-catalog/api.md @@ -175,7 +175,7 @@ Some more real world usable examples: #### Ordering -By default the entities are returned ordered by their internal uid. You can +By default the entities are returned ordered by their internal `uid`. You can customize the `orderField` query parameters to affect that ordering. For example, to return entities by their name: diff --git a/docs/features/software-catalog/catalog-customization.md b/docs/features/software-catalog/catalog-customization.md index 1ef865039f..a828e6591a 100644 --- a/docs/features/software-catalog/catalog-customization.md +++ b/docs/features/software-catalog/catalog-customization.md @@ -571,10 +571,8 @@ Finally, entity predicates also support value operators that can be used in plac ```json { "filter": { - { - "kind": "component", - "spec.type": { "$in": ["service", "website"] } - }, + "kind": "component", + "spec.type": { "$in": ["service", "website"] } } } ``` @@ -695,6 +693,6 @@ filter: relations: $contains: type: ownedBy - target: + targetRef: $in: [group:default/admins, group:default/viewers] ``` diff --git a/docs/features/software-catalog/extending-the-model--old.md b/docs/features/software-catalog/extending-the-model--old.md index 00a6688352..c39561d11b 100644 --- a/docs/features/software-catalog/extending-the-model--old.md +++ b/docs/features/software-catalog/extending-the-model--old.md @@ -28,23 +28,23 @@ Backstage comes with a number of catalog concepts out of the box: We'll list different possibilities for extending this below. -## Adding a New apiVersion of an Existing Kind +## Adding a New `apiVersion` of an Existing Kind Example intents: > "I want to evolve this core kind, tweaking the semantics a bit so I will bump -> the apiVersion a step" +> the `apiVersion` a step" > "This core kind is a decent fit but we want to evolve it at will so we'll move -> it to our own company's apiVersion space and use that instead of +> it to our own company's `apiVersion` space and use that instead of > `backstage.io`." -The `backstage.io` apiVersion space is reserved for use by the Backstage +The `backstage.io` `apiVersion` space is reserved for use by the Backstage maintainers. Please do not change or add versions within that space. -If you add an [apiVersion](descriptor-format.md#apiversion-and-kind-required) +If you add an [`apiVersion`](descriptor-format.md#apiversion-and-kind-required) space of your own, you are effectively branching out from the underlying kind -and making your own. An entity kind is identified by the apiVersion + kind pair, +and making your own. An entity kind is identified by the `apiVersion` + `kind` pair, so even though the resulting entity may be similar to the core one, there will be no guarantees that plugins will be able to parse or understand its data. See below about adding a new kind. @@ -57,7 +57,7 @@ Example intents: > thing that is a poor fit for either of the builtins." > "This core kind is a decent fit but we want to evolve it at will so we'll move -> it to our own company's apiVersion space and use that instead of +> it to our own company's `apiVersion` space and use that instead of > `backstage.io`." A [kind](descriptor-format.md#apiversion-and-kind-required) is an overarching @@ -67,7 +67,7 @@ variety of needs that one may want to model in Backstage. The primary ambition is to map things to these kinds, but sometimes you may want or need to extend beyond them. -Introducing a new apiVersion is basically the same as adding a new kind. Bear in +Introducing a new `apiVersion` is basically the same as adding a new kind. Bear in mind that most plugins will be compiled against the builtin `@backstage/catalog-model` package and have expectations that kinds align with that. diff --git a/docs/features/software-catalog/extending-the-model.md b/docs/features/software-catalog/extending-the-model.md index 5d51a69e6b..7300f4f969 100644 --- a/docs/features/software-catalog/extending-the-model.md +++ b/docs/features/software-catalog/extending-the-model.md @@ -28,23 +28,23 @@ Backstage comes with a number of catalog concepts out of the box: We'll list different possibilities for extending this below. -## Adding a New apiVersion of an Existing Kind +## Adding a New `apiVersion` of an Existing Kind Example intents: > "I want to evolve this core kind, tweaking the semantics a bit so I will bump -> the apiVersion a step" +> the `apiVersion` a step" > "This core kind is a decent fit but we want to evolve it at will so we'll move -> it to our own company's apiVersion space and use that instead of +> it to our own company's `apiVersion` space and use that instead of > `backstage.io`." -The `backstage.io` apiVersion space is reserved for use by the Backstage +The `backstage.io` `apiVersion` space is reserved for use by the Backstage maintainers. Please do not change or add versions within that space. -If you add an [apiVersion](descriptor-format.md#apiversion-and-kind-required) +If you add an [`apiVersion`](descriptor-format.md#apiversion-and-kind-required) space of your own, you are effectively branching out from the underlying kind -and making your own. An entity kind is identified by the apiVersion + kind pair, +and making your own. An entity kind is identified by the `apiVersion` + `kind` pair, so even though the resulting entity may be similar to the core one, there will be no guarantees that plugins will be able to parse or understand its data. See below about adding a new kind. @@ -57,7 +57,7 @@ Example intents: > thing that is a poor fit for either of the builtins." > "This core kind is a decent fit but we want to evolve it at will so we'll move -> it to our own company's apiVersion space and use that instead of +> it to our own company's `apiVersion` space and use that instead of > `backstage.io`." A [kind](descriptor-format.md#apiversion-and-kind-required) is an overarching @@ -67,7 +67,7 @@ variety of needs that one may want to model in Backstage. The primary ambition is to map things to these kinds, but sometimes you may want or need to extend beyond them. -Introducing a new apiVersion is basically the same as adding a new kind. Bear in +Introducing a new `apiVersion` is basically the same as adding a new kind. Bear in mind that most plugins will be compiled against the builtin `@backstage/catalog-model` package and have expectations that kinds align with that. diff --git a/docs/features/software-catalog/external-integrations.md b/docs/features/software-catalog/external-integrations.md index 0aef4565d9..298c686bea 100644 --- a/docs/features/software-catalog/external-integrations.md +++ b/docs/features/software-catalog/external-integrations.md @@ -775,7 +775,7 @@ sent. Luckily many external systems provide ETag support to check for changes which usually doesn't count towards the quota and saves resources both internally and externally. -The catalog has built in support for leveraging ETags when refreshing external +The catalog has built in support for leveraging `ETag`s when refreshing external locations in GitHub. This example aims to demonstrate how to add the same behavior for `system-x` that we implemented earlier. diff --git a/docs/features/software-catalog/index.md b/docs/features/software-catalog/index.md index 1f374385a4..53afb5205e 100644 --- a/docs/features/software-catalog/index.md +++ b/docs/features/software-catalog/index.md @@ -126,7 +126,7 @@ _starring_ of components: The software catalog is a great way to organize the infrastructure tools you use to manage the software. This is how Backstage creates one developer portal for all your tools. Rather than asking teams to jump between different -infrastructure UIs (and incurring additional cognitive overhead each time they +infrastructure user interfaces (and incurring additional cognitive overhead each time they make a context switch), most of these tools can be organized around the entities in the catalog. diff --git a/docs/features/software-catalog/life-of-an-entity.md b/docs/features/software-catalog/life-of-an-entity.md index 628b083ed2..c16fb8d523 100644 --- a/docs/features/software-catalog/life-of-an-entity.md +++ b/docs/features/software-catalog/life-of-an-entity.md @@ -133,7 +133,7 @@ the order of registration depends on the order in which the modules are loaded by the framework. It's possible to customize the order of the processors by modifying the -`catalog.processors..priority` configuration option. +`catalog.processorOptions..priority` configuration option. The default priority is `20`, and lower value means that the processor runs earlier. ::: diff --git a/docs/frontend-system/architecture/20-extensions.md b/docs/frontend-system/architecture/20-extensions.md index 24a5f24e12..a6d00a3131 100644 --- a/docs/frontend-system/architecture/20-extensions.md +++ b/docs/frontend-system/architecture/20-extensions.md @@ -352,3 +352,69 @@ const extension = createExtension({ }, }); ``` + +## Relative attachment points + +When creating an extension or an [extension blueprint](./23-extension-blueprints.md) you can specify an attachment point that is relative to the current plugin. This is particularly useful for groups of blueprints that are part of a common hierarchy, with extensions from one blueprint attaching to extensions from the other blueprint. For example, the following pair of extension definitions could be installed multiple times in different plugins, each creating their own hierarchy: + +```tsx +// Parent extension with a fixed attachment point +const parentExtension = createExtension({ + kind: 'section', + attachTo: [{ id: 'app/some-fixed-extension', input: 'children' }], + inputs: { + content: createExtensionInput([coreExtensionData.reactElement], { + singleton: true, + }), + }, + output: [coreExtensionData.reactElement], + factory({ inputs }) { + return [ + coreExtensionData.reactElement( +
+

Section Title

+ {inputs.content.get(coreExtensionData.reactElement)} +
, + ), + ]; + }, +}); + +// Child extension with a relative attachment point +const childExtension = createExtension({ + kind: 'section-content', + attachTo: [{ relative: { kind: 'section' }, input: 'content' }], + output: [coreExtensionData.reactElement], + factory() { + return [coreExtensionData.reactElement(

Section Content

)]; + }, +}); +``` + +## Extension input references + +Building on the relative attachment point concept, you can also reference extension inputs directly via the `inputs` property of an extension definition. This provides a more convenient and type-safe way to attach child extensions, especially when using blueprints that provide a nested hierarchy of extensions. + +Extension inputs references are always relative, this means that they can only be used for referencing extensions within the same plugin. + +Each extension definition exposes an `inputs` property that contains references to all of its defined inputs. These references can be passed directly to the `attachTo` option when creating child extensions: + +```tsx +const parent = createExtension({ + inputs: { + children: createExtensionInput([coreExtensionData.reactElement]), + }, + // other options... +}); + +// Create a child extension that attaches to the parent's input +const child = createExtension({ + attachTo: page.inputs.children, // Direct reference to the input + output: [coreExtensionData.reactElement], // Outputs are verified against the parent input + // other options... +}); +``` + +These references are a type-safe way to attach child extensions, it both ensures that the parent input is present, as well as the child providing the required data for the parent. + +Under the hood, input references are resolved in the same way as relative attachment points, using the extension's kind, namespace, and name to construct the final attachment target. diff --git a/docs/frontend-system/building-apps/01-index.md b/docs/frontend-system/building-apps/01-index.md index d11b6efba2..0c96bdf15d 100644 --- a/docs/frontend-system/building-apps/01-index.md +++ b/docs/frontend-system/building-apps/01-index.md @@ -35,10 +35,12 @@ This is how to create a minimal app: import ReactDOM from 'react-dom/client'; import { createApp } from '@backstage/frontend-defaults'; import catalogPlugin from '@backstage/plugin-catalog/alpha'; +import '@backstage/ui/css/styles.css'; // Create your app instance const app = createApp({ - // Features such as plugins can be installed explicitly, but we will explore other options later on + // Custom features such as plugins can be installed explicitly, but they are usually + // auto-discovered, unless `app.packages` is customized in `app-config.yaml`. features: [catalogPlugin], }); diff --git a/docs/frontend-system/building-apps/08-migrating.md b/docs/frontend-system/building-apps/08-migrating.md index a2ef37b5e3..f57c22db96 100644 --- a/docs/frontend-system/building-apps/08-migrating.md +++ b/docs/frontend-system/building-apps/08-migrating.md @@ -979,6 +979,7 @@ When creating a new Backstage app with `create-app` and using the `--next` flag "@backstage/cli/templates/plugin-common-library", "@backstage/cli/templates/web-library", "@backstage/cli/templates/node-library", + "@backstage/cli/templates/catalog-provider-module", "@backstage/cli/templates/scaffolder-backend-module" ] } diff --git a/docs/frontend-system/building-plugins/04-built-in-data-refs.md b/docs/frontend-system/building-plugins/04-built-in-data-refs.md index b69e4cb3f4..c01520d7bd 100644 --- a/docs/frontend-system/building-plugins/04-built-in-data-refs.md +++ b/docs/frontend-system/building-plugins/04-built-in-data-refs.md @@ -34,6 +34,14 @@ const examplePage = createExtension({ }); ``` +### `title` + +| id | type | +| :----------: | :------: | +| `core.title` | `string` | + +The `title` data reference can be used for defining the extension input/output of string titles. + ### `routePath` | id | type | diff --git a/docs/getting-started/index.md b/docs/getting-started/index.md index 252884dc54..876af4f0a5 100644 --- a/docs/getting-started/index.md +++ b/docs/getting-started/index.md @@ -157,7 +157,7 @@ Choose the correct next steps for your user role, if you're likely to be deployi - Configuring Backstage - [Adding plugins](./configure-app-with-plugins.md) - - [Customizing the theme](./app-custom-theme.md) + - [Customizing Your App's UI](../conf/user-interface/index.md) - [Populating the homepage](./homepage.md) ### Developer diff --git a/docs/getting-started/app-custom-theme.md b/docs/golden-path/create-app/customize-theme.md similarity index 97% rename from docs/getting-started/app-custom-theme.md rename to docs/golden-path/create-app/customize-theme.md index 38f2fd4c98..a79507de59 100644 --- a/docs/getting-started/app-custom-theme.md +++ b/docs/golden-path/create-app/customize-theme.md @@ -1,7 +1,7 @@ --- -id: app-custom-theme -title: Customize the look-and-feel of your App -description: Documentation on Customizing look and feel of the App +id: custom-theme +title: 005 - Customize your App's theme +description: Documentation on customizing the look and feel of your Backstage app. --- Backstage ships with a default theme with a light and dark mode variant. The themes are provided as a part of the [`@backstage/theme`](https://www.npmjs.com/package/@backstage/theme) package, which also includes utilities for customizing the default theme, or creating completely new themes. @@ -435,7 +435,7 @@ Supply your custom icon in `packages/app/src/App.tsx` ```tsx title="packages/app/src/App.tsx" /* highlight-add-next-line */ -import { ExampleIcon } from './assets/icons/CustomIcons' +import { ExampleIcon } from './assets/customIcons' const app = createApp({ @@ -495,7 +495,7 @@ You can add more icons, if the [default icons](https://github.com/backstage/back And this is the result: - ![Example Link with Alert icon](../assets/getting-started/add-icons-links-example.png) + ![Example Link with Alert icon](../../assets/getting-started/add-icons-links-example.png) Another way you can use these icons is from the `AppContext` like this: @@ -599,15 +599,15 @@ For this example we'll show you how you can expand the sidebar with a sub-menu: When you startup your Backstage app and hover over the Home option on the sidebar you'll now see a nice sub-menu appear with links to the various Kinds in your Catalog. It would look like this: -![Sidebar sub-menu example](./../assets/getting-started/sidebar-submenu-example.png) +![Sidebar sub-menu example](../../assets/getting-started/sidebar-submenu-example.png) You can see more ways to use this in the [Storybook Sidebar examples](https://backstage.io/storybook/?path=/story/layout-sidebar--sample-scalable-sidebar) ## Custom Homepage In addition to a custom theme, a custom logo, you can also customize the -homepage of your app. Read the full guide on the [next page](homepage.md). +homepage of your app. Read the full guide on the [next page](../../getting-started/homepage.md). ## Migrating to Material UI v5 -We now support Material UI v5 in Backstage. Check out our [migration guide](../tutorials/migrate-to-mui5.md) to get started. +We now support Material UI v5 in Backstage. Check out our [migration guide](../../tutorials/migrate-to-mui5.md) to get started. diff --git a/docs/golden-path/create-app/index.md b/docs/golden-path/create-app/index.md new file mode 100644 index 0000000000..ebd736594d --- /dev/null +++ b/docs/golden-path/create-app/index.md @@ -0,0 +1,16 @@ +--- +id: index +title: 'Creating your first Backstage app' +--- + +### Prerequisites + +None! + +### What should I get out of this guide? + +This guide is the first of 4 Golden Paths that will walk you through everything you need to start working with Backstage. We'll touch on how to get started and spin up a new app, how to write plugins, how to deploy your app to production and how to drive adoption for your new portal. Even if you're non-technical, you can skip forward to the `adoption` Golden Path and learn about how to help your team's new developer portal succeed. + +### Structure + +As mentioned above, this is the first of 4 Golden Paths - you should make sure this guide is 100% complete before continuing on to the other Golden Paths (`adoption` is the exception). We'll start by spinning up a new app for you, walking through what we just created and what you can do after you have a working app. diff --git a/docs/golden-path/create-app/installing-plugins.md b/docs/golden-path/create-app/installing-plugins.md new file mode 100644 index 0000000000..ab1e1a68c5 --- /dev/null +++ b/docs/golden-path/create-app/installing-plugins.md @@ -0,0 +1,69 @@ +--- +id: installing-plugins +sidebar_label: 003 - Installing plugins +title: 003 - Installing plugins +--- + +Now that you have a working Backstage app, let's walk through the most valuable part of the Backstage ecosystem - plugins! + +## What is a Backstage plugin? + +A Backstage plugin usually consists of frontend and backend functionality. Some examples of Backstage plugins are our Software Catalog, Search, and Software Templates plugins! Each plugin provides a series of well-contained focused features, for example - the Software Catalog contains an entity ingestion engine, an optimized query layer for fetching entity information and a series of UI elements that provide list and detail functionality for entities. Some plugins allow modules which supplement existing plugin-level functionality, customizing it for specific use cases - a good example here are catalog processor modules which allow for ingesting data from common sources into the catalog. + +:::note Backstage Plugin Naming + +The `backstage-cli new` command scaffolds plugins automatically with the expected naming conventions. We describe the naming conventions below for users who are installing external plugins. + +::: + +You'll generally have multiple packages that combine into a single "plugin". The common naming standard (detailed in [ADR-11](../../architecture-decisions/adr011-plugin-package-structure.md)) is demonstrated below for plugin `x`: + +> `x`: Primary frontend entrypoint, contains frontend-only code for the plugin. + +> `x-backend`: Primary backend entrypoint, contains backend-only code. + +> `x-backend-module-y`: Optional backend module `y` for plugin `x`, contains backend-only code. + +> `x-node`: Shared utilities for consumers of backend plugin `x`, should _NOT_ be used on the frontend. + +> `x-react`: Shared utilities for consumers of frontend plugin `x`, should _NOT_ be used on the backend. + +> `x-common`: Shared utilities for consumers of plugin `x`, can be used across backend and frontend. + +Not all plugins need all of those packages, we recommend starting with just a `x` and `x-backend` package and expanding from there. + +## How do I install a plugin? + +As mentioned above, there's 2 parts to installing a plugin - the frontend and the backend. It's recommended to start with installing the backend plugin to ensure your frontend doesn't run into any weird errors. + +In both cases, you'll want to find the plugin's installation documentation. For most plugins, this is available through that plugin's `README.md` file. For example, the Software Catalog plugin's installation instructions are available through their [backend plugin README](https://github.com/backstage/backstage/blob/850ad502eafc356d940e4f1ce6d32951548bb257/plugins/catalog-backend/README.md#L1) and [frontend plugin README](https://github.com/backstage/backstage/blob/850ad502eafc356d940e4f1ce6d32951548bb257/plugins/catalog/README.md#L1). + +### Installing a Backend Plugin + +Generally, installing a backend plugin is really easy - you just add a + +``` +backend.import(`@scope/package`) +``` + +to your `packages/backend/src/index.ts` file alongside the other entries. Saving the file will trigger a hot reload and just like that your new plugin is available and usable. For advanced cases, there may be required config for the plugin that you'll have to set. That config will (or should) be documented by the plugin in their `README`. + +You may also need to add backend modules to provide the additional functionality in the plugin that you're looking for. Backend modules are further extensions to backend code that can provide tailored functionality, good examples are catalog processor modules that add support for Github, LDAP and AWS software entities. Modules install the exact same way as backend plugins. Installing a module may also require additional configuration, which should also be documented in the plugin's `README`. + +### Installing a Frontend Plugin + +Frontend plugins have multiple entrypoints, you should follow the plugin's documentation for how to install it. + +The New Frontend System vastly simplifies this! Keep your eyes peeled for updates. + +## Finding plugins + +The open source community already has a host of plugins that solve many common asks - we recommend you look through [the plugin directory](https://backstage.io/plugins) before you go about creating your own! + +You can find other community maintained plugins in the [Community Plugins Repository](https://github.com/backstage/community-plugins)! + +## Next Steps + +If you're chomping at the bit to write your own plugin, you can move to the `plugins` Golden Path. We recommend you make a note to come back and finish this Golden Path to get more information on maintaining a Backstage app long term. + +For the rest of you, let's walk through keeping your Backstage app up to date! diff --git a/docs/golden-path/create-app/keeping-backstage-updated.md b/docs/golden-path/create-app/keeping-backstage-updated.md new file mode 100644 index 0000000000..de7d99b2b8 --- /dev/null +++ b/docs/golden-path/create-app/keeping-backstage-updated.md @@ -0,0 +1,166 @@ +--- +id: keeping-backstage-updated +sidebar_label: 006 - Keep Backstage updated +title: 006 - Keeping Backstage up to date +--- + +Audience: Developers and Admins + +:::note Note +To better understand the concepts in this section, it's recommended to have an understanding of [Monorepos](https://semaphoreci.com/blog/what-is-monorepo), [Semantic Versioning](https://semver.org) and [CHANGELOGs](https://keepachangelog.com). +::: + +## Summary + +Backstage is always improving, so it's a good idea to stay in sync with the +latest releases. Backstage is more of a library than an application or service; +similar to `create-react-app`, the `@backstage/create-app` tool gives you a +starting point that's meant to be evolved. + +## Updating Backstage versions with backstage-cli + +The Backstage CLI has a command to bump all `@backstage` packages and +dependencies you're using to the latest versions: +[versions:bump](https://backstage.io/docs/tooling/cli/03-commands#versionsbump). + +```bash +yarn backstage-cli versions:bump +``` + +The reason for bumping all `@backstage` packages at once is to maintain the +dependencies that they have between each other. + + +:::tip + +To make the version bump process even easier and more streamlined we highly recommend using the [Backstage yarn plugin](#managing-package-versions-with-the-backstage-yarn-plugin) + +::: + +By default the bump command will upgrade `@backstage` packages to the latest `main` release line which is released monthly. For those in a hurry that want to track the `next` release line which releases weekly can do so using the `--release next` option. + +```bash +yarn backstage-cli versions:bump --release next +``` + +If you are using other plugins you can pass in the `--pattern` option to update +more than just the `@backstage/*` dependencies. + +```bash +yarn backstage-cli versions:bump --pattern '@{backstage,roadiehq}/*' +``` + +## Following create-app template changes + +The `@backstage/create-app` command creates the initial structure of your +Backstage installation from a **template**. The source of this template in the +Backstage repository is updated periodically, but your local `app` and `backend` +packages are established at `create-app` time and won't automatically get these +template updates. + +For this reason, any changes made to the template are documented along with +upgrade instructions in the +[changelog](https://github.com/backstage/backstage/blob/master/packages/create-app/CHANGELOG.md) +of the `@backstage/create-app` package. We recommend peeking at this changelog +for any applicable updates when upgrading packages. As an alternative, the +[Backstage Upgrade Helper](https://backstage.github.io/upgrade-helper/) provides +a consolidated view of all the changes between two versions of Backstage. You +can find the current version of your Backstage installation in `backstage.json` located in the root of your backstage repository. + +## Managing package versions with the Backstage yarn plugin + +The Backstage yarn plugin makes it easier to manage Backstage package versions, +by determining the appropriate version for each package based on the overall +Backstage version in `backstage.json`. This avoids the need to update every +package.json across your Backstage monorepo, and means that when adding new +`@backstage` dependencies, you don't need to worry about figuring out the right +version to use to match the currently-installed release of Backstage. + +### Requirements + +In order to use the yarn plugin, you'll need to be using yarn 4.1.1 or greater. + +### Installation + +To install the yarn plugin, run the following command in your Backstage +monorepo: + +```bash +yarn plugin import https://versions.backstage.io/v1/tags/main/yarn-plugin +``` + +The resulting changes in the file system should be committed to your repo. + +:::tip + +For best results it's ideal to add the Backstage Yarn plugin when you are about to do a Backstage upgrade as it will make it easier to confirm everything is working. + +::: + +### Usage + +When the yarn plugin is installed, versions for currently-released `@backstage` +packages can be replaced in package.json with the string `"backstage:^"`. This +instructs yarn to resolve the version based on the overall Backstage version in +`backstage.json`. + +:::tip + +The `backstage.json` is key for the plugin to work, make sure this file is included in your CI/CD pipelines and/or any Container builds. + +::: + +The `backstage-cli versions:bump` command documented above will detect the +installation of the yarn plugin, and when it's installed, will automatically +migrate dependencies across the monorepo to use it. + +## More information on dependency mismatches + +Backstage is structured as a monorepo with +[Yarn workspaces](https://classic.yarnpkg.com/en/docs/workspaces/). This means +the `app` and `backend` packages, as well as any custom plugins you've added, +are separate packages with their own `package.json` and dependencies. + +When a given dependency version is the _same_ between different packages, the +dependency is hoisted to the main `node_modules` folder in the monorepo root to +be shared between packages. When _different_ versions of the same dependency are +encountered, Yarn creates a `node_modules` folder within a particular package. +This can lead to multiple versions of the same package being installed and used +in the same app. + +All Backstage core packages are implemented in such as way that package +duplication is **not** a problem. For example, duplicate installations of +packages like `@backstage/core-plugin-api`, `@backstage/core-components`, +`@backstage/plugin-catalog-react`, and `@backstage/backend-plugin-api` are all +acceptable. + +While package duplication might be acceptable in many cases, you might want to +deduplicate packages for the purpose of optimizing bundle size and installation +speed. We recommend using deduplication utilities such as `yarn dedupe` to trim +down the number of duplicate packages. + +## Proxy + +The Backstage CLI uses [global-agent](https://www.npmjs.com/package/global-agent) and `undici` to configure HTTP/HTTPS proxy settings using environment variables. This allows you to route the CLI’s network traffic through a proxy server, which can be useful in environments with restricted internet access. + +Additionally, `yarn` needs a proxy too (sometimes), when in environments with restricted internet access. It uses different settings than the other modules. If you decide to use the backstage yarn plugin [mentioned above](#plugin), you will need to set additional proxy values. +If you will always need proxy settings in all environments and situations, you can add `httpProxy` and `httpsProxy` values to [the yarnrc.yml file](https://yarnpkg.com/configuration/yarnrc). If some environments need it (say a developer workstation) but other environments do not (perhaps a CI build server running on AWS), then you may not want to update the yarnrc.yml file but just set environment variables `YARN_HTTP_PROXY` and `YARN_HTTPS_PROXY` in the environments/situations where you need to proxy. + +**If you plan to use the backstage yarn plugin, you will need these extra yarn proxy settings to both install the plugin and run the `versions:bump` command**. If you do not plan to use the backstage yarn plugin, it seems like the global agent proxy settings alone are sufficient. + +### Example Configuration + +```bash +export HTTP_PROXY=http://proxy.company.com:8080 +export HTTPS_PROXY=https://secure-proxy.company.com:8080 +export NO_PROXY=localhost,internal.company.com +export GLOBAL_AGENT_HTTP_PROXY=${HTTP_PROXY} +export GLOBAL_AGENT_HTTPS_PROXY=${HTTPS_PROXY} +export GLOBAL_AGENT_NO_PROXY=${NO_PROXY} +export YARN_HTTP_PROXY=${HTTP_PROXY} # optional +export YARN_HTTPS_PROXY=${HTTPS_PROXY} # optional +``` + +## Rollback migrations + +In some cases you could need to downgrade Backstage instance due to some problem or maybe because you are using a test environment to validate the new version of Backstage. You can check the [Manual Rollback using Knex](../../tutorials/manual-knex-rollback.md) guide to know how to rollback migrations using Knex. diff --git a/docs/golden-path/create-app/local-development.md b/docs/golden-path/create-app/local-development.md new file mode 100644 index 0000000000..2c81c2c643 --- /dev/null +++ b/docs/golden-path/create-app/local-development.md @@ -0,0 +1,57 @@ +--- +id: local-development +title: 002 - Local development +--- + +Your Backstage app is fully installed and ready to be run! Now that the installation is complete, you can go to the application directory and start the app using the `yarn start` command. The `yarn start` command will run both the frontend and backend as separate processes (named `[0]` and `[1]`) in the same window. + +```bash +cd my-backstage-app # your app name +yarn start +``` + +![Screenshot of the command output, with the message webpack compiled successfully](../../assets/getting-started/startup.png) + +Here again, there's a small wait for the frontend to start up. Once the frontend is built, your browser window should automatically open. + +:::tip Browser window didn't open + +When you see the message `[0] webpack compiled successfully`, you can navigate directly to `http://localhost:3000` to see your Backstage app. + +::: + +Once its spun up, you should see something similar to the below. + +![Screenshot of the Backstage portal](../../assets/getting-started/portal.png) + +## Architecture of local development + +:::note Deploy architecture + +This section only touches on local development, we'll walk through what a Golden Path production architecture looks like in the `deployment` Golden Path. + +::: + +Now that you have that running, let's talk through what you just set up. You have 2 commands running as part of `yarn start` - the website that is stored at `packages/app` and the backend stored at `packages/backend`. + +The website listens on port `3000` by default. It's a React app with some extra flavor to provide strong plugin-friendly defaults. For local development, we use `rspack` for fast compilation and near-instant feedback. + +The backend listens on port `7007` by default. It is a NodeJS app that has among other things an HTTP server through `express` and talks to a database. + +Locally, we use `sqlite` for the database. This is a fast in-memory database that is perfect for local development. Because of its ephemeral nature, you shouldn't rely on the database to keep data across `yarn start`s. We _do_ however, maintain the database across hot reloads. + +Speaking of hot reloads, these are supported for both the frontend and backend. + +In the frontend, whenever you save a file used by your React app, after a slight delay, you should see a message like + +``` +Rspack compiled successfully +``` + +In the backend, you should see a + +``` +Change detected, restarting the development server... +``` + +followed by init logs from your server. diff --git a/docs/golden-path/create-app/logging-in.md b/docs/golden-path/create-app/logging-in.md new file mode 100644 index 0000000000..a45a89b7a2 --- /dev/null +++ b/docs/golden-path/create-app/logging-in.md @@ -0,0 +1,35 @@ +--- +id: logging-in +title: 004 - Logging into your instance +description: Getting up and running with Backstage and your identity provider +--- + +Audience: Developers, Admins + +## Summary + +This guide will provide a quick tutorial on how to log in to your Backstage instance. It should be used as both an introduction to Backstage's authentication system as well as a debugging guide for any issues you may have while logging in. + +## Prerequisites + +You should have completed the GitHub OAuth app setup defined in [the authentication tutorial](../../getting-started/config/authentication.md). + +## 1. Login to Backstage + +Run your Backstage app with `yarn start`. Navigate to `http://localhost:3000`. + +If you're not already logged in, you should see a login screen like this, + +![Screenshot of the login screen](../../assets/getting-started/login-screen.png) + +To login, you should choose the "GitHub" provider and click the "Sign in" button. This will redirect you to a GitHub OAuth page. Verify that the scopes mentioned on that page match the setup you did in [the authentication tutorial](../../getting-started/config/authentication.md). Once you click "Confirm", you will be brought back to the Backstage interface and signed in! + +If you are already logged in, you will be automatically brought to your Backstage instance. + +## 2. Verify that you're logged in + +Once you've logged in, find the "Settings" item in the navigation bar to the left. Click it and you will see your profile. If you see your profile picture and name from GitHub here, congratulations! You've successfully set up a GitHub authentication integration. + + + +If you don't see your profile picture and name, check that you followed all of the steps in [the authentication tutorial](../../getting-started/config/authentication.md). If you have, search for similar issues on [the Discord server](https://discord.gg/backstage-687207715902193673). diff --git a/docs/golden-path/create-app/npx-create-app.md b/docs/golden-path/create-app/npx-create-app.md new file mode 100644 index 0000000000..8ee016f41f --- /dev/null +++ b/docs/golden-path/create-app/npx-create-app.md @@ -0,0 +1,117 @@ +--- +id: npx-create-app +title: '001 - Scaffolding' +--- + +Audience: Developers and Admins + +:::note Note +It is not required, although recommended to have a basic understanding of [Yarn](https://www.pluralsight.com/guides/yarn-a-package-manager-for-node-js) and [npm](https://docs.npmjs.com/about-npm) before starting this guide. +::: + +## Summary + +This guide walks through how to get started creating your very own Backstage customizable app. This is the first step in evaluating, developing on, or demoing Backstage. + +By the end of this guide, you will have a standalone Backstage installation running locally with a `SQLite` database and demo content. + +:::caution Organization customization + +To be clear, this is not a production-ready installation, and it does not contain information specific to your organization. You will learn how to customize Backstage for your use case through this guide. + +::: + +## Prerequisites + +This guide also assumes a basic understanding of working on a Linux based operating system and have some experience with the terminal, specifically, these commands: `npm`, `yarn`. + +- Access to a Unix-based operating system, such as Linux, macOS or + [Windows Subsystem for Linux](https://docs.microsoft.com/en-us/windows/wsl/) +- A GNU-like build environment available at the command line. + For example, on Debian/Ubuntu you will want to have the `make` and `build-essential` packages installed. + On macOS, you will want to have run `xcode-select --install` to get the XCode command line build tooling in place. +- An account with elevated rights to install the dependencies +- `curl` or `wget` installed +- Node.js [Active LTS Release](../../overview/versioning-policy.md#nodejs-releases) installed using one of these + methods: + - Using `nvm` (recommended) + - [Installing nvm](https://github.com/nvm-sh/nvm#install--update-script) + - [Install and change Node version with nvm](https://nodejs.org/en/download/package-manager/#nvm) + - Node 20 is a good starting point, this can be installed using `nvm install lts/iron` + - [Binary download](https://nodejs.org/en/download/) + - [Package manager](https://nodejs.org/en/download/package-manager/) + - [Using NodeSource packages](https://github.com/nodesource/distributions/blob/master/README.md) +- `yarn` [Installation](https://yarnpkg.com/getting-started/install) + - Backstage currently uses Yarn 4.4.1, once you've ran `corepack enable` you'll want to then run `yarn set version 4.4.1` +- `git` [installation](https://github.com/git-guides/install-git) + +## Scaffold your new Backstage app + +## 1. Create your Backstage App + +To scaffold your new Backstage app, we'll be running an interactive command. Before you run the command, you should open a terminal and move your current working directory somewhere you're comfortable creating a new directory. + +The wizard for this command will ask what name you want to have for your new app. That name will match the folder that we create for you. + +When you run the command, you'll see an output like this. + +![create app](../../assets/getting-started/create-app-output.png) + +And when it finishes, you'll have a working Backstage app (with example data)! + +Now, that we know what it does, let's actually scaffold some code! + +```bash +npx @backstage/create-app@latest +``` + +This may take a few minutes to fully install everything. Don't stress if the loading seems to be spinning nonstop, there's a lot going on in the background. + +:::note + +If this fails on the `yarn install` step, it's likely that you will need to install some additional dependencies which are used to configure `isolated-vm`. You can find out more in their [requirements section](https://github.com/laverdet/isolated-vm#requirements), and then run `yarn install` manually again after you've completed those steps. + +::: + +## Structure of your app + +### General folder structure + +Below is a simplified layout of the files and folders generated when creating an app. + +``` +app +├── app-config.yaml +├── catalog-info.yaml +├── package.json +└── packages +   ├── app +   └── backend +``` + +- **app-config.yaml**: Main configuration file for the app. See + [Configuration](https://backstage.io/docs/conf/) for more information. +- **catalog-info.yaml**: Catalog Entities descriptors. See + [Descriptor Format of Catalog Entities](https://backstage.io/docs/features/software-catalog/descriptor-format) + to get started. +- **package.json**: Root package.json for the project. _Note: Be sure that you + don't add any npm dependencies here as they probably should be installed in + the intended workspace rather than in the root._ +- **packages/**: Lerna leaf packages or "workspaces". Everything here is going + to be a separate package, managed by lerna. +- **packages/app/**: A fully functioning Backstage frontend app that acts as a + good starting point for you to get to know Backstage. +- **packages/backend/**: We include a backend that helps power features such as + [Authentication](https://backstage.io/docs/auth/), + [Software Catalog](https://backstage.io/docs/features/software-catalog/), + [Software Templates](https://backstage.io/docs/features/software-templates/) + and [TechDocs](https://backstage.io/docs/features/techdocs/) + amongst other things. + +## Common Issues + +- App is not running on port X: Backstage uses ports `3000` and `7007` as its default frontend and backend ports. Make sure that your commands haven't exited with errors. For remote or containerized setups, make sure those ports above are accessible. + +## Next Steps + +Now that you have a scaffolded app, let's learn how to start it locally for development! diff --git a/docs/golden-path/plugins/backend/001-first-steps.md b/docs/golden-path/plugins/backend/001-first-steps.md new file mode 100644 index 0000000000..1850ba52c1 --- /dev/null +++ b/docs/golden-path/plugins/backend/001-first-steps.md @@ -0,0 +1,55 @@ +--- +id: 001-first-steps +sidebar_label: 001 - Scaffolding the plugin +title: How to scaffold a new plugin? +--- + +# Scaffolding a new plugin + + + +## `yarn new` + +A new, bare-bones backend plugin package can be created by issuing the following +command in your Backstage repository's root directory and selecting `backend-plugin`: + +```sh +yarn new +``` + +You will be asked to supply a name for the plugin. This is an identifier that +will be part of the NPM package name, so make it short and containing only +lowercase characters separated by dashes, for our example, you should provide `todo`. For plugins you may write in the future, this should be an easy to remember indicator of what this plugins does, like if it's a +package that adds an integration with a system named Carmen, you would want to name it `carmen`. + +This will create a new NPM package with a package name something like `@internal/plugin-carmen-backend`, depending on the other flags passed to the `new` command, and your settings for the `new` command in your root `package.json`. For future reference, we also support additional flags and configuration. Learn more at [the CLI docs](../../../tooling/cli/03-commands.md#new). + +Creating the plugin will take a little while, so be patient. If it runs with no issues, it will run the initial installation and build commands, so that your package is ready to be hacked on! + +Once the commands complete, you should see a new folder `plugins/todo-backend` with content like the below tree: + +``` +/ <- your Backstage app's root directory + /plugins/ + /todo-backend/ + package.json + README.md + eslintrc.js + /dev/ + index.ts + /src/ + plugin.ts + index.ts + router.ts + /services/ + /TodoListService/ + TodoListService.ts + types.ts + index.ts +``` + + + +### FAQs + + diff --git a/docs/golden-path/plugins/backend/002-poking-around.md b/docs/golden-path/plugins/backend/002-poking-around.md new file mode 100644 index 0000000000..35de3c4d47 --- /dev/null +++ b/docs/golden-path/plugins/backend/002-poking-around.md @@ -0,0 +1,34 @@ +--- +id: 002-poking-around +sidebar_label: 002 - Poking around +title: 002 - Poking around +--- + +## Default plugin functionality + +By default, that plugin that you just created hosts a simple todo list application. It exposes an HTTP API at `http://localhost:7007/api/todo/todos` that allows you to create TODOs, list existing TODOs, and get a specific TODO. It stores those TODOs in memory, which means that you would lose all of your TODOs if you restarted your application. It also allows you to tag TODOs with a Software Catalog entity, which will be useful for our frontend integration. + +To make this plugin production ready, we'll need to adjust a few things, + +1. Write our TODOs to a database so they don't get lost on restart. +2. Write some proper tests to make sure everything works the way we expect. +3. Get user feedback. + +## Testing locally + +Before we jump in to making this plugin ready to ship, let's walk through how to run it locally. If you open your backend plugin's manifest (`plugins/todo-backend/package.json`), and look at the `scripts` section, you'll notice a few important commands. The ones relevant to use right now are + +1. `yarn start` - Starts a local development server using the content in `dev/index.ts` as the backend. +2. `yarn test` - Runs all of the tests for your backend plugin. + +If you run `yarn start`, you should see a custom backend for just your plugin start up. This will simplify plugin development and iteration for you or your team by easily testing out new features in just your plugin - just make sure you add what you need to the global `packages/backend`. The important log for us to look for is + +``` +2025-06-08T16:14:53.229Z rootHttpRouter info Listening on :7007 +``` + +This indicates that your HTTP server is up and running and we can start sending test HTTP requests. Grab your favorite HTTP client and let's get testing! If you aren't sure what to use, I'd recommend the `humao.rest-client` VSCode extension which can easily be run in VSCode itself with very little extra set up. + +``` + +``` diff --git a/docs/golden-path/plugins/backend/meta.md b/docs/golden-path/plugins/backend/__meta__.md similarity index 76% rename from docs/golden-path/plugins/backend/meta.md rename to docs/golden-path/plugins/backend/__meta__.md index 4507a934d5..e261a8f970 100644 --- a/docs/golden-path/plugins/backend/meta.md +++ b/docs/golden-path/plugins/backend/__meta__.md @@ -88,44 +88,6 @@ After verifying everything, introduce the problem of persistence - the todos are Saving values to the database. Writing a migrations file. Plumbing through the database service. -## Integrations - -Now that our plugin is ready for prime time, let's see how we can really leverage the rest of the Backstage ecosystem. Backstage provides a set of core features out of the box, namely, the Software Catalog, Search, Permissions, and Notifications. - -### Catalog - -We want to show our todos as separate Catalog entities. How can we make this happen? - -### Search - -We want to make our todos searchable. - -### Permissions - -We only want users to be able to find their own todos. - -### Notifications - -We want to set an alarm time for todos that sends a notification when the time is met. - ## SCM Integrations Our users love the new plugin, and now they want it to automatically fetch todos from their source code. - -## Additional Resources and Further Reading - -- **Real-world Implementations and Lessons** - - - [Case studies and examples from the community](https://github.com/backstage/community#newsletters). - - Best practices derived from mature implementations. - - [Existing open-source community-maintained plugins](https://github.com/backstage/community-plugins). - -- **Resource Compendium** - - - [Backstage Glossary](https://backstage.io/docs/references/glossary) of key terms. - - Recommended readings and tools for advanced developers. - -- **Certification and Learning Pathways** - - Pathways to deepen your understanding and expertise in plugin development for Backstage. - -Stay tuned for detailed exploration and guidance in each of these modules. We're excited to accompany you on your plugin development journey! diff --git a/docs/golden-path/plugins/backend/recap.md b/docs/golden-path/plugins/backend/recap.md new file mode 100644 index 0000000000..94eca8fafe --- /dev/null +++ b/docs/golden-path/plugins/backend/recap.md @@ -0,0 +1,23 @@ +## Learning Recap + +With this golden path, you learned how to, + + + +## Additional Resources and Further Reading + +- **Real-world Implementations and Lessons** + + - [Case studies and examples from the community](https://github.com/backstage/community#newsletters). + - Best practices derived from mature implementations. + - [Existing open-source community-maintained plugins](https://github.com/backstage/community-plugins). + +- **Resource Compendium** + + - [Backstage Glossary](https://backstage.io/docs/references/glossary) of key terms. + - Recommended readings and tools for advanced developers. + +- **Certification and Learning Pathways** + - Pathways to deepen your understanding and expertise in plugin development for Backstage. + +Stay tuned for detailed exploration and guidance in each of these modules. We're excited to accompany you on your plugin development journey! diff --git a/docs/golden-path/plugins/backend/todo.http b/docs/golden-path/plugins/backend/todo.http new file mode 100644 index 0000000000..932dcb76e4 --- /dev/null +++ b/docs/golden-path/plugins/backend/todo.http @@ -0,0 +1,12 @@ +POST http://localhost:7007/api/todo/todos +Content-Type: application/json + +{ + "title": "My First TODO" +} + +### + +GET http://localhost:7007/api/todo/todos + +### \ No newline at end of file diff --git a/docs/golden-path/plugins/index.md b/docs/golden-path/plugins/index.md new file mode 100644 index 0000000000..616c2c0c36 --- /dev/null +++ b/docs/golden-path/plugins/index.md @@ -0,0 +1,33 @@ +--- +id: index +sidebar_label: Backstage Plugins! +title: How to create plugins with Backstage +--- + +### Prerequisites + +- We expect that you have finished the create-app golden path. + +### Scenario + +You have an awesome idea to create a todo list tracker in your Backstage instance at an upcoming company hackathon. Backstage is supposed to unify all of our information after all, it should track future tasks to complete as well! + +Many of the great Backstage plugins started in a similar way, a developer noticed that others on their team or in the company were: + +- Wasting time manually compiling spreadsheets filled with error-prone data +- Spending hours every week trying to find that one specific link from that one site +- A million other problems that impact developer flow or are just toil + + And they decided to create a shared plugin in Backstage to solve that problem. + +This guide will teach you how to deliver high-quality Backstage plugins with confidence. Both so you can impress everyone at the hackathon and set yourself up for success when you inevitably are asked to make your plugin production-ready. + +### Structure + +To start, this guide will walk through creating a backend plugin. You'll get your feet wet working with an HTTP API, a database and the Backstage backend system. Then, we'll move to the frontend, where we'll show you how to create a new page that's visible to your Backstage users as well as how to call your API. Finally, we'll walk through some common integrations you may want to consider as you write plugins. + +### Next Steps + +- [Why build plugins?](./why-build-plugins.md) +- [Sustainable plugin development](./sustainable-plugin-development.md) +- [Golden path: Backend plugins](./backend/001-first-steps.md) diff --git a/docs/golden-path/plugins/integrations/__meta__.md b/docs/golden-path/plugins/integrations/__meta__.md new file mode 100644 index 0000000000..2d8df8136e --- /dev/null +++ b/docs/golden-path/plugins/integrations/__meta__.md @@ -0,0 +1,19 @@ +## Integrations + +Now that our plugin is ready for prime time, let's see how we can really leverage the rest of the Backstage ecosystem. Backstage provides a set of core features out of the box, namely, the Software Catalog, Search, Permissions, and Notifications. + +### Catalog + +We want to show our todos as separate Catalog entities. How can we make this happen? + +### Search + +We want to make our todos searchable. + +### Permissions + +We only want users to be able to find their own todos. + +### Notifications + +We want to set an alarm time for todos that sends a notification when the time is met. diff --git a/docs/golden-path/plugins/sustainable-plugin-development.md b/docs/golden-path/plugins/sustainable-plugin-development.md new file mode 100644 index 0000000000..82d529d6e1 --- /dev/null +++ b/docs/golden-path/plugins/sustainable-plugin-development.md @@ -0,0 +1,29 @@ +--- +id: sustainable-plugin-development +sidebar_label: Sustainable plugin development +title: Sustainably developing plugins in Backstage +--- + +Plugins are not created in a vacuum, they generally solve a customer ask, be that + +- a business problem, like showing cloud spend +- a new integration, like showing data from an external vendor such as Pagerduty +- a developer pain point, like organizing information from disjoint or disorganized systems. + +To ensure that your plugin lives the test of time, you'll need to figure out how to keep it up-to-date, + +### Finding your stakeholders + + + +### Iterating on your plugin + +In many cases, your first version of a plugin will cut a few corners - this is a good sign, you're more focused on delivering a strong use case to continue development than over-indexing on your initial code. It may be temporary after all, if you don't get the response you're looking for! + +So, how do you decide when you should iterate on your plugin? + + + +### Ensuring the success of your plugin + + diff --git a/docs/golden-path/plugins/why-build-plugins.md b/docs/golden-path/plugins/why-build-plugins.md new file mode 100644 index 0000000000..b0e1be0a98 --- /dev/null +++ b/docs/golden-path/plugins/why-build-plugins.md @@ -0,0 +1,33 @@ +--- +id: why-build-plugins +sidebar_label: Why build plugins? +title: Introduction to the Value and Impact of Plugins within Backstage +--- + +Backstage plugins are essential components that enable the integration of various tools and services into a unified developer portal. Here’s a detailed look at why building plugins for Backstage can be highly beneficial: + +## Enhancing Developer Productivity + +Plugins in Backstage centralize and simplify access to tools, reducing the time developers spend switching between different systems. By providing a consistent interface and user experience, plugins minimize cognitive load and streamline workflows, allowing developers to focus more on coding and less on tool management. Plugins can leverage platform APIs and reusable UI components to integrate external data and services seamlessly. + +## Customizable and Extensible Platform + +Backstage's plugin architecture is designed to be highly flexible, enabling the integration of nearly any infrastructure or software development tool. This extensibility allows organizations to tailor Backstage to meet their specific needs, integrating internal tools, third-party services, and other custom functionalities seamlessly. For detailed guidelines on plugin development, refer to the [Backstage Plugin Development documentation](https://backstage.io/docs/plugins/plugin-development/). + +## Improved Collaboration and Knowledge Sharing + +Plugins facilitate better collaboration among teams by consolidating documentation, code repositories, CI/CD pipelines, and monitoring tools in one place. This centralization makes it easier for team members to find information, share insights, and collaborate on projects, enhancing overall team productivity and coherence. + +## Consistency and Best Practices + +By adhering to Backstage’s design guidelines, plugins ensure a consistent user experience across the platform. This consistency helps in maintaining usability and reducing the learning curve for new users, promoting the adoption of best practices within development teams. More details can be found in the [Introduction to Plugins](https://backstage.io/docs/plugins/) section. + +## Scalability and Maintenance + +Backstage plugins are designed to be modular and independent, allowing for easy updates and maintenance without affecting the overall system. This modularity supports horizontal scalability, where each plugin can be scaled independently according to the needs of the application, ensuring robust performance and reliability. For more on structuring and connecting plugins, see the [Structure of a Plugin](https://backstage.io/docs/plugins/structure-of-a-plugin) documentation. + +## Community and Ecosystem Growth + +Developing and contributing plugins to the Backstage community helps in expanding the ecosystem. Open-source contributions foster innovation and collaboration, allowing developers to leverage a wide range of existing plugins and avoid reinventing the wheel. This collaborative environment accelerates the development of new features and enhances the overall value of the Backstage platform. + +Building plugins for Backstage significantly enhances developer productivity, promotes best practices, supports scalability, and fosters community growth. These plugins transform Backstage into a comprehensive developer portal that can adapt to the unique needs of any organization. For more detailed information, refer to the [Backstage Plugin Development documentation](https://backstage.io/docs/plugins/plugin-development/) and the [Introduction to Plugins](https://backstage.io/docs/plugins/). diff --git a/docs/integrations/github/discovery.md b/docs/integrations/github/discovery.md index 4a40b25361..f245e960ea 100644 --- a/docs/integrations/github/discovery.md +++ b/docs/integrations/github/discovery.md @@ -58,7 +58,7 @@ To receive the `repository.transferred` event, the new owner account must have t ::: -When creating the webhook in GitHub the "Payload URL" will looks something along these lines: `https:///api/events/http/github` and the "Content Type" should be `application/json`. +When creating the webhook in GitHub the "Payload URL" will looks something along these lines: `https:///api/events/http/github` and the "Content Type" should be `application/json`. The GitHub Webhooks UI will send a trial event to validate it can connect when you save your new Webhook. It is possible to retry this trial event if it fails and you want to send it again. Additionally there is a Recent Deliveries tab you can use to validate that the events are being fired should you need to do any later troubleshooting. @@ -210,6 +210,8 @@ catalog: filters: # optional filters branch: 'develop' # optional string repository: '.*' # optional Regex + pageSizes: + repositories: 25 wildcardProviderId: organization: 'new-org' # string catalogPath: '/groups/**/*.yaml' # this will search all folders for files that end in .yaml @@ -308,6 +310,10 @@ If you do so, `default` will be used as provider ID. The amount of time that should pass before the first invocation happens. - **`scope`** _(optional)_: `'global'` or `'local'`. Sets the scope of concurrency control. +- **`pageSizes`** _(optional)_: + Configure page sizes for GitHub GraphQL API queries. This can help prevent `RESOURCE_LIMITS_EXCEEDED` errors. + - **`repositories`** _(optional)_: + Number of repositories to fetch per page. Defaults to `25`. Reduce this value if hitting API resource limits. ## GitHub API Rate Limits diff --git a/docs/integrations/github/org.md b/docs/integrations/github/org.md index 473d408b0b..6526183f2d 100644 --- a/docs/integrations/github/org.md +++ b/docs/integrations/github/org.md @@ -79,6 +79,10 @@ catalog: initialDelay: { seconds: 30 } frequency: { hours: 1 } timeout: { minutes: 50 } + pageSizes: + teams: 25 + teamMembers: 50 + organizationMembers: 50 - id: ghe githubUrl: https://ghe.mycompany.com orgs: ['internal-1', 'internal-2', 'internal-3'] @@ -94,6 +98,13 @@ Directly under the `githubOrg` is a list of configurations, each entry is a stru - `githubUrl`: The target that this provider should consume - `orgs` (optional): The list of the GitHub orgs to consume. If you only list a single org the generated group entities will use the `default` namespace, otherwise they will use the org name as the namespace. By default the provider will consume all accessible orgs on the given GitHub instance (support for GitHub App integration only). - `schedule`: The refresh schedule to use, matches the structure of [`SchedulerServiceTaskScheduleDefinitionConfig`](https://backstage.io/docs/reference/backend-plugin-api.schedulerservicetaskscheduledefinitionconfig/) +- `pageSizes` (optional): Configure page sizes for GitHub GraphQL API queries to prevent `RESOURCE_LIMITS_EXCEEDED` errors. You can configure the following page sizes: + + - `teams`: Number of teams to fetch per page when querying organization teams (default: 25) + - `teamMembers`: Number of team members to fetch per page when querying team members (default: 50) + - `organizationMembers`: Number of organization members to fetch per page (default: 50) + + Reducing page sizes will result in more API calls and slightly longer sync times, but will prevent API resource limits for organizations with large number of teams and members. ### Events Support diff --git a/docs/integrations/gitlab/discovery.md b/docs/integrations/gitlab/discovery.md index 0fce7b864e..5e02c853fa 100644 --- a/docs/integrations/gitlab/discovery.md +++ b/docs/integrations/gitlab/discovery.md @@ -27,7 +27,7 @@ yarn --cwd packages/backend add @backstage/plugin-catalog-backend-module-gitlab Then add the following to your backend initialization: -```ts title="packages/backend/src/index.ts +```ts title="packages/backend/src/index.ts" // optional if you want HTTP endpoints to receive external events // backend.add(import('@backstage/plugin-events-backend')); // optional if you want to use AWS SQS instead of HTTP endpoints to receive external events @@ -49,7 +49,7 @@ Further documentation: - [Events Plugin](https://github.com/backstage/backstage/tree/master/plugins/events-backend/README.md) - [GitLab Module for the Events Plugin](https://github.com/backstage/backstage/blob/master/plugins/events-backend-module-gitlab/README.md) -### Installation with Legacy Backend System +### Installation with Legacy Backend System (skip if you are using Backstage v1.31.0 or later) #### Installation without Events Support diff --git a/docs/integrations/harness/locations.md b/docs/integrations/harness/locations.md index a3d0e2cfd4..a497dfa63b 100644 --- a/docs/integrations/harness/locations.md +++ b/docs/integrations/harness/locations.md @@ -30,4 +30,4 @@ check out https://developer.harness.io/docs/platform/automation/api/add-and-mana - `host`: The host of the Harness Code instance that you want to match on. - `token` (optional): The password or api token to authenticate with. -- `apiKey` (optional): The apiKey to authenticate with. +- `apiKey` (optional): The API key to authenticate with. diff --git a/docs/landing-page/doc-landing-page.md b/docs/landing-page/doc-landing-page.md new file mode 100644 index 0000000000..cb1b889851 --- /dev/null +++ b/docs/landing-page/doc-landing-page.md @@ -0,0 +1,109 @@ +--- +id: doc-landing-page +title: Documentation +description: Documentation landing page. +--- + +## Understand, try, and administer Backstage + + + + + + + + + + + + + + +
Understand BackstageTry BackstageAdministrationAdditional Features
Learn about Backstage.

+ +
Build a customizable Backstage app.
+ +
Configure, Deploy, & Upgrade.

+ +
Additional Backstage Features

+ +
+ +## Plugin Development, references, and how to contribute + + + + + + + + + + + + +
Plugin DevelopmentReferencesContribute to Backstage
Creating and Configuring Plugins

+ +
Glossary, FAQ, and other reference material.
+ +
Contributions are welcome and greatly appreciated!
+ +
diff --git a/docs/notifications/index.md b/docs/notifications/index.md index 59ff2962ed..dce58d3214 100644 --- a/docs/notifications/index.md +++ b/docs/notifications/index.md @@ -164,6 +164,61 @@ You can customize the origin names shown in the UI by passing an object where th Each notification processor will receive its own row in the settings page, where the user can enable or disable notifications from that processor. +### Default notification settings + +You can configure default notification settings for all users in your `app-config.yaml` file. This allows you to set up notification preferences globally, such as disabling specific channels or origins by default, implementing an opt-in strategy instead of opt-out. + +#### Channel-level defaults + +You can set a default enabled state for an entire channel. When set to `false`, the channel uses an opt-in strategy where notifications are disabled by default unless explicitly enabled by the user or for specific origins. + +```yaml +notifications: + defaultSettings: + channels: + - id: 'Web' + enabled: false # Opt-in strategy: channel disabled by default + - id: 'Email' + enabled: true # Opt-out strategy: channel enabled by default (default behavior) +``` + +#### Origin-level defaults + +You can also configure defaults for specific origins within a channel: + +```yaml +notifications: + defaultSettings: + channels: + - id: 'Web' + enabled: true # Channel is enabled by default + origins: + - id: 'plugin:scaffolder' + enabled: false # Disable scaffolder notifications by default + - id: 'plugin:catalog' + enabled: true # Enable catalog notifications by default +``` + +#### Topic-level defaults + +For even more granular control, you can set defaults for specific topics within origins: + +```yaml +notifications: + defaultSettings: + channels: + - id: 'Email' + enabled: false # Email is opt-in by default + origins: + - id: 'plugin:catalog' + enabled: true # But catalog notifications are enabled + topics: + - id: 'entity:validation:error' + enabled: false # Except validation errors +``` + +**Note:** If a channel's `enabled` flag is not set, it defaults to `true` for backwards compatibility. When a channel is set to `enabled: false`, all origins within that channel default to disabled unless explicitly enabled. + ### Automatic notification cleanup Notifications are deleted automatically after a certain period of time to prevent the database from growing indefinitely diff --git a/docs/notifications/processors.md b/docs/notifications/processors.md index ad3e427364..c25b669ed6 100644 --- a/docs/notifications/processors.md +++ b/docs/notifications/processors.md @@ -148,6 +148,7 @@ notifications: - token: xoxb-XXXXXXXXX broadcastChannels: # Optional, if you wish to support broadcast notifications. - C12345678 + username: 'Backstage Bot' # Optional, defaults to the name of the Slack App. ``` Multiple instances can be added in the `slack` array, allowing you to have multiple configurations if you need to send diff --git a/docs/overview/adopting.md b/docs/overview/adopting.md index e924cfbeb0..8b5703288e 100644 --- a/docs/overview/adopting.md +++ b/docs/overview/adopting.md @@ -85,7 +85,7 @@ Examples of tactics we have used to evangelize Backstage internally: as to send out weekly digest emails showing how usage metrics have changed for individual plugins. -- Pro-actively identify new plugins. Reach out to teams that own internal UIs or +- Pro-actively identify new plugins. Reach out to teams that own internal user interfaces or platforms that you think would make sense to consolidate into Backstage. ## KPIs and metrics diff --git a/docs/overview/architecture-overview.md b/docs/overview/architecture-overview.md index 63e2c16e3c..de283beec0 100644 --- a/docs/overview/architecture-overview.md +++ b/docs/overview/architecture-overview.md @@ -10,285 +10,232 @@ Backstage is organized into three main components, each catering to different gr - Core - This includes the base functionality developed by core developers within the open-source project. - App - The app represents a deployed instance of a Backstage application, customized and maintained by app developers, typically a productivity team within an organization. It integrates core functionalities with additional plugins. -- Plugins - These provide additional functionalities to enhance the usefulness of your Backstage app. Plugins can be company-specific or open-sourced and reusable. At Spotify, we have over 100 plugins created by more than 50 different teams, significantly enriching the unified developer experience by incorporating contributions from various infrastructure teams. +- Plugins - These provide additional functionalities to enhance the usefulness of your Backstage app. Plugins can be company-specific or open-sourced and reusable. ## Overview -The following diagram shows how Backstage might look when deployed inside a -company which uses the Tech Radar plugin, the Lighthouse plugin, the CircleCI -plugin and the software catalog. +The following diagram shows a high level view of the overall architecture of Backstage. Running this architecture in a real environment typically involves +containerizing the components. Various commands are provided for accomplishing this. There are 3 main components in this architecture: -1. The core Backstage UI -2. The UI plugins and their backing services -3. Databases +- The [frontend](#frontend-building-blocks) includes the core Backstage [UI](#user-interface) which is an [extension](#extensions) that interacts directly with the user to present the information from the integrated core feature plugins, and other plugins added by a user. +- The [backend](#backend-building-blocks) includes the backend plugins, [core services](https://backstage.io/docs/backend-system/core-services/index), and other services. This is the server-side part of Backstage that is responsible for wiring things together. You can deploy more than one backend, and more than one backend container, depending on your need to scale and isolate individual features. +- Databases host your Backstage data. -Running this architecture in a real environment typically involves -containerizing the components. Various commands are provided for accomplishing -this. +![The architecture of a basic Backstage application](../assets/architecture-overview/backstage-front-back-arch.jpeg) -![The architecture of a basic Backstage application](../assets/architecture-overview/backstage-typical-architecture.png) +## Frontend building blocks -## User Interface +The architectural diagram provides an overview of the different building blocks and the other blocks that each of them interacts with. -The UI is a thin, client-side wrapper around a set of plugins. It provides some -core UI components and libraries for shared activities such as config -management. [[live demo](https://demo.backstage.io/catalog)] +### App + +This is the app instance itself that you create and use as the root of your Backstage frontend application. It does not have any direct functionality in and of itself, but is simply responsible for wiring things together. + +### Extensions + +[Extensions](../frontend-system/architecture/20-extensions.md) are the building blocks that build out both the visual and non-visual structure of the application. There are both built-in extensions provided by the app itself as well as extensions provided by plugins. Each extension is attached to a parent with which it shares data and can have any number of children of its own. It is up to the app to wire together all extensions into a single tree known as the app extension tree. It is from this structure that the entire app can then be instantiated and rendered. + +#### User Interface + +The UI is one of the extensions in the frontend. It is a thin, client-side wrapper around a set of plugins. It provides some core UI components and libraries for shared activities such as config management. [[live demo](https://demo.backstage.io/catalog)] ![UI with different components highlighted](../assets/architecture-overview/core-vs-plugin-components-highlighted.png) -Each plugin typically makes itself available in the UI on a dedicated URL. For -example, the Lighthouse plugin is registered with the UI on `/lighthouse`. -[[learn more](https://backstage.io/blog/2020/04/06/lighthouse-plugin)] +Each plugin typically makes itself available in the UI on a dedicated URL. For example, the Service Catalog plugin is registered with the UI on `/catalog`. -![The lighthouse plugin UI](../assets/architecture-overview/lighthouse-plugin.png) +### Frontend plugins -The CircleCI plugin is available on `/circleci`. +Plugins provide the actual features inside an app. The size of a plugin can range from a tiny component to an entire new system in which other plugins can be composed and integrated. Plugins can be completely standalone or built on top of each other to extend existing plugins and augment their features. Plugins can communicate with each other by composing their extensions or by sharing Utility APIs and routes. -![CircleCI Plugin UI](../assets/architecture-overview/circle-ci.png) +Backstage includes the following set of core plugins: -## Plugins and plugin backends +- [Software Catalog](../features/software-catalog/index.md) - A centralized system that contains metadata for all your software, such as services, websites, libraries, ML models, data pipelines, and so on. It can also contain metadata for the physical or virtual infrastructure needed to operate a piece of software. The software catalog can be viewed and searched through a UI. +- [Software Templates](../features/software-templates/index.md) - A tool to help you create components inside Backstage. A template can load skeletons of code, include some variables, and then publish the template to a location, such as GitHub. +- [TechDocs](https://backstage.io/docs/features/techdocs/) - A docs-like-code solution built into Backstage. Documentation is written in Markdown files which lives together with the code. +- [Kubernetes](../features/kubernetes/index.md) - A tool that allows developers to check the health of their services whether it is on a local host or in production. +- [Search](https://backstage.io/docs/features/search/) - Search for information in the Backstage ecosystem. You can customize the look and feel of each search result and use your own search engine. -Each plugin is a client side application which mounts itself on the UI. Plugins -are written in TypeScript or JavaScript. They each live in their own directory -in the `plugins` folder. For example, the source code for the catalog plugin -is available at -[plugins/catalog](https://github.com/backstage/backstage/tree/master/plugins/catalog). +[Plugin architecture](#plugin-architecture) provides greater detail about the architecture of the plugins themselves. -### Installing plugins +### Extension Overrides -Plugins are typically installed as React components in your Backstage -application. For example, -[here](https://github.com/backstage/backstage/blob/master/packages/app/src/App.tsx) -is a file that imports many full-page plugins in the Backstage sample app. +In addition to the built-in extensions and extensions provided by plugins, it is also possible to install [extension overrides](../frontend-system/architecture/25-extension-overrides.md). This is a collection of extensions with high priority that can replace existing extensions. They can for example be used to override an individual extension provided by a plugin, or install a completely new extension, such as a new app theme. -An example of one of these plugin components is the `CatalogIndexPage`, which is -a full-page view that allows you to browse entities in the Backstage catalog. It -is installed in the app by importing it and adding it as an element like this: +### Utility APIs -```tsx -import { CatalogIndexPage } from '@backstage/plugin-catalog'; +[Utility APIs](../api/utility-apis.md) provide functionality that makes it easier to build plugins, make it possible for plugins to share functionality with other plugins, as well as serve as a customization point for integrators to change the behaviour of the app. Each Utility API is defined by a TypeScript interface as well as a reference used to access the implementations. The implementations of Utility APIs are defined by extensions that are provided and can be overridden the same as any other extension. -... +### Routes -const routes = ( - - ... - } /> - ... - -); -``` +The [Backstage routing system](../frontend-system/architecture/36-routes.md) adds a layer of indirection that makes it possible for plugins to route to each other's extensions without explicit knowledge of what URL paths the extensions are rendered at or if they even exist at all. It makes it possible for plugins to share routes with each other and dynamically generate concrete links at runtime. It is the responsibility of the app to resolve these links to actual URLs, but it is also possible for integrators to define their own route bindings that decide how the links should be resolved. The routing system also lets plugins define internal routes, aiding in the linking to different content in the same plugin. -Note that we use `"/catalog"` as our path to this plugin page, but we can choose -any route we want for the page, as long as it doesn't collide with the routes -that we choose for the other plugins in the app. +## Backend building blocks -These components that are exported from plugins are referred to as "Plugin -Extension Components", or "Extension Components". They are regular React -components, but in addition to being able to be rendered by React, they also -contain various pieces of metadata that is used to wire together the entire app. -Extension components are created using `create*Extension` methods, which you can -read more about in the -[composability documentation](../plugins/composability.md). +The architectural diagram provides an overview of the different building blocks, and the other blocks that each of them interact with. -As of this moment, there is no config based install procedure for plugins. Some -code changes are required. +### Backend -### Plugin architecture +This is the [backend instance](../backend-system/architecture/02-backends.md) itself, which you can think of as the unit of deployment. It does not have any functionality in and of itself, but is simply responsible for wiring things together. + +It is up to you to decide how many different backends you want to deploy. You can have all features in a single one, or split things out into multiple smaller deployments, depending on your need to scale and isolate individual features. + +### Backend plugins + +[Plugins](../backend-system/architecture/04-plugins.md) provide the actual features. They operate completely independently of each other. If plugins want to communicate with each other, they must do so over the wire. There can be no direct communication between plugins through code. Because of this constraint, each plugin can be considered to be its own microservice. + +[Plugin architecture](#plugin-architecture) provides greater detail about the architecture of the plugins themselves. + +### Services + +[Services](../backend-system/architecture/03-services.md) provide utilities to help make it simpler to implement plugins, so that each plugin doesn't need to implement everything from scratch. There are many built-in core services, such as the ones for logging, database access, and reading configuration, but you can also import third-party services, or create your own. + +Services are also a customization point for individual backend installations. You can override services with your own implementations, as well as make smaller customizations to existing services. + +### Extension Points + +Many plugins have ways in which you can extend them, for example entity providers for the Catalog, or custom actions for the Scaffolder. These extension patterns are now encoded into Extension Points. + +[Extension Points](../backend-system/architecture/05-extension-points.md) look a little bit like services, since you depend on them just like you would a service. A key difference is that extension points are registered and provided by plugins or modules themselves, based on what customizations each of them want to expose. + +Extension Points are exported separately from the plugin or module instance itself, and it is possible to expose multiple different extension points at once. This makes it easier to evolve and deprecate individual Extension Points over time, rather than dealing with a single large API surface. + +### Modules + +[Modules](../backend-system/architecture/06-modules.md) use Extension Points to add new features to other plugins or modules. They might for example add an individual Catalog Entity Provider, or one or more Scaffolder Actions. + +Each module may only use Extension Points that belong to a single plugin, and the module must be deployed together with that plugin in the same backend instance. Modules may only communicate with their plugin or other modules through the registered extension points. + +Just like plugins, modules also have access to services and can depend on their own service implementations. They will however share services with the plugin that they extend - there are no module-specific service implementations. + +## Databases + +The databases host your Backstage data. The Backstage backend and its built-in plugins are based on the [Knex](http://knexjs.org/) library, and set up a separate logical database per plugin. This gives great isolation and lets them perform migrations and evolve separately from each other. + +The Knex library supports a multitude of databases, but Backstage at this time of writing is tested primarily against two of them: + +- SQLite, which is mainly used as an in-memory mock/test database +- PostgreSQL, which is the preferred production database. + +Other databases such as the MySQL variants are reported to work but [aren't fully tested](https://github.com/backstage/backstage/issues/2460) yet. + +You can find instructions on setting up a PostgreSQL for your Backstage instance in [Database](../getting-started/config/database.md). [Configuring Plugin Databases](../tutorials/configuring-plugin-databases.md) provides information on how to configure a database for a plugin. + +## Plugin architecture Architecturally, plugins can take three forms: -1. Standalone -2. Service backed -3. Third-party backed +- [Standalone](#standalone-plugins) +- [Service backend](#service-backend-plugins) +- [Third-party backend](#third-party-backend-plugins) -#### Standalone plugins +### Standalone plugins -Standalone plugins run entirely in the browser. -[The Tech Radar plugin](https://demo.backstage.io/tech-radar), for example, -simply renders hard-coded information. It doesn't make any API requests to other -services. +Standalone plugins run entirely in the browser. [The Tech Radar plugin](https://demo.backstage.io/tech-radar), for example, simply renders hard-coded information. It doesn't make any API requests to other services. + +The architecture of the Tech Radar installed into a Backstage app is very simple. You just need to add Tech Radar as a frontend plugin into your app, as shown in the following diagram. + +> **NOTE:** +> The following diagram does not show the detailed contents of the frontend and backend containers in order to highlight the changes that pertain to the addition of the specified plugin. + +![ui and tech radar plugin connected together](../assets/architecture-overview/simplified-standalone-plugin-architecture.jpeg) + +Once the plugin has been added, then you can view the Tech Radar information in the Backstage UI. ![tech radar plugin ui](../assets/architecture-overview/tech-radar-plugin.png) -The architecture of the Tech Radar installed into a Backstage app is very -simple. +### Service backend plugins -![ui and tech radar plugin connected together](../assets/architecture-overview/tech-radar-plugin-architecture.png) +Service backend plugins make API requests to a service which is within the purview of the organization running Backstage. -#### Service backed plugins +The Lighthouse plugin, for example, makes requests to the [lighthouse-audit-service](https://github.com/spotify/lighthouse-audit-service). The `lighthouse-audit-service` is a microservice which runs a copy of Google's [Lighthouse library](https://github.com/GoogleChrome/lighthouse/) and stores the results in a PostgreSQL database. -Service backed plugins make API requests to a service which is within the -purview of the organisation running Backstage. +The Lighthouse plugin is added to the frontend. The lighthouse-audit-service container is already publicly available in Docker Hub and can be downloaded and run with -The Lighthouse plugin, for example, makes requests to the -[lighthouse-audit-service](https://github.com/spotify/lighthouse-audit-service). -The `lighthouse-audit-service` is a microservice which runs a copy of Google's -[Lighthouse library](https://github.com/GoogleChrome/lighthouse/) and stores the -results in a PostgreSQL database. +```bash +docker run spotify/lighthouse-audit-service:latest +``` -Its architecture looks like this: +> **NOTE:** +> The following diagram does not show the detailed contents of the frontend and backend, in order to highlight the changes that pertain to the addition of the specified plugin. -![lighthouse plugin backed to microservice and database](../assets/architecture-overview/lighthouse-plugin-architecture.png) +![lighthouse plugin backend to microservice and database](../assets/architecture-overview/simplified-service-based-plugin-architecture.jpeg) -The software catalog in Backstage is another example of a service backed plugin. -It retrieves a list of services, or "entities", from the Backstage Backend -service and renders them in a table for the user. +The software catalog in Backstage is another example of a service backend plugin. It retrieves a list of services, or "entities", from the Backstage Backend service and renders them in a table for the user. -### Third-party backed plugins +### Third-party backend plugins -Third-party backed plugins are similar to service backed plugins. The main -difference is that the service which backs the plugin is hosted outside of the -ecosystem of the company hosting Backstage. +Third-party backend plugins are similar to service backend plugins. The main difference is that the service which backs the plugin is hosted outside of the ecosystem of the company hosting Backstage. -The CircleCI plugin is an example of a third-party backed plugin. CircleCI is a -SaaS service which can be used without any knowledge of Backstage. It has an API -which a Backstage plugin consumes to display content. +The CircleCI plugin is an example of a third-party backend plugin. CircleCI is a SaaS service which can be used without any knowledge of Backstage. It has an API which a Backstage plugin consumes to display content. -Requests going to CircleCI from the user's browser are passed through a proxy -service that Backstage provides. Without this, the requests would be blocked by -Cross Origin Resource Sharing policies which prevent a browser page served at -[https://example.com](https://example.com) from serving resources hosted at -https://circleci.com. +Requests going to CircleCI from the user's browser are passed through a proxy service that Backstage provides. Without this, the requests would be blocked by Cross Origin Resource Sharing policies which prevent a browser page served at [https://example.com](https://example.com) from serving resources hosted at https://circleci.com. -![CircleCI plugin talking to proxy talking to SaaS Circle CI](../assets/architecture-overview/circle-ci-plugin-architecture.png) +> **NOTE:** +> The following diagram does not show the detailed contents of the frontend and backend, in order to highlight the changes that pertain to the addition of the specified plugin. + +![CircleCI plugin talking to proxy talking to SaaS Circle CI](../assets/architecture-overview/simplified-third-party-plugin-architecture.jpeg) ## Package Architecture -Backstage relies heavily on NPM packages, both for distribution of libraries, -and structuring of code within projects. While the way you structure your -Backstage project is up to you, there is a set of established patterns that we -encourage you to follow. These patterns can help set up a sound project -structure as well as provide familiarity between different Backstage projects. +Backstage relies heavily on NPM packages, both for distribution of libraries, and structuring of code within projects. While the way you structure your Backstage project is up to you, there is a set of established patterns that we encourage you to follow. These patterns can help set up a sound project structure as well as provide familiarity between different Backstage projects. -The following diagram shows an overview of the package architecture of -Backstage. It takes the point of view of an individual plugin and all of the -packages that it may contain, indicated by the thicker border and italic text. -Surrounding the plugin are different package groups which are the different -possible interface points of the plugin. Note that not all library package lists -are complete as packages have been omitted for brevity. +The following diagram shows an overview of the package architecture of Backstage. It takes the point of view of an individual plugin and all of the packages that it may contain, indicated by the thicker border and italic text. Surrounding the plugin are different package groups which are the different possible interface points of the plugin. Note that not all library package lists are complete as packages have been omitted for brevity. ![Package architecture](../assets/architecture-overview/package-architecture.drawio.svg) ### Overview -The arrows in the diagram above indicate a runtime dependency on the code of the -target package. This strict dependency graph only applies to runtime -`dependencies`, and there may be `devDependencies` that break the rules of this -table for the purpose of testing. While there are some arrows that show a -dependency on a collection of frontend, backend and isomorphic packages, those -still have to abide by important compatibility rules shown in the bottom left. +The arrows in the diagram above indicate a runtime dependency on the code of the target package. This strict dependency graph only applies to runtime +`dependencies`, and there may be `devDependencies` that break the rules of this table for the purpose of testing. While there are some arrows that show a dependency on a collection of frontend, backend and isomorphic packages, those still have to abide by important compatibility rules shown in the bottom left. -The `app` and `backend` packages are the entry points of a Backstage project. -The `app` package is the frontend application that brings together a collection -of frontend plugins and customizes them to fit an organization, while the -`backend` package is the backend service that powers the Backstage application. -Worth noting is that there can be more than one instance of each of these -packages within a project. Particularly the `backend` packages can benefit from -being split up into smaller deployment units that each serve their own purpose -with a smaller collection of plugins. +The `app` and `backend` packages are the entry points of a Backstage project. The `app` package is the frontend application that brings together a collection of frontend plugins and customizes them to fit an organization, while the `backend` package is the backend service that powers the Backstage application. Worth noting is that there can be more than one instance of each of these packages within a project. Particularly the `backend` packages can benefit from being split up into smaller deployment units that each serve their own purpose with a smaller collection of plugins. ### Plugin Packages -A typical plugin consists of up to five packages, two frontend ones, two -backend, and one isomorphic package. All packages within the plugin must share a -common prefix, typically of the form `@/plugin-`, but -alternatives like `backstage-plugin-` or -`@/backstage-plugin-` are also valid. Along with this prefix, -each of the packages have their own unique suffix that denotes their role. In -addition to these five plugin packages it's also possible for a plugin to have -additional frontend and backend modules that can be installed to enable optional -features. For a full list of suffixes and their roles, see the -[Plugin Package Structure ADR](../architecture-decisions/adr011-plugin-package-structure.md). +A typical plugin consists of up to five packages, two frontend ones, two backend, and one isomorphic package. All packages within the plugin must share a common prefix, typically of the form `@/plugin-`, but alternatives like `backstage-plugin-` or `@/backstage-plugin-` are also valid. Along with this prefix, each of the packages have their own unique suffix that denotes their role. In addition to these five plugin packages it's also possible for a plugin to have +additional frontend and backend modules that can be installed to enable optional features. For a full list of suffixes and their roles, see the [Plugin Package Structure ADR](../architecture-decisions/adr011-plugin-package-structure.md). -The `-react`, `-common`, and `-node` plugin packages together form the external -library of a plugin. The plugin library enables other plugins to build on top of -and extend a plugin, and likewise allows the plugin to depend on and extend -other plugins. Because of this, it is preferable that plugin library packages -allow duplicate installations of themselves, as you may end up with a mix of -versions being installed as dependencies of various plugins. It is also -forbidden for plugins to directly import non-library packages from other -plugins, all communication between plugins must be handled through libraries and -the application itself. +The `-react`, `-common`, and `-node` plugin packages together form the external library of a plugin. The plugin library enables other plugins to build on top of and extend a plugin, and likewise allows the plugin to depend on and extend other plugins. Because of this, it is preferable that plugin library packages allow duplicate installations of themselves, as you may end up with a mix of versions being installed as dependencies of various plugins. It is also forbidden for plugins to directly import non-library packages from other plugins, all communication between plugins must be handled through libraries and the application itself. ### Frontend Packages -The frontend packages are grouped into two main groups. The first one is -"Frontend App Core", which is the set of packages that are only used by the -`app` package itself. These packages help build up the core structure of the app -as well as provide a foundation for the plugin libraries to rely upon. +The frontend packages are grouped into two main groups. The first one is "Frontend App Core", which is the set of packages that are only used by the `app` package itself. These packages help build up the core structure of the app as well as provide a foundation for the plugin libraries to rely upon. -The second group is the rest of the shared packages, further divided into -"Frontend Plugin Core" and "Frontend Libraries". The core packages are -considered particularly stable and form the core of the frontend framework. -Their most important role is to form the boundary around each plugin and provide -a set of tools that helps you combine a collection of plugins into a running -application. The rest of the frontend packages are more traditional libraries -that serve as building blocks to create plugins. +The second group is the rest of the shared packages, further divided into "Frontend Plugin Core" and "Frontend Libraries". The core packages are considered particularly stable and form the core of the frontend framework. Their most important role is to form the boundary around each plugin and provide a set of tools that helps you combine a collection of plugins into a running application. The rest of the frontend packages are more traditional libraries that serve as building blocks to create plugins. ### Backend Packages -The backend library packages do not currently share a similar plugin -architecture as the frontend packages. They are instead simply a collection of -building blocks and patterns that help you build backend services. This is -however likely to change in the future. +The backend library packages do not currently share a similar plugin architecture as the frontend packages. They are instead simply a collection of building blocks and patterns that help you build backend services. This is however likely to change in the future. ### Common Packages -The common packages are the packages effectively depended on by all other pages. -This is a much smaller set of packages but they are also very pervasive. Because -the common packages are isomorphic and must execute both in the frontend and -backend, they are never allowed to depend on any of the frontend or backend -packages. +The common packages are the packages effectively depended on by all other pages. This is a much smaller set of packages but they are also very pervasive. Because the common packages are isomorphic and must execute both in the frontend and backend, they are never allowed to depend on any of the frontend or backend packages. -The Backstage CLI is in a category of its own and is depended on by virtually -all other packages. It's not a library in itself though, and must always be a -development dependency only. +The Backstage CLI is in a category of its own and is depended on by virtually all other packages. It's not a library in itself though, and must always be a development dependency only. ### Deciding where you place your code -It can sometimes be difficult to decide where to place your plugin code. For example -should it go directly in the `-backend` plugin package or in the `-node` package? -As a general guideline you should try to keep the exposure of your code as low -as possible. If it doesn't need to be public API, it's best to avoid. If you don't -need it to be used by other plugins, then keep it directly in the plugin packages. +It can sometimes be difficult to decide where to place your plugin code. For example, should it go directly in the `-backend` plugin package or in the `-node` package? As a general guideline you should try to keep the exposure of your code as low as possible. If it doesn't need to be public API, it's best to avoid. If you don't need it to be used by other plugins, then keep it directly in the plugin packages. Below is a chart to help you decide where to place your code. ![Package decision](../assets/architecture-overview/package-decision.drawio.svg) -## Databases - -As we have seen, both the `lighthouse-audit-service` and `catalog-backend` -require a database to work with. - -The Backstage backend and its built-in plugins are based on the -[Knex](http://knexjs.org/) library, and set up a separate logical database per -plugin. This gives great isolation and lets them perform migrations and evolve -separately from each other. - -The Knex library supports a multitude of databases, but Backstage at this time -of writing is tested primarily against two of them: SQLite, which is mainly used as -an in-memory mock/test database, and PostgreSQL, which is the preferred -production database. Other databases such as the MySQL variants are reported to -work but -[aren't fully tested](https://github.com/backstage/backstage/issues/2460) -yet. - ## Cache -The Backstage backend and its built-in plugins are also able to leverage cache -stores as a means of improving performance or reliability. Similar to how -databases are supported, plugins receive logically separated cache connections, -which are powered by [Keyv](https://github.com/lukechilds/keyv) under the hood. +The Backstage backend and its built-in plugins are also able to leverage cache stores as a means of improving performance or reliability. Similar to how databases are supported, plugins receive logically separated cache connections, which are powered by [Keyv](https://github.com/lukechilds/keyv) under the hood. -At this time of writing, Backstage can be configured to use one of five cache -stores: memory, which is mainly used for local testing, memcache, redis, valkey or infinispan, -which are cache stores better suited for production deployment. The right cache -store for your Backstage instance will depend on your own run-time constraints -and those required of the plugins you're running. +At this time of writing, Backstage can be configured to use one of five cache stores: + +- `memory` +- `memcache` +- `redis` +- `valkey` +- `infinispan` + +`memory` is primarily used for local development, for production deployments, we recommend using one of the other cache stores. The right cache store for your Backstage instance will depend on your own run-time constraints and those required of the plugins you're running. ### Use memory for cache @@ -351,30 +298,3 @@ backend: ``` Contributions supporting other cache stores are welcome! - -## Containerization - -The example Backstage architecture shown above would Dockerize into three -separate Docker images. - -1. The frontend container -2. The backend container -3. The Lighthouse audit service container - -![Boxes around the architecture to indicate how it is containerised](../assets/architecture-overview/containerised.png) - -The backend container can be built by running the following command: - -```bash -yarn run build -yarn run build-image -``` - -This will create a container called `example-backend`. - -The lighthouse-audit-service container is already publicly available in Docker -Hub and can be downloaded and run with - -```bash -docker run spotify/lighthouse-audit-service:latest -``` diff --git a/docs/overview/technical-overview.md b/docs/overview/technical-overview.md new file mode 100644 index 0000000000..63c20dbf2f --- /dev/null +++ b/docs/overview/technical-overview.md @@ -0,0 +1,88 @@ +--- +id: technical-overview +title: Technical overview +description: Technical overview of Backstage +--- + +## Purpose + +Backstage is an open source framework for building developer portals that was created at Spotify to simplify end-to-end software development. As Spotify grew, their infrastructure became more fragmented and teams couldn't find the APIs they were supposed to use, or who owned a service, or documentation on anything. + +Backstage is powered by a centralized [software catalog](#software-catalog-system-model) and utilizes an abstraction layer that sits on top of all of your infrastructure and developer tooling, allowing you to manage all of your software, services, tooling, and testing in one place. + +Backstage uses a [plugin-architecture](#plugin-architecture-overview) which allows you customize the functionality of your Backstage application using a wide variety of available plugins or you can write your own. It also includes automated templates that your teams can use to create new microservices, helping to ensure consistency and adherence to your best practices. Backstage also provides the ability to create, maintain, and find the documentation for all of your software. + +Backstage is now a [CNCF incubation project](https://backstage.io/blog/2022/03/16/backstage-turns-two#out-of-the-sandbox-and-into-incubation). + +## Benefits + +- For _engineering managers_, it allows you to maintain standards and best practices across the organization, and can help you manage your whole tech ecosystem, from migrations to test certification. +- For _end users_ (developers), it makes it fast and simple to build software components in a standardized way, and it provides a central place to manage all projects and documentation. +- For _platform engineers_, it enables extensibility and scalability by letting you easily integrate new tools and services (via plugins), as well as extending the functionality of existing ones. +- For _everyone_, it is a single, consistent experience that ties all of your infrastructure tooling, resources, standards, owners, contributors, and administrators together in one place. + +If you have question or want support, please join our [Discord server](https://discord.gg/backstage-687207715902193673). + +## Core Features + +Backstage includes the following set of core features: + +- [Authentication and Identity](../auth/index.md) - Sign-in and identification of users, and delegating access to third-party resources, using built-in authentication providers. +- [Kubernetes](../features/kubernetes/index.md) - A tool that allows developers to check the health of their services whether it is on a local host or in production. +- [Notifications](../notifications/index.md) - Provides a means for plugins and external services to send messages to either individual users or groups. +- [Permissions](../permissions/overview.md) - Ability to enforce rules concerning the type of access a user is given to specific data, APIs, or interface actions. +- [Search](https://backstage.io/docs/features/search/) - Search for information in the Backstage ecosystem. You can customize the look and feel of each search result and use your own search engine. +- [Software Catalog](../features/software-catalog/index.md) - A centralized system that contains metadata for all your software, such as services, websites, libraries, ML models, data pipelines, and so on. It can also contain metadata for the physical or virtual infrastructure needed to operate a piece of software. The software catalog can be viewed and searched through a UI. +- [Software Templates](../features/software-templates/index.md) - A tool to help you create components inside Backstage. A template can load skeletons of code, include some variables, and then publish the template to a location, such as GitHub. +- [TechDocs](https://backstage.io/docs/features/techdocs/) - A docs-like-code solution built into Backstage. Documentation is written in Markdown files which lives together with the code. + +## Plugin Architecture Overview + +Plugins are client side applications which mount themselves on the Backstage UI. They allow you to incorporate a wide variety of infrastructure and software development tools into your Backstage application. Backstage uses a [plugin-architecture](../overview/architecture-overview.md#plugin-architecture) to provide a consistent user experience, in a single UI, around all of your plugins. + +The Backstage architecture supports three types of plugins: + +- Standalone - runs entirely in a browser and it does not make any API requests to other services. +- Service backed - makes API requests to a service within the ecosystem of the organization running Backstage. +- Third-party backed - similar to service-backed, but the service backing the plugin is hosted outside of the ecosystem of the company hosting Backstage. + +Many of the features available in Backstage are provided by plugins. For example, the Software Catalog is a service backed plugin. When you view the catalog, it retrieves a set of services ("[entities](#software-catalog-system-model)") from the Backstage Backend service and renders them in a table in the UI for you. + +## Software Catalog System Model + +The system model behind the software catalog is based on [_entities_](../references/glossary.md#entity) and it models two main types: + +- Core Entities +- Organizational Entities + +`Core Entities` include: + +- `Components` - Individual pieces of software that can be tracked in source control and can implement APIs for other components to consume. +- `APIs` - Implemented by components and form the boundaries between different components. The API can be either public, restricted, or private. +- `Resources` - The physical or virtual infrastructure needed to operate a component. + +![core entity relationships in the software model](../assets/software-catalog/software-model-core-entities.drawio.svg) + +`Organizational Entities` include: + +- `User` - A person, such as an employee, contractor, or similar. +- `Group` - An organizational entity, such as a team, business unit, and so on. + +When you have a large catalogue of components, APIs, and resources, it can be difficult to understand how they work together. Ecosystem modeling allows you to organize a large catalog of core entities into: + +- Systems - A collection of resources and components that cooperate to perform a function by exposing one or several public APIs. It hides the resources and private APIs between the components from the consumer. +- Domains - A collection of systems that share terminology, domain models, metrics, KPIs, business purpose, or documentation. + +There are three additional items that can be part of the system model: + +- `Location` - A marker that references other places to look for catalog data. +- `Type` - It has no set meaning. You can assign your own types and use them as desired. +- `Template` - Describes both the parameters that are rendered in the frontend part of the scaffolding wizard, and the steps that are executed when scaffolding that component. + +The following diagram illustrates an example of ecosystem modeling, and provides sample relationships between a domain, system, core entities, and organization entities. + +![ecosystem modeling example](../assets/software-catalog/software-model-entities.drawio.svg) + +The following shows an example of viewing all of the components, APIs, and resources that are managed by your group after setting up the relationships to create a group organizational entity. + +![sample ui showing group ownership](../assets/technical-overview/backstage-ui-group-ownership.png) diff --git a/docs/plugins/backend-plugin.md b/docs/plugins/backend-plugin.md index bfd210aeb9..9b8b63fc89 100644 --- a/docs/plugins/backend-plugin.md +++ b/docs/plugins/backend-plugin.md @@ -48,11 +48,20 @@ This will think for a bit, and then say `Listening on :7007`. In a different terminal window, now run ```sh -curl localhost:7007/api/carmen/health +curl localhost:7007/api/carmen/todos ``` -This should return `{"status":"ok"}`. Success! Press `Ctrl + c` to stop it -again. +You should see the following response: + +```json +{ + "items": [] +} +``` + +:::note Note: The route shown here matches the default in the current backend plugin template. If you want a `/health` endpoint for health checks, you can add it to your router yourself. + +::: ## Developing your Backend Plugin diff --git a/docs/plugins/integrating-search-into-plugins.md b/docs/plugins/integrating-search-into-plugins.md index d6f2037529..ec1c4e7a5a 100644 --- a/docs/plugins/integrating-search-into-plugins.md +++ b/docs/plugins/integrating-search-into-plugins.md @@ -248,7 +248,7 @@ You can also check out the documentation on [how to test Backstage plugin module Run `yarn start` in the root folder of your Backstage project and look for logs like these: ```sh -[backend]: YYYY-MM-DDTHH:MM:SS.000Z search info Task worker starting: search_index_faq_snippets, {"version":2,"cadence":"PT10M","initialDelayDuration":"PT3S","timeoutAfterDuration":"PT15M"} task=search_index_faq_snippets +[backend]: YYYY-MM-DDTHH:MM:SS.000Z search info Registered scheduled task: search_index_faq_snippets, {"version":2,"cadence":"PT10M","initialDelayDuration":"PT3S","timeoutAfterDuration":"PT15M"} task=search_index_faq_snippets [backend]: YYYY-MM-DDTHH:MM:SS.000Z search info Collating documents for faq-snippets via FaqSnippetsCollatorFactory documentType=faq-snippets [backend]: YYYY-MM-DDTHH:MM:SS.000Z search info Fetching faq snippets from https://backstage.example.biz/faq-snippets [backend]: YYYY-MM-DDTHH:MM:SS.000Z search info Collating documents for faq-snippets succeeded documentType=faq-snippets diff --git a/docs/plugins/new-backend-system.md b/docs/plugins/new-backend-system.md index 12525b9f95..cf983615b5 100644 --- a/docs/plugins/new-backend-system.md +++ b/docs/plugins/new-backend-system.md @@ -195,7 +195,7 @@ A `ServiceRef` is a named reference to an interface which are later used to reso Services is what provides common utilities that previously resided in the `PluginEnvironment` such as Config, Logging and Database. On startup the backend will make sure that the services are initialized before being passed to the plugin/module that depend on them. -ServiceRefs contain a scope which is used to determine if the serviceFactory creating the service will create a new instance scoped per plugin/module or if it will be shared. `plugin` scoped services will be created once per plugin/module and `root` scoped services will be created once per backend instance. +ServiceRefs contain a scope which is used to determine if the service factory creating the service will create a new instance scoped per plugin/module or if it will be shared. `plugin` scoped services will be created once per plugin/module and `root` scoped services will be created once per backend instance. #### Defining a Service diff --git a/docs/publishing.md b/docs/publishing.md index 3d23572ffe..e6c0b6b466 100644 --- a/docs/publishing.md +++ b/docs/publishing.md @@ -53,8 +53,6 @@ Additional steps for the main line release - Mention any security fixes - Create Release Notes PR - Add the release note file as [`/docs/releases/vx.y.0.md`](./releases) - - Add an entry to [`/microsite/sidebars.ts`](https://github.com/backstage/backstage/blob/master/microsite/sidebars.ts) for the release note - - Update the navigation bar item in [`/microsite/docusaurus.config.ts`](https://github.com/backstage/backstage/blob/master/microsite/docusaurus.config.ts) to point to the new release note - Finally copy the content, without the metadata header, into the description of the [`Version Packages` Pull Request](https://github.com/backstage/backstage/pulls?q=is%3Aopen+is%3Apr+in%3Atitle+%22Version+Packages) Once the release has been published edit the newly created release in the [GitHub repository](https://github.com/backstage/backstage/releases) and replace the text content with the release notes. diff --git a/docs/releases/v1.44.0-changelog.md b/docs/releases/v1.44.0-changelog.md new file mode 100644 index 0000000000..c188973af5 --- /dev/null +++ b/docs/releases/v1.44.0-changelog.md @@ -0,0 +1,2259 @@ +# Release v1.44.0 + +Upgrade Helper: [https://backstage.github.io/upgrade-helper/?to=1.44.0](https://backstage.github.io/upgrade-helper/?to=1.44.0) + +## @backstage/plugin-scaffolder-backend@3.0.0 + +### Major Changes + +- 9b81a90: **BREAKING** - Removing the deprecated types and interfaces, there's no replacement for these types, and hopefully not currently used as they offer no value with the plugin being on the new backend system and no way to consume them. + + Affected types: `CreateWorkerOptions`, `CurrentClaimedTask`, `DatabaseTaskStore`, `DatabaseTaskStoreOptions`, `TaskManager`, `TaskStore`, `TaskStoreCreateTaskOptions`, `TaskStoreCreateTaskResult`, `TaskStoreEmitOptions`, `TaskStoreListEventsOptions`, `TaskStoreRecoverTaskOptions`, `TaskStoreShutDownTaskOptions`, `TaskWorker` and `TemplateActionRegistry`. + +### Patch Changes + +- f222a2e: Fixed distributed actions not being visible in the scaffolder template actions. + + Depending on the plugin startup order, some of the distributed actions were not being registered correctly, + causing them to be invisible in the scaffolder template actions list. + +- Updated dependencies + - @backstage/backend-defaults@0.13.0 + - @backstage/integration@1.18.1 + - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.14 + - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.14 + - @backstage/plugin-scaffolder-backend-module-bitbucket@0.3.15 + - @backstage/plugin-scaffolder-backend-module-gerrit@0.2.14 + - @backstage/plugin-scaffolder-backend-module-github@0.9.1 + - @backstage/plugin-scaffolder-backend-module-gitlab@0.9.6 + - @backstage/plugin-scaffolder-backend-module-azure@0.2.14 + - @backstage/plugin-scaffolder-backend-module-gitea@0.2.14 + - @backstage/plugin-scaffolder-node@0.12.0 + - @backstage/config@1.3.5 + - @backstage/plugin-bitbucket-cloud-common@0.3.3 + - @backstage/backend-openapi-utils@0.6.2 + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-auth-node@0.6.8 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.2.13 + - @backstage/plugin-catalog-node@1.19.1 + - @backstage/plugin-events-node@0.4.16 + - @backstage/plugin-permission-common@0.9.2 + - @backstage/plugin-permission-node@0.10.5 + - @backstage/plugin-scaffolder-common@1.7.2 + +## @backstage/backend-defaults@0.13.0 + +### Minor Changes + +- 2d3e2b2: implement support for direct url for AzureBlobStorageUrlReader search function + +- 8b91238: Adds support for configuring server-level HTTP options through the + `app-config.yaml` file under the `backend.server` key. Supported options + include `headersTimeout`, `keepAliveTimeout`, `requestTimeout`, `timeout`, + `maxHeadersCount`, and `maxRequestsPerSocket`. + + These are passed directly to the underlying Node.js HTTP server. + If omitted, Node.js defaults are used. + +- 8495b18: Add a new `externalTokenHandlersServiceRef` to allow custom external token validations + +### Patch Changes + +- Updated dependencies + - @backstage/backend-app-api@1.2.8 + - @backstage/integration@1.18.1 + - @backstage/config-loader@1.10.5 + - @backstage/config@1.3.5 + - @backstage/backend-plugin-api@1.4.4 + - @backstage/integration-aws-node@0.1.18 + - @backstage/plugin-auth-node@0.6.8 + - @backstage/plugin-events-node@0.4.16 + - @backstage/plugin-permission-node@0.10.5 + +## @backstage/frontend-test-utils@0.4.0 + +### Minor Changes + +- c41dd80: **BREAKING**: Removed the `extensions` option from `renderInTestApp`. If you need to pass extensions to the test app, use the new `renderTestApp` utility instead. + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-app@0.3.1 + - @backstage/frontend-plugin-api@0.12.1 + - @backstage/config@1.3.5 + - @backstage/frontend-app-api@0.13.1 + - @backstage/test-utils@1.7.12 + +## @techdocs/cli@1.10.0 + +### Minor Changes + +- 43afbe5: Techdocs CLI serve supports automatic refresh, relying on `mkdocs` `watch` feature. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.13.0 + - @backstage/config@1.3.5 + - @backstage/plugin-techdocs-node@1.13.8 + +## @backstage/theme@0.7.0 + +### Minor Changes + +- 865bce8: **BREAKING**: Removed the built-in `CssBaseline` from `UnifiedThemeProvider`. If your Backstage instance looks broken after this update, you likely forgot to add our new Backstage UI global CSS. To do that, please import `@backstage/ui/css/styles.css` in `packages/app/src/index.tsx`: + + ```tsx + import '@backstage/ui/css/styles.css'; + ``` + + This change also removes the `noCssBaseline` prop, which became redundant. + +### Patch Changes + +- d5cbdba: The `UnifiedThemeProvider` now coordinates theme attributes on the document `body` in case multiple theme providers are rendered. + +## @backstage/ui@0.8.0 + +### Minor Changes + +- 9acc1d6: **BREAKING**: Added a new `PasswordField` component. As part of this change, the `password` and `search` types have been removed from `TextField`. +- b0d11b5: **BREAKING** Restructure Backstage UI component styling to use CSS Modules instead of pure CSS. We don't expect this to be an issue in practice but it is important to call out that all styles are now loaded through CSS modules with generated class names. We are still providing fixed class names for all components to allow anyone to style their Backstage instance. +- 0c53517: **BREAKING** The ScrollArea component has been removed from Backstage UI because it did not meet our accessibility standards. +- 7b319c5: **BREAKING** Remove Icon component in Backstage UI. This component was creating issue for tree-shaking. It is recommended to use icons from @remixicon/react until we found a better alternative in Backstage UI. + +### Patch Changes + +- 2591b42: Adding a new Dialog component to Backstage UI. +- 827340f: remove default selection of tab +- 5dc17cc: Fix margin utility classes in Backstage UI. +- 85faee0: Fix scroll jumping when opening menu in Backstage UI. +- 3c921c5: Making href mandatory in tabs that are part of a Header component +- df7d2cf: Update react-aria-components to version 1.13.0 +- 507ee55: Fix table sorting icon position in Backstage UI. +- 8b7c3c9: Add new `virtualized`, `maxWidth` and `maxHeight` props to `Menu`, `MenuListBox`, `MenuAutocomplete` and `MenuAutocompleteListBox` to allow for virtalization of long lists inside menus. +- b940062: Added support for data attributes in ``, ``, ``, and `` components, ensuring they are correctly applied to the rendered elements. +- 206c801: Cleaning up Backstage UI props definitions as well as removing ScrollArea in Card to improve accessibility. +- 5c21e45: Add react router for internal routing for ButtonLinks +- 865bce8: Added a background color default on the body +- af4d9b4: We are restructuring our CSS to have a better layer structure. +- 9a47125: Improved SearchField component flex layout and animations. Fixed SearchField behavior in Header components by switching from width-based transitions to flex-basis transitions for better responsive behavior. Added new Storybook stories to test SearchField integration with Header component. +- 9781815: Remove auto selection of tabs for tabs that all have href defined +- 4adbb03: Avoid overriding onChange when spreading props +- f6dff5b: Using react router for internal links in the Menu component + +## @backstage/plugin-api-docs@0.13.0 + +### Minor Changes + +- b8a381e: Remove explicit dependency on `isomorphic-form-data`. + + This explicit dependency was added to address [an issue](https://github.com/swagger-api/swagger-ui/issues/7436) in the + dependency `swagger-ui-react`. That [issue has since been resolved](https://github.com/swagger-api/swagger-ui/issues/7436#issuecomment-889792304), + and `isomorphic-form-data` no longer needs to be declared. + + Additionally, this changeset updates the `swagger-ui-react` dependency to version `5.19.0` or higher, which includes + [compatibility](https://github.com/swagger-api/swagger-ui?tab=readme-ov-file#compatibility) with the latest versions of + the OpenAPI specification. + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.2 + - @backstage/core-components@0.18.2 + - @backstage/frontend-plugin-api@0.12.1 + - @backstage/core-compat-api@0.5.3 + - @backstage/plugin-catalog@1.31.4 + - @backstage/core-plugin-api@1.11.1 + - @backstage/plugin-catalog-common@1.1.6 + - @backstage/plugin-permission-react@0.4.37 + +## @backstage/plugin-mui-to-bui@0.2.0 + +### Minor Changes + +- d5cbdba: This is the first release of the Material UI to Backstage UI migration helper plugin. It adds a new page at `/mui-to-bui` that converts an existing MUI v5 theme into Backstage UI (BUI) CSS variables, with live preview and copy/download. + +### Patch Changes + +- 28ee81c: Fix invalid conversion for `--bui-bg` variable +- Updated dependencies + - @backstage/ui@0.8.0 + - @backstage/frontend-plugin-api@0.12.1 + - @backstage/theme@0.7.0 + - @backstage/core-compat-api@0.5.3 + - @backstage/core-plugin-api@1.11.1 + +## @backstage/plugin-notifications-backend-module-slack@0.2.0 + +### Minor Changes + +- 3d09bb2: Adds username as optional config in order to send Slack notifications with a specific username in the case when using one Slack App for more than just Backstage. + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.5 + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-catalog-node@1.19.1 + - @backstage/plugin-notifications-common@0.1.1 + - @backstage/plugin-notifications-node@0.2.20 + +## @backstage/plugin-scaffolder-node@0.12.0 + +### Minor Changes + +- 9b81a90: **BREAKING** - Marking optional fields as required in the `TaskBroker`, these can be fixed with a no-op `() => void` if you don't want to implement the functions. + + - `cancel`, `recoverTasks` and `retry` are the required methods on the `TaskBroker` interface. + + **NOTE**: If you're affected by this breaking change, please reach out to us in an issue as we're thinking about completely removing the `TaskBroker` extension point soon and would like to hear your use cases for the upcoming re-architecture of the `scaffolder-backend` plugin. + +### Patch Changes + +- c8aa210: **BREAKING ALPHA**: We've moved the `scaffolderActionsExtensionPoint` from `/alpha` to the main export. + + ```tsx + // before + import { scaffolderActionsExtensionPoint } from '@backstage/plugin-scaffolder-node/alpha'; + + // after + import { scaffolderActionsExtensionPoint } from '@backstage/plugin-scaffolder-node'; + ``` + +- 6e2bda7: **DEPRECATION**: We're going to be working on refactoring a lot of the internals of the Scaffolder backend plugin, and with that comes a lot of deprecations and removals for public types that are making these things hard. + + If you're using these types, please reach out to us either on Discord or a GitHub issue with your use cases. + + - `SerializedTask`, `SerializedTaskEvent`, `TaskBroker`, `TaskContext`, `TaskBrokerDispatchOptions`, `TaskBrokerDispatchResult`, `TaskCompletionState`, `TaskEventType`, `TaskFilter`, `TaskFilters`, `TaskStatus` are the types that have now been marked as deprecated, and will be removed in a future release. + +- Updated dependencies + - @backstage/integration@1.18.1 + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-permission-common@0.9.2 + - @backstage/plugin-scaffolder-common@1.7.2 + +## @backstage/app-defaults@1.7.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2 + - @backstage/theme@0.7.0 + - @backstage/core-app-api@1.19.1 + - @backstage/core-plugin-api@1.11.1 + - @backstage/plugin-permission-react@0.4.37 + +## @backstage/backend-app-api@1.2.8 + +### Patch Changes + +- dd69cf6: Moved up registration of unhandled rejections and errors listeners to be done as early as possible, avoiding flakiness in backend startups and instead always logging these failures rather than sometimes crashing the process. +- Updated dependencies + - @backstage/config@1.3.5 + - @backstage/backend-plugin-api@1.4.4 + +## @backstage/backend-dynamic-feature-service@0.7.5 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@3.1.2 + - @backstage/backend-defaults@0.13.0 + - @backstage/plugin-scaffolder-node@0.12.0 + - @backstage/config-loader@1.10.5 + - @backstage/config@1.3.5 + - @backstage/backend-openapi-utils@0.6.2 + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-app-node@0.1.38 + - @backstage/plugin-auth-node@0.6.8 + - @backstage/plugin-events-backend@0.5.7 + - @backstage/plugin-events-node@0.4.16 + - @backstage/plugin-permission-common@0.9.2 + - @backstage/plugin-permission-node@0.10.5 + - @backstage/plugin-search-backend-node@1.3.16 + - @backstage/plugin-search-common@1.2.20 + +## @backstage/backend-openapi-utils@0.6.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4 + +## @backstage/backend-plugin-api@1.4.4 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.5 + - @backstage/plugin-auth-node@0.6.8 + - @backstage/plugin-permission-common@0.9.2 + - @backstage/plugin-permission-node@0.10.5 + +## @backstage/backend-test-utils@1.9.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.13.0 + - @backstage/backend-app-api@1.2.8 + - @backstage/config@1.3.5 + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-auth-node@0.6.8 + - @backstage/plugin-events-node@0.4.16 + - @backstage/plugin-permission-common@0.9.2 + +## @backstage/cli@0.34.4 + +### Patch Changes + +- f2cf564: Removed the script transform cache from the default Jest configuration. The script cache provided a moderate performance boost, but it is incompatible with Jest 30. + +- 33faad2: Allow using custom manifest location in the yarn plugin and version bump. + + The Backstage yarn plugin and version bump allows two new environment variables to configure custom manifest location: + + - `BACKSTAGE_VERSIONS_BASE_URL`: The base URL for fetching the Backstage version + manifest. Defaults to `https://versions.backstage.io/v1/releases/VERSION/manifest.json`. + Useful for running the plugin in environment without direct access to the internet, + for example by using a mirror of the versions API or a proxy. + Note that the environment variable is just the host name, and the path is appended by + the plugin. If you are using the yarn plugin, bump version command will also try + to fetch the new version of the yarn plugin from the same base URL (defaults to + `https://versions.backstage.io/v1/releases/RELEASE/yarn-plugin`) + - `BACKSTAGE_MANIFEST_FILE`: Path to a local manifest file. If set, the plugin + will not attempt to fetch the manifest from the network. Useful for running + the plugin in environment without internet access and without mirror of the + versions API. + +- 6ebc1ea: Fixed module federation config by only setting `import: false` on shared libraries for remote. + +- ab96bb7: Added a new `--entrypoint` option to the `package start` command, which allows you to specify a custom entry directory/file for development applications. This is particularly useful when maintaining separate dev apps for different versions of your plugin (e.g., stable and alpha). + + **Example usage:** + + Consider the following plugin dev folder structure: + + dev/ + index.tsx + alpha/ + index.ts + + - The default `yarn package start` command uses the `dev/` folder as the entry point and executes `dev/index.tsx` file; + - Running `yarn package start --entrypoint dev/alpha` will instead use `dev/alpha/` as the entry point and execute `dev/alpha/index.ts` file. + +- 024645e: Remove unused @octokit modules from cli package + + - @octokit/graphql + - @octokit/graphql-schema + - @octokit/oauth-app + +- d14ef24: Added automatic detection and support for the Backstage Yarn plugin when generating new packages with `yarn new`. When the plugin is installed, new packages will automatically use `backstage:^` ranges for `@backstage/*` dependencies. + +- Updated dependencies + - @backstage/eslint-plugin@0.1.12 + - @backstage/integration@1.18.1 + - @backstage/config-loader@1.10.5 + - @backstage/config@1.3.5 + +## @backstage/config@1.3.5 + +### Patch Changes + +- b45b094: Allow colon to be used as config key. + +## @backstage/config-loader@1.10.5 + +### Patch Changes + +- b45b094: Allow colon to be used as config key. +- Updated dependencies + - @backstage/config@1.3.5 + +## @backstage/core-app-api@1.19.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.5 + - @backstage/core-plugin-api@1.11.1 + +## @backstage/core-compat-api@0.5.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.2 + - @backstage/frontend-plugin-api@0.12.1 + - @backstage/core-plugin-api@1.11.1 + +## @backstage/core-components@0.18.2 + +### Patch Changes + +- d493126: Swap base token for semantic token in ItemCardHeader to ensure readability in light mode. +- 431130c: Added `renderEdge` prop to `` component in `@backstage/core-components` to allow custom rendering of graph edges. +- 6981ae6: Fixed DependencyGraph `svg` size not adapting to the container size +- 95935fb: Fixed dependency graph automatically scrolling forever +- Updated dependencies + - @backstage/config@1.3.5 + - @backstage/theme@0.7.0 + - @backstage/core-plugin-api@1.11.1 + +## @backstage/core-plugin-api@1.11.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.5 + +## @backstage/create-app@0.7.5 + +### Patch Changes + +- c73bfa4: Bumped create-app version. +- c2c6054: Bumped create-app version. +- ffb5b44: Bumped create-app version. +- 316d077: Bumped create-app version. +- 7dcedff: Bump `better-sqlite3` to the latest version + +## @backstage/dev-utils@1.1.15 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.2 + - @backstage/core-components@0.18.2 + - @backstage/theme@0.7.0 + - @backstage/app-defaults@1.7.1 + - @backstage/core-app-api@1.19.1 + - @backstage/core-plugin-api@1.11.1 + - @backstage/integration-react@1.2.11 + +## @backstage/eslint-plugin@0.1.12 + +### Patch Changes + +- 5e98e61: Minor doc updates +- a1dae71: Allow frontend plugin to import from another frontend plugin with same plugin id. + + This prevents the ESLint rule from incorrectly flagging these imports in the new frontend system + where plugin override requires cross-plugin imports. + +## @backstage/frontend-app-api@0.13.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.12.1 + - @backstage/config@1.3.5 + - @backstage/core-app-api@1.19.1 + - @backstage/core-plugin-api@1.11.1 + - @backstage/frontend-defaults@0.3.2 + +## @backstage/frontend-defaults@0.3.2 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2 + - @backstage/plugin-app@0.3.1 + - @backstage/frontend-plugin-api@0.12.1 + - @backstage/config@1.3.5 + - @backstage/frontend-app-api@0.13.1 + +## @backstage/frontend-dynamic-feature-loader@0.1.6 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.12.1 + - @backstage/config@1.3.5 + +## @backstage/frontend-plugin-api@0.12.1 + +### Patch Changes + +- 8ed53eb: Added `coreExtensionData.title`, especially useful for creating extensible layout with tabbed pages, but available for use for other cases too. +- Updated dependencies + - @backstage/core-components@0.18.2 + - @backstage/core-plugin-api@1.11.1 + +## @backstage/integration@1.18.1 + +### Patch Changes + +- d772b51: remove host from azure blob storage integration type +- 84443f1: Adds config definitions for Azure Blob Storage. +- Updated dependencies + - @backstage/config@1.3.5 + +## @backstage/integration-aws-node@0.1.18 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.5 + +## @backstage/integration-react@1.2.11 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.18.1 + - @backstage/config@1.3.5 + - @backstage/core-plugin-api@1.11.1 + +## @backstage/repo-tools@0.15.3 + +### Patch Changes + +- 1359279: Fixed an issue with the OpenAPI generated client and server where import/export statements were duplicated. +- Updated dependencies + - @backstage/config-loader@1.10.5 + - @backstage/backend-plugin-api@1.4.4 + +## @backstage/test-utils@1.7.12 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.5 + - @backstage/theme@0.7.0 + - @backstage/core-app-api@1.19.1 + - @backstage/core-plugin-api@1.11.1 + - @backstage/plugin-permission-common@0.9.2 + - @backstage/plugin-permission-react@0.4.37 + +## @backstage/plugin-app@0.3.1 + +### Patch Changes + +- ae1dad0: Fixed an issue that caused the `NotFound` page to not render correctly when a Page was mounted at `/`. +- Updated dependencies + - @backstage/core-components@0.18.2 + - @backstage/frontend-plugin-api@0.12.1 + - @backstage/theme@0.7.0 + - @backstage/core-plugin-api@1.11.1 + - @backstage/integration-react@1.2.11 + - @backstage/plugin-permission-react@0.4.37 + +## @backstage/plugin-app-backend@0.5.7 + +### Patch Changes + +- Updated dependencies + - @backstage/config-loader@1.10.5 + - @backstage/config@1.3.5 + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-app-node@0.1.38 + - @backstage/plugin-auth-node@0.6.8 + +## @backstage/plugin-app-node@0.1.38 + +### Patch Changes + +- Updated dependencies + - @backstage/config-loader@1.10.5 + - @backstage/backend-plugin-api@1.4.4 + +## @backstage/plugin-app-visualizer@0.1.24 + +### Patch Changes + +- 4406144: Ensure that the text rendering has react keys for all elements +- Updated dependencies + - @backstage/core-components@0.18.2 + - @backstage/frontend-plugin-api@0.12.1 + - @backstage/core-plugin-api@1.11.1 + +## @backstage/plugin-auth@0.1.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2 + - @backstage/frontend-plugin-api@0.12.1 + - @backstage/theme@0.7.0 + +## @backstage/plugin-auth-backend@0.25.5 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.5 + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-auth-node@0.6.8 + - @backstage/plugin-catalog-node@1.19.1 + +## @backstage/plugin-auth-backend-module-atlassian-provider@0.4.8 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-auth-node@0.6.8 + +## @backstage/plugin-auth-backend-module-auth0-provider@0.2.8 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-auth-node@0.6.8 + +## @backstage/plugin-auth-backend-module-aws-alb-provider@0.4.8 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-auth-backend@0.25.5 + - @backstage/plugin-auth-node@0.6.8 + +## @backstage/plugin-auth-backend-module-azure-easyauth-provider@0.2.13 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-auth-node@0.6.8 + +## @backstage/plugin-auth-backend-module-bitbucket-provider@0.3.8 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-auth-node@0.6.8 + +## @backstage/plugin-auth-backend-module-bitbucket-server-provider@0.2.8 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-auth-node@0.6.8 + +## @backstage/plugin-auth-backend-module-cloudflare-access-provider@0.4.8 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.5 + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-auth-node@0.6.8 + +## @backstage/plugin-auth-backend-module-gcp-iap-provider@0.4.8 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-auth-node@0.6.8 + +## @backstage/plugin-auth-backend-module-github-provider@0.3.8 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-auth-node@0.6.8 + +## @backstage/plugin-auth-backend-module-gitlab-provider@0.3.8 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-auth-node@0.6.8 + +## @backstage/plugin-auth-backend-module-google-provider@0.3.8 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-auth-node@0.6.8 + +## @backstage/plugin-auth-backend-module-guest-provider@0.2.13 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-auth-node@0.6.8 + +## @backstage/plugin-auth-backend-module-microsoft-provider@0.3.8 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-auth-node@0.6.8 + +## @backstage/plugin-auth-backend-module-oauth2-provider@0.4.8 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-auth-node@0.6.8 + +## @backstage/plugin-auth-backend-module-oauth2-proxy-provider@0.2.13 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-auth-node@0.6.8 + +## @backstage/plugin-auth-backend-module-oidc-provider@0.4.8 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.5 + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-auth-backend@0.25.5 + - @backstage/plugin-auth-node@0.6.8 + +## @backstage/plugin-auth-backend-module-okta-provider@0.2.8 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-auth-node@0.6.8 + +## @backstage/plugin-auth-backend-module-onelogin-provider@0.3.8 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-auth-node@0.6.8 + +## @backstage/plugin-auth-backend-module-openshift-provider@0.1.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-auth-node@0.6.8 + +## @backstage/plugin-auth-backend-module-pinniped-provider@0.3.8 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.5 + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-auth-node@0.6.8 + +## @backstage/plugin-auth-backend-module-vmware-cloud-provider@0.5.8 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-auth-node@0.6.8 + +## @backstage/plugin-auth-node@0.6.8 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.5 + - @backstage/backend-plugin-api@1.4.4 + +## @backstage/plugin-auth-react@0.1.20 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2 + - @backstage/core-plugin-api@1.11.1 + +## @backstage/plugin-bitbucket-cloud-common@0.3.3 + +### Patch Changes + +- 2aded73: Allow for passing a `pagelen` parameter to configure the `pagelength` property of the `BitbucketCloudEntityProvider` `searchCode` pagination to resolve [bug](https://jira.atlassian.com/browse/BCLOUD-23644) pertaining to duplicate results being returned. +- Updated dependencies + - @backstage/integration@1.18.1 + +## @backstage/plugin-catalog@1.31.4 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.2 + - @backstage/core-components@0.18.2 + - @backstage/frontend-plugin-api@0.12.1 + - @backstage/plugin-search-react@1.9.5 + - @backstage/core-compat-api@0.5.3 + - @backstage/plugin-techdocs-react@1.3.4 + - @backstage/core-plugin-api@1.11.1 + - @backstage/integration-react@1.2.11 + - @backstage/plugin-catalog-common@1.1.6 + - @backstage/plugin-permission-react@0.4.37 + - @backstage/plugin-scaffolder-common@1.7.2 + - @backstage/plugin-search-common@1.2.20 + +## @backstage/plugin-catalog-backend@3.1.2 + +### Patch Changes + +- 9890488: Internal refactor to remove remnants of the old backend system + +- 6493c98: Log before provider-orphaning eviction happens + +- 2aaf01a: Fix for duplicate search results in entity-facets API call + +- e489661: Moved catalog processor and provider disabling and priorities under own config objects. + + This is due to issue with some existing providers, such as GitHub, using array syntax for the provider configuration. + + The new config format is not backwards compatible, so users will need to update their config files. The new format + is as follows: + + ```yaml + catalog: + providerOptions: + providerA: + disabled: false + providerB: + disabled: true + processorOptions: + processorA: + disabled: false + priority: 10 + processorB: + disabled: true + ``` + +- 77516c5: Added new `catalog:validate-entity` action to actions registry. + + This action can be used to validate entities against the software catalog. + This is useful for validating `catalog-info.yaml` file changes locally using the + Backstage MCP server. + +- Updated dependencies + - @backstage/integration@1.18.1 + - @backstage/config@1.3.5 + - @backstage/backend-openapi-utils@0.6.2 + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-catalog-common@1.1.6 + - @backstage/plugin-catalog-node@1.19.1 + - @backstage/plugin-events-node@0.4.16 + - @backstage/plugin-permission-common@0.9.2 + - @backstage/plugin-permission-node@0.10.5 + +## @backstage/plugin-catalog-backend-module-aws@0.4.16 + +### Patch Changes + +- 99fcf98: Removed unused dependencies +- Updated dependencies + - @backstage/backend-defaults@0.13.0 + - @backstage/integration@1.18.1 + - @backstage/plugin-kubernetes-common@0.9.7 + - @backstage/config@1.3.5 + - @backstage/backend-plugin-api@1.4.4 + - @backstage/integration-aws-node@0.1.18 + - @backstage/plugin-catalog-common@1.1.6 + - @backstage/plugin-catalog-node@1.19.1 + +## @backstage/plugin-catalog-backend-module-azure@0.3.10 + +### Patch Changes + +- 84443f1: Adds config definitions for Azure Blob Storage. +- 99fcf98: Removed unused dependencies +- Updated dependencies + - @backstage/integration@1.18.1 + - @backstage/config@1.3.5 + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-catalog-common@1.1.6 + - @backstage/plugin-catalog-node@1.19.1 + +## @backstage/plugin-catalog-backend-module-backstage-openapi@0.5.7 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.5 + - @backstage/backend-openapi-utils@0.6.2 + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-catalog-node@1.19.1 + +## @backstage/plugin-catalog-backend-module-bitbucket-cloud@0.5.4 + +### Patch Changes + +- 2aded73: Allow for passing a `pagelen` parameter to configure the `pagelength` property of the `BitbucketCloudEntityProvider` `searchCode` pagination to resolve [bug](https://jira.atlassian.com/browse/BCLOUD-23644) pertaining to duplicate results being returned. +- 99fcf98: Removed unused dependencies +- Updated dependencies + - @backstage/integration@1.18.1 + - @backstage/config@1.3.5 + - @backstage/plugin-bitbucket-cloud-common@0.3.3 + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-catalog-common@1.1.6 + - @backstage/plugin-catalog-node@1.19.1 + - @backstage/plugin-events-node@0.4.16 + +## @backstage/plugin-catalog-backend-module-bitbucket-server@0.5.4 + +### Patch Changes + +- 99fcf98: Removed unused dependencies +- Updated dependencies + - @backstage/integration@1.18.1 + - @backstage/config@1.3.5 + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-catalog-common@1.1.6 + - @backstage/plugin-catalog-node@1.19.1 + - @backstage/plugin-events-node@0.4.16 + +## @backstage/plugin-catalog-backend-module-gcp@0.3.13 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-kubernetes-common@0.9.7 + - @backstage/config@1.3.5 + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-catalog-node@1.19.1 + +## @backstage/plugin-catalog-backend-module-gerrit@0.3.7 + +### Patch Changes + +- 99fcf98: Removed unused dependencies +- Updated dependencies + - @backstage/integration@1.18.1 + - @backstage/config@1.3.5 + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-catalog-common@1.1.6 + - @backstage/plugin-catalog-node@1.19.1 + +## @backstage/plugin-catalog-backend-module-gitea@0.1.5 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.18.1 + - @backstage/config@1.3.5 + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-catalog-common@1.1.6 + - @backstage/plugin-catalog-node@1.19.1 + +## @backstage/plugin-catalog-backend-module-github@0.11.1 + +### Patch Changes + +- 99fcf98: Removed unused dependencies +- Updated dependencies + - @backstage/integration@1.18.1 + - @backstage/config@1.3.5 + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-catalog-common@1.1.6 + - @backstage/plugin-catalog-node@1.19.1 + - @backstage/plugin-events-node@0.4.16 + +## @backstage/plugin-catalog-backend-module-github-org@0.3.15 + +### Patch Changes + +- 99fcf98: Removed unused dependencies +- Updated dependencies + - @backstage/config@1.3.5 + - @backstage/plugin-catalog-backend-module-github@0.11.1 + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-catalog-node@1.19.1 + - @backstage/plugin-events-node@0.4.16 + +## @backstage/plugin-catalog-backend-module-gitlab@0.7.4 + +### Patch Changes + +- 0443119: Fixed an issue in `GitlabDiscoveryEntityProvider` where entity fetching could fail for projects with special characters or that had been renamed or moved. +- 99fcf98: Removed unused dependencies +- Updated dependencies + - @backstage/backend-defaults@0.13.0 + - @backstage/integration@1.18.1 + - @backstage/config@1.3.5 + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-catalog-common@1.1.6 + - @backstage/plugin-catalog-node@1.19.1 + - @backstage/plugin-events-node@0.4.16 + +## @backstage/plugin-catalog-backend-module-gitlab-org@0.2.14 + +### Patch Changes + +- 99fcf98: Removed unused dependencies +- Updated dependencies + - @backstage/plugin-catalog-backend-module-gitlab@0.7.4 + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-catalog-node@1.19.1 + - @backstage/plugin-events-node@0.4.16 + +## @backstage/plugin-catalog-backend-module-incremental-ingestion@0.7.5 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@3.1.2 + - @backstage/backend-defaults@0.13.0 + - @backstage/config@1.3.5 + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-catalog-node@1.19.1 + - @backstage/plugin-events-node@0.4.16 + - @backstage/plugin-permission-common@0.9.2 + +## @backstage/plugin-catalog-backend-module-ldap@0.11.10 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.5 + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-catalog-common@1.1.6 + - @backstage/plugin-catalog-node@1.19.1 + +## @backstage/plugin-catalog-backend-module-logs@0.1.15 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@3.1.2 + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-events-node@0.4.16 + +## @backstage/plugin-catalog-backend-module-msgraph@0.8.1 + +### Patch Changes + +- 99fcf98: Removed unused dependencies +- Updated dependencies + - @backstage/config@1.3.5 + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-catalog-common@1.1.6 + - @backstage/plugin-catalog-node@1.19.1 + +## @backstage/plugin-catalog-backend-module-openapi@0.2.15 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.18.1 + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-catalog-common@1.1.6 + - @backstage/plugin-catalog-node@1.19.1 + +## @backstage/plugin-catalog-backend-module-puppetdb@0.2.15 + +### Patch Changes + +- 99fcf98: Removed unused dependencies +- Updated dependencies + - @backstage/config@1.3.5 + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-catalog-node@1.19.1 + +## @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.2.13 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-catalog-common@1.1.6 + - @backstage/plugin-catalog-node@1.19.1 + - @backstage/plugin-scaffolder-common@1.7.2 + +## @backstage/plugin-catalog-backend-module-unprocessed@0.6.5 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-auth-node@0.6.8 + - @backstage/plugin-catalog-node@1.19.1 + - @backstage/plugin-catalog-unprocessed-entities-common@0.0.10 + - @backstage/plugin-permission-common@0.9.2 + +## @backstage/plugin-catalog-common@1.1.6 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-common@0.9.2 + - @backstage/plugin-search-common@1.2.20 + +## @backstage/plugin-catalog-graph@0.5.2 + +### Patch Changes + +- 87b5e6e: Add missing API implementation for catalog graph plugin in NFS apps. +- 431130c: Added `renderEdge` prop to `` component in `@backstage/core-components` to allow custom rendering of graph edges. +- 6981ae6: Fixed DependencyGraph `svg` size not adapting to the container size +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.2 + - @backstage/core-components@0.18.2 + - @backstage/frontend-plugin-api@0.12.1 + - @backstage/core-compat-api@0.5.3 + - @backstage/core-plugin-api@1.11.1 + +## @backstage/plugin-catalog-import@0.13.6 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.2 + - @backstage/core-components@0.18.2 + - @backstage/integration@1.18.1 + - @backstage/frontend-plugin-api@0.12.1 + - @backstage/config@1.3.5 + - @backstage/core-compat-api@0.5.3 + - @backstage/core-plugin-api@1.11.1 + - @backstage/integration-react@1.2.11 + - @backstage/plugin-catalog-common@1.1.6 + +## @backstage/plugin-catalog-node@1.19.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-catalog-common@1.1.6 + - @backstage/plugin-permission-common@0.9.2 + - @backstage/plugin-permission-node@0.10.5 + +## @backstage/plugin-catalog-react@1.21.2 + +### Patch Changes + +- 2a3704d: Correct translation key from "type" to "owner" for owner column in entity table to ensure the right translation is loaded. +- ace202e: Update `material-ui-popup-state` to latest available version. +- Updated dependencies + - @backstage/core-components@0.18.2 + - @backstage/frontend-plugin-api@0.12.1 + - @backstage/frontend-test-utils@0.4.0 + - @backstage/core-compat-api@0.5.3 + - @backstage/core-plugin-api@1.11.1 + - @backstage/integration-react@1.2.11 + - @backstage/plugin-catalog-common@1.1.6 + - @backstage/plugin-permission-common@0.9.2 + - @backstage/plugin-permission-react@0.4.37 + +## @backstage/plugin-catalog-unprocessed-entities@0.2.22 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2 + - @backstage/frontend-plugin-api@0.12.1 + - @backstage/core-compat-api@0.5.3 + - @backstage/core-plugin-api@1.11.1 + +## @backstage/plugin-catalog-unprocessed-entities-common@0.0.10 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-common@0.9.2 + +## @backstage/plugin-config-schema@0.1.73 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2 + - @backstage/core-plugin-api@1.11.1 + +## @backstage/plugin-devtools@0.1.32 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2 + - @backstage/frontend-plugin-api@0.12.1 + - @backstage/core-compat-api@0.5.3 + - @backstage/core-plugin-api@1.11.1 + - @backstage/plugin-devtools-common@0.1.18 + - @backstage/plugin-permission-react@0.4.37 + +## @backstage/plugin-devtools-backend@0.5.10 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.13.0 + - @backstage/config-loader@1.10.5 + - @backstage/config@1.3.5 + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-devtools-common@0.1.18 + - @backstage/plugin-permission-common@0.9.2 + - @backstage/plugin-permission-node@0.10.5 + +## @backstage/plugin-devtools-common@0.1.18 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-common@0.9.2 + +## @backstage/plugin-events-backend@0.5.7 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.5 + - @backstage/backend-openapi-utils@0.6.2 + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-events-node@0.4.16 + +## @backstage/plugin-events-backend-module-aws-sqs@0.4.16 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.5 + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-events-node@0.4.16 + +## @backstage/plugin-events-backend-module-azure@0.2.25 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-events-node@0.4.16 + +## @backstage/plugin-events-backend-module-bitbucket-cloud@0.2.25 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-events-node@0.4.16 + +## @backstage/plugin-events-backend-module-bitbucket-server@0.1.6 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-events-node@0.4.16 + +## @backstage/plugin-events-backend-module-gerrit@0.2.25 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-events-node@0.4.16 + +## @backstage/plugin-events-backend-module-github@0.4.5 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.18.1 + - @backstage/config@1.3.5 + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-events-node@0.4.16 + +## @backstage/plugin-events-backend-module-gitlab@0.3.6 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.5 + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-events-node@0.4.16 + +## @backstage/plugin-events-backend-module-google-pubsub@0.1.5 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.5 + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-events-node@0.4.16 + +## @backstage/plugin-events-backend-module-kafka@0.1.4 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.5 + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-events-node@0.4.16 + +## @backstage/plugin-events-backend-test-utils@0.1.49 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-events-node@0.4.16 + +## @backstage/plugin-events-node@0.4.16 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4 + +## @backstage/plugin-gateway-backend@1.0.6 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4 + +## @backstage/plugin-home@0.8.13 + +### Patch Changes + +- e7d59d3: fix(home): correct `clearAll` logic to properly handle `deletable` flag +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.2 + - @backstage/core-components@0.18.2 + - @backstage/frontend-plugin-api@0.12.1 + - @backstage/config@1.3.5 + - @backstage/theme@0.7.0 + - @backstage/core-compat-api@0.5.3 + - @backstage/core-app-api@1.19.1 + - @backstage/core-plugin-api@1.11.1 + - @backstage/plugin-home-react@0.1.31 + +## @backstage/plugin-home-react@0.1.31 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2 + - @backstage/frontend-plugin-api@0.12.1 + - @backstage/core-plugin-api@1.11.1 + +## @backstage/plugin-kubernetes@0.12.12 + +### Patch Changes + +- 99fcf98: Removed unused dependencies +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.2 + - @backstage/plugin-kubernetes-react@0.5.12 + - @backstage/core-components@0.18.2 + - @backstage/plugin-kubernetes-common@0.9.7 + - @backstage/frontend-plugin-api@0.12.1 + - @backstage/core-compat-api@0.5.3 + - @backstage/core-plugin-api@1.11.1 + - @backstage/plugin-permission-react@0.4.37 + +## @backstage/plugin-kubernetes-backend@0.20.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-kubernetes-common@0.9.7 + - @backstage/config@1.3.5 + - @backstage/backend-plugin-api@1.4.4 + - @backstage/integration-aws-node@0.1.18 + - @backstage/plugin-auth-node@0.6.8 + - @backstage/plugin-catalog-node@1.19.1 + - @backstage/plugin-kubernetes-node@0.3.5 + - @backstage/plugin-permission-common@0.9.2 + - @backstage/plugin-permission-node@0.10.5 + +## @backstage/plugin-kubernetes-cluster@0.0.30 + +### Patch Changes + +- 99fcf98: Removed unused dependencies +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.2 + - @backstage/plugin-kubernetes-react@0.5.12 + - @backstage/core-components@0.18.2 + - @backstage/plugin-kubernetes-common@0.9.7 + - @backstage/core-plugin-api@1.11.1 + - @backstage/plugin-permission-react@0.4.37 + +## @backstage/plugin-kubernetes-common@0.9.7 + +### Patch Changes + +- bdd7f95: Make SERVICEACCOUNT_CA_PATH public so it can be imported by external modules. +- Updated dependencies + - @backstage/plugin-permission-common@0.9.2 + +## @backstage/plugin-kubernetes-node@0.3.5 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-kubernetes-common@0.9.7 + - @backstage/backend-plugin-api@1.4.4 + +## @backstage/plugin-kubernetes-react@0.5.12 + +### Patch Changes + +- ac405f2: The configmaps added to be rendered +- f7a4144: Fixes calculation of CPU utilization in the PodTable +- Updated dependencies + - @backstage/core-components@0.18.2 + - @backstage/plugin-kubernetes-common@0.9.7 + - @backstage/core-plugin-api@1.11.1 + +## @backstage/plugin-mcp-actions-backend@0.1.4 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.13.0 + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-catalog-node@1.19.1 + +## @backstage/plugin-notifications@0.5.10 + +### Patch Changes + +- f5e0963: Removed unused dependencies +- Updated dependencies + - @backstage/core-components@0.18.2 + - @backstage/frontend-plugin-api@0.12.1 + - @backstage/theme@0.7.0 + - @backstage/core-compat-api@0.5.3 + - @backstage/core-plugin-api@1.11.1 + - @backstage/plugin-notifications-common@0.1.1 + - @backstage/plugin-signals-react@0.0.16 + +## @backstage/plugin-notifications-backend@0.5.11 + +### Patch Changes + +- f5e0963: Removed unused dependencies +- 3b8e156: Fixed exclude entity reference not working in notification sending +- Updated dependencies + - @backstage/config@1.3.5 + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-catalog-node@1.19.1 + - @backstage/plugin-notifications-common@0.1.1 + - @backstage/plugin-notifications-node@0.2.20 + - @backstage/plugin-signals-node@0.1.25 + +## @backstage/plugin-notifications-backend-module-email@0.3.14 + +### Patch Changes + +- b8cf31a: chore(deps): bump `nodemailer` from 6.9.16 to 7.0.7 +- f5e0963: Removed unused dependencies +- Updated dependencies + - @backstage/config@1.3.5 + - @backstage/backend-plugin-api@1.4.4 + - @backstage/integration-aws-node@0.1.18 + - @backstage/plugin-catalog-node@1.19.1 + - @backstage/plugin-notifications-common@0.1.1 + - @backstage/plugin-notifications-node@0.2.20 + +## @backstage/plugin-notifications-common@0.1.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.5 + +## @backstage/plugin-notifications-node@0.2.20 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-notifications-common@0.1.1 + - @backstage/plugin-signals-node@0.1.25 + +## @backstage/plugin-org@0.6.45 + +### Patch Changes + +- 8b7351f: Add `initialRelationAggregation` and `showAggregateMembersToggle` options to `EntityMembersListCard` as well to `EntityOwnershipCard` +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.2 + - @backstage/core-components@0.18.2 + - @backstage/frontend-plugin-api@0.12.1 + - @backstage/core-compat-api@0.5.3 + - @backstage/core-plugin-api@1.11.1 + - @backstage/plugin-catalog-common@1.1.6 + +## @backstage/plugin-org-react@0.1.43 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.2 + - @backstage/core-components@0.18.2 + - @backstage/core-plugin-api@1.11.1 + +## @backstage/plugin-permission-backend@0.7.5 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.5 + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-auth-node@0.6.8 + - @backstage/plugin-permission-common@0.9.2 + - @backstage/plugin-permission-node@0.10.5 + +## @backstage/plugin-permission-backend-module-allow-all-policy@0.2.13 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-auth-node@0.6.8 + - @backstage/plugin-permission-common@0.9.2 + - @backstage/plugin-permission-node@0.10.5 + +## @backstage/plugin-permission-common@0.9.2 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.5 + +## @backstage/plugin-permission-node@0.10.5 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.5 + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-auth-node@0.6.8 + - @backstage/plugin-permission-common@0.9.2 + +## @backstage/plugin-permission-react@0.4.37 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.5 + - @backstage/core-plugin-api@1.11.1 + - @backstage/plugin-permission-common@0.9.2 + +## @backstage/plugin-proxy-backend@0.6.7 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-proxy-node@0.1.9 + +## @backstage/plugin-proxy-node@0.1.9 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4 + +## @backstage/plugin-scaffolder@1.34.2 + +### Patch Changes + +- e0ffe84: Add missing `templatingExtensions` option to RouterProps.contextMenu to allow global control across scaffolder pages +- d9aed74: Forward `ui:disabled` in `OwnedEntityPicker` to allow disabling it +- 075e064: Added missing form fields for the new frontend system. +- Updated dependencies + - @backstage/plugin-scaffolder-react@1.19.2 + - @backstage/plugin-catalog-react@1.21.2 + - @backstage/core-components@0.18.2 + - @backstage/integration@1.18.1 + - @backstage/frontend-plugin-api@0.12.1 + - @backstage/core-compat-api@0.5.3 + - @backstage/plugin-techdocs-react@1.3.4 + - @backstage/core-plugin-api@1.11.1 + - @backstage/integration-react@1.2.11 + - @backstage/plugin-catalog-common@1.1.6 + - @backstage/plugin-permission-react@0.4.37 + - @backstage/plugin-scaffolder-common@1.7.2 + +## @backstage/plugin-scaffolder-backend-module-azure@0.2.14 + +### Patch Changes + +- c8aa210: Updating import for the `scaffolderActionsExtensionPoint` to be the main export +- Updated dependencies + - @backstage/integration@1.18.1 + - @backstage/plugin-scaffolder-node@0.12.0 + - @backstage/config@1.3.5 + - @backstage/backend-plugin-api@1.4.4 + +## @backstage/plugin-scaffolder-backend-module-bitbucket@0.3.15 + +### Patch Changes + +- c8aa210: Updating import for the `scaffolderActionsExtensionPoint` to be the main export +- Updated dependencies + - @backstage/integration@1.18.1 + - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.14 + - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.14 + - @backstage/plugin-scaffolder-node@0.12.0 + - @backstage/config@1.3.5 + - @backstage/backend-plugin-api@1.4.4 + +## @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.14 + +### Patch Changes + +- c8aa210: Updating import for the `scaffolderActionsExtensionPoint` to be the main export +- Updated dependencies + - @backstage/integration@1.18.1 + - @backstage/plugin-scaffolder-node@0.12.0 + - @backstage/config@1.3.5 + - @backstage/plugin-bitbucket-cloud-common@0.3.3 + - @backstage/backend-plugin-api@1.4.4 + +## @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.14 + +### Patch Changes + +- c8aa210: Updating import for the `scaffolderActionsExtensionPoint` to be the main export +- f5e0963: Removed unused dependencies +- Updated dependencies + - @backstage/integration@1.18.1 + - @backstage/plugin-scaffolder-node@0.12.0 + - @backstage/config@1.3.5 + - @backstage/backend-plugin-api@1.4.4 + +## @backstage/plugin-scaffolder-backend-module-confluence-to-markdown@0.3.14 + +### Patch Changes + +- c8aa210: Updating import for the `scaffolderActionsExtensionPoint` to be the main export +- Updated dependencies + - @backstage/integration@1.18.1 + - @backstage/plugin-scaffolder-node@0.12.0 + - @backstage/config@1.3.5 + - @backstage/backend-plugin-api@1.4.4 + +## @backstage/plugin-scaffolder-backend-module-cookiecutter@0.3.16 + +### Patch Changes + +- c8aa210: Updating import for the `scaffolderActionsExtensionPoint` to be the main export +- Updated dependencies + - @backstage/backend-defaults@0.13.0 + - @backstage/integration@1.18.1 + - @backstage/plugin-scaffolder-node@0.12.0 + - @backstage/config@1.3.5 + - @backstage/backend-plugin-api@1.4.4 + +## @backstage/plugin-scaffolder-backend-module-gcp@0.2.14 + +### Patch Changes + +- baf1cab: Fix documentation strings to mention GCP instead of Azure +- Updated dependencies + - @backstage/integration@1.18.1 + - @backstage/plugin-scaffolder-node@0.12.0 + - @backstage/config@1.3.5 + - @backstage/backend-plugin-api@1.4.4 + +## @backstage/plugin-scaffolder-backend-module-gerrit@0.2.14 + +### Patch Changes + +- c8aa210: Updating import for the `scaffolderActionsExtensionPoint` to be the main export +- Updated dependencies + - @backstage/integration@1.18.1 + - @backstage/plugin-scaffolder-node@0.12.0 + - @backstage/config@1.3.5 + - @backstage/backend-plugin-api@1.4.4 + +## @backstage/plugin-scaffolder-backend-module-gitea@0.2.14 + +### Patch Changes + +- c8aa210: Updating import for the `scaffolderActionsExtensionPoint` to be the main export +- Updated dependencies + - @backstage/integration@1.18.1 + - @backstage/plugin-scaffolder-node@0.12.0 + - @backstage/config@1.3.5 + - @backstage/backend-plugin-api@1.4.4 + +## @backstage/plugin-scaffolder-backend-module-github@0.9.1 + +### Patch Changes + +- c8aa210: Updating import for the `scaffolderActionsExtensionPoint` to be the main export +- Updated dependencies + - @backstage/integration@1.18.1 + - @backstage/plugin-scaffolder-node@0.12.0 + - @backstage/config@1.3.5 + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-catalog-node@1.19.1 + +## @backstage/plugin-scaffolder-backend-module-gitlab@0.9.6 + +### Patch Changes + +- c8aa210: Updating import for the `scaffolderActionsExtensionPoint` to be the main export +- f5e0963: Removed unused dependencies +- Updated dependencies + - @backstage/integration@1.18.1 + - @backstage/plugin-scaffolder-node@0.12.0 + - @backstage/config@1.3.5 + - @backstage/backend-plugin-api@1.4.4 + +## @backstage/plugin-scaffolder-backend-module-notifications@0.1.15 + +### Patch Changes + +- c8aa210: Updating import for the `scaffolderActionsExtensionPoint` to be the main export +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.12.0 + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-notifications-common@0.1.1 + - @backstage/plugin-notifications-node@0.2.20 + +## @backstage/plugin-scaffolder-backend-module-rails@0.5.14 + +### Patch Changes + +- c8aa210: Updating import for the `scaffolderActionsExtensionPoint` to be the main export +- Updated dependencies + - @backstage/integration@1.18.1 + - @backstage/plugin-scaffolder-node@0.12.0 + - @backstage/config@1.3.5 + - @backstage/backend-plugin-api@1.4.4 + +## @backstage/plugin-scaffolder-backend-module-sentry@0.2.14 + +### Patch Changes + +- c8aa210: Updating import for the `scaffolderActionsExtensionPoint` to be the main export +- eea5360: Add sentry:fetch:dsn action to retrieve a Sentry project's DSN +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.12.0 + - @backstage/config@1.3.5 + - @backstage/backend-plugin-api@1.4.4 + +## @backstage/plugin-scaffolder-backend-module-yeoman@0.4.15 + +### Patch Changes + +- c8aa210: Updating import for the `scaffolderActionsExtensionPoint` to be the main export +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.12.0 + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-scaffolder-node-test-utils@0.3.4 + +## @backstage/plugin-scaffolder-common@1.7.2 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.18.1 + - @backstage/plugin-permission-common@0.9.2 + +## @backstage/plugin-scaffolder-node-test-utils@0.3.4 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.12.0 + - @backstage/backend-plugin-api@1.4.4 + - @backstage/backend-test-utils@1.9.1 + +## @backstage/plugin-scaffolder-react@1.19.2 + +### Patch Changes + +- e61f89e: Don't change loading to false until we've actually got some log state +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.2 + - @backstage/core-components@0.18.2 + - @backstage/frontend-plugin-api@0.12.1 + - @backstage/theme@0.7.0 + - @backstage/core-plugin-api@1.11.1 + - @backstage/plugin-permission-react@0.4.37 + - @backstage/plugin-scaffolder-common@1.7.2 + +## @backstage/plugin-search@1.4.31 + +### Patch Changes + +- 67a3e1a: Implemented AbortController request cancellation for overlapping search requests. This change ensures that when users type quickly, previous search requests are properly canceled before new ones start. +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.2 + - @backstage/core-components@0.18.2 + - @backstage/frontend-plugin-api@0.12.1 + - @backstage/plugin-search-react@1.9.5 + - @backstage/core-compat-api@0.5.3 + - @backstage/core-plugin-api@1.11.1 + - @backstage/plugin-search-common@1.2.20 + +## @backstage/plugin-search-backend@2.0.7 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.13.0 + - @backstage/config@1.3.5 + - @backstage/backend-openapi-utils@0.6.2 + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-permission-common@0.9.2 + - @backstage/plugin-permission-node@0.10.5 + - @backstage/plugin-search-backend-node@1.3.16 + - @backstage/plugin-search-common@1.2.20 + +## @backstage/plugin-search-backend-module-catalog@0.3.9 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.5 + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-catalog-common@1.1.6 + - @backstage/plugin-catalog-node@1.19.1 + - @backstage/plugin-permission-common@0.9.2 + - @backstage/plugin-search-backend-node@1.3.16 + - @backstage/plugin-search-common@1.2.20 + +## @backstage/plugin-search-backend-module-elasticsearch@1.7.7 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.5 + - @backstage/backend-plugin-api@1.4.4 + - @backstage/integration-aws-node@0.1.18 + - @backstage/plugin-search-backend-node@1.3.16 + - @backstage/plugin-search-common@1.2.20 + +## @backstage/plugin-search-backend-module-explore@0.3.8 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.5 + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-search-backend-node@1.3.16 + - @backstage/plugin-search-common@1.2.20 + +## @backstage/plugin-search-backend-module-pg@0.5.49 + +### Patch Changes + +- a919ca3: Truncate long docs to fit PG index size limit +- 8d15a51: Added the < character to the query filter regexp +- Updated dependencies + - @backstage/config@1.3.5 + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-search-backend-node@1.3.16 + - @backstage/plugin-search-common@1.2.20 + +## @backstage/plugin-search-backend-module-stack-overflow-collator@0.3.14 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.5 + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-search-backend-node@1.3.16 + - @backstage/plugin-search-common@1.2.20 + +## @backstage/plugin-search-backend-module-techdocs@0.4.7 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.5 + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-catalog-common@1.1.6 + - @backstage/plugin-catalog-node@1.19.1 + - @backstage/plugin-permission-common@0.9.2 + - @backstage/plugin-search-backend-node@1.3.16 + - @backstage/plugin-search-common@1.2.20 + - @backstage/plugin-techdocs-node@1.13.8 + +## @backstage/plugin-search-backend-node@1.3.16 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.5 + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-permission-common@0.9.2 + - @backstage/plugin-search-common@1.2.20 + +## @backstage/plugin-search-common@1.2.20 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-common@0.9.2 + +## @backstage/plugin-search-react@1.9.5 + +### Patch Changes + +- 67a3e1a: Implemented AbortController request cancellation for overlapping search requests. This change ensures that when users type quickly, previous search requests are properly canceled before new ones start. +- Updated dependencies + - @backstage/core-components@0.18.2 + - @backstage/frontend-plugin-api@0.12.1 + - @backstage/theme@0.7.0 + - @backstage/core-plugin-api@1.11.1 + - @backstage/plugin-search-common@1.2.20 + +## @backstage/plugin-signals@0.0.24 + +### Patch Changes + +- 7b41d9d: Remove `app-root-element:signals/signals-display` which was not doing anything useful +- Updated dependencies + - @backstage/core-components@0.18.2 + - @backstage/frontend-plugin-api@0.12.1 + - @backstage/theme@0.7.0 + - @backstage/core-compat-api@0.5.3 + - @backstage/core-plugin-api@1.11.1 + - @backstage/plugin-signals-react@0.0.16 + +## @backstage/plugin-signals-backend@0.3.9 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.5 + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-auth-node@0.6.8 + - @backstage/plugin-events-node@0.4.16 + - @backstage/plugin-signals-node@0.1.25 + +## @backstage/plugin-signals-node@0.1.25 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.5 + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-auth-node@0.6.8 + - @backstage/plugin-events-node@0.4.16 + +## @backstage/plugin-signals-react@0.0.16 + +### Patch Changes + +- Updated dependencies + - @backstage/core-plugin-api@1.11.1 + +## @backstage/plugin-techdocs@1.15.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.2 + - @backstage/core-components@0.18.2 + - @backstage/integration@1.18.1 + - @backstage/frontend-plugin-api@0.12.1 + - @backstage/plugin-search-react@1.9.5 + - @backstage/config@1.3.5 + - @backstage/theme@0.7.0 + - @backstage/core-compat-api@0.5.3 + - @backstage/plugin-techdocs-react@1.3.4 + - @backstage/core-plugin-api@1.11.1 + - @backstage/integration-react@1.2.11 + - @backstage/plugin-auth-react@0.1.20 + - @backstage/plugin-search-common@1.2.20 + +## @backstage/plugin-techdocs-addons-test-utils@1.1.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.2 + - @backstage/plugin-search-react@1.9.5 + - @backstage/plugin-catalog@1.31.4 + - @backstage/test-utils@1.7.12 + - @backstage/plugin-techdocs@1.15.1 + - @backstage/plugin-techdocs-react@1.3.4 + - @backstage/core-app-api@1.19.1 + - @backstage/core-plugin-api@1.11.1 + - @backstage/integration-react@1.2.11 + +## @backstage/plugin-techdocs-backend@2.1.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.13.0 + - @backstage/integration@1.18.1 + - @backstage/config@1.3.5 + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-catalog-common@1.1.6 + - @backstage/plugin-catalog-node@1.19.1 + - @backstage/plugin-permission-common@0.9.2 + - @backstage/plugin-search-backend-module-techdocs@0.4.7 + - @backstage/plugin-techdocs-node@1.13.8 + +## @backstage/plugin-techdocs-module-addons-contrib@1.1.29 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2 + - @backstage/integration@1.18.1 + - @backstage/frontend-plugin-api@0.12.1 + - @backstage/plugin-techdocs-react@1.3.4 + - @backstage/core-plugin-api@1.11.1 + - @backstage/integration-react@1.2.11 + +## @backstage/plugin-techdocs-node@1.13.8 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.18.1 + - @backstage/config@1.3.5 + - @backstage/backend-plugin-api@1.4.4 + - @backstage/integration-aws-node@0.1.18 + - @backstage/plugin-search-common@1.2.20 + +## @backstage/plugin-techdocs-react@1.3.4 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2 + - @backstage/frontend-plugin-api@0.12.1 + - @backstage/config@1.3.5 + - @backstage/core-plugin-api@1.11.1 + +## @backstage/plugin-user-settings@0.8.27 + +### Patch Changes + +- 52fa068: Added user settings storage API blueprint +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.2 + - @backstage/core-components@0.18.2 + - @backstage/frontend-plugin-api@0.12.1 + - @backstage/theme@0.7.0 + - @backstage/core-compat-api@0.5.3 + - @backstage/core-app-api@1.19.1 + - @backstage/core-plugin-api@1.11.1 + - @backstage/plugin-signals-react@0.0.16 + +## @backstage/plugin-user-settings-backend@0.3.7 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.13.0 + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-auth-node@0.6.8 + - @backstage/plugin-signals-node@0.1.25 + +## example-app@0.2.114 + +### Patch Changes + +- Updated dependencies + - @backstage/ui@0.8.0 + - @backstage/plugin-mui-to-bui@0.2.0 + - @backstage/plugin-catalog-graph@0.5.2 + - @backstage/plugin-scaffolder-react@1.19.2 + - @backstage/plugin-catalog-react@1.21.2 + - @backstage/core-components@0.18.2 + - @backstage/plugin-signals@0.0.24 + - @backstage/cli@0.34.4 + - @backstage/plugin-home@0.8.13 + - @backstage/plugin-org@0.6.45 + - @backstage/plugin-scaffolder@1.34.2 + - @backstage/plugin-search-react@1.9.5 + - @backstage/plugin-search@1.4.31 + - @backstage/plugin-notifications@0.5.10 + - @backstage/plugin-api-docs@0.13.0 + - @backstage/config@1.3.5 + - @backstage/plugin-user-settings@0.8.27 + - @backstage/theme@0.7.0 + - @backstage/plugin-kubernetes-cluster@0.0.30 + - @backstage/plugin-kubernetes@0.12.12 + - @backstage/frontend-app-api@0.13.1 + - @backstage/plugin-catalog@1.31.4 + - @backstage/app-defaults@1.7.1 + - @backstage/plugin-techdocs@1.15.1 + - @backstage/plugin-techdocs-react@1.3.4 + - @backstage/core-app-api@1.19.1 + - @backstage/core-plugin-api@1.11.1 + - @backstage/integration-react@1.2.11 + - @backstage/plugin-auth-react@0.1.20 + - @backstage/plugin-catalog-common@1.1.6 + - @backstage/plugin-catalog-import@0.13.6 + - @backstage/plugin-catalog-unprocessed-entities@0.2.22 + - @backstage/plugin-devtools@0.1.32 + - @backstage/plugin-permission-react@0.4.37 + - @backstage/plugin-search-common@1.2.20 + - @backstage/plugin-techdocs-module-addons-contrib@1.1.29 + +## example-app-next@0.0.28 + +### Patch Changes + +- Updated dependencies + - @backstage/ui@0.8.0 + - @backstage/plugin-catalog-graph@0.5.2 + - @backstage/plugin-scaffolder-react@1.19.2 + - @backstage/plugin-catalog-react@1.21.2 + - @backstage/core-components@0.18.2 + - @backstage/plugin-app@0.3.1 + - @backstage/plugin-signals@0.0.24 + - @backstage/cli@0.34.4 + - @backstage/plugin-home@0.8.13 + - @backstage/plugin-org@0.6.45 + - @backstage/frontend-plugin-api@0.12.1 + - @backstage/plugin-scaffolder@1.34.2 + - @backstage/plugin-search-react@1.9.5 + - @backstage/plugin-search@1.4.31 + - @backstage/plugin-notifications@0.5.10 + - @backstage/plugin-api-docs@0.13.0 + - @backstage/config@1.3.5 + - @backstage/plugin-user-settings@0.8.27 + - @backstage/plugin-app-visualizer@0.1.24 + - @backstage/theme@0.7.0 + - @backstage/plugin-kubernetes-cluster@0.0.30 + - @backstage/plugin-kubernetes@0.12.12 + - @backstage/core-compat-api@0.5.3 + - @backstage/frontend-app-api@0.13.1 + - @backstage/plugin-catalog@1.31.4 + - @backstage/app-defaults@1.7.1 + - @backstage/plugin-auth@0.1.1 + - @backstage/plugin-techdocs@1.15.1 + - @backstage/plugin-techdocs-react@1.3.4 + - @backstage/core-app-api@1.19.1 + - @backstage/core-plugin-api@1.11.1 + - @backstage/frontend-defaults@0.3.2 + - @backstage/integration-react@1.2.11 + - @backstage/plugin-auth-react@0.1.20 + - @backstage/plugin-catalog-common@1.1.6 + - @backstage/plugin-catalog-import@0.13.6 + - @backstage/plugin-catalog-unprocessed-entities@0.2.22 + - @backstage/plugin-permission-react@0.4.37 + - @backstage/plugin-search-common@1.2.20 + - @backstage/plugin-techdocs-module-addons-contrib@1.1.29 + +## app-next-example-plugin@0.0.28 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2 + - @backstage/frontend-plugin-api@0.12.1 + +## example-backend@0.0.43 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@3.1.2 + - @backstage/backend-defaults@0.13.0 + - @backstage/plugin-scaffolder-backend-module-notifications@0.1.15 + - @backstage/plugin-scaffolder-backend-module-github@0.9.1 + - @backstage/plugin-scaffolder-backend@3.0.0 + - @backstage/plugin-notifications-backend@0.5.11 + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-app-backend@0.5.7 + - @backstage/plugin-auth-backend@0.25.5 + - @backstage/plugin-auth-backend-module-github-provider@0.3.8 + - @backstage/plugin-auth-backend-module-guest-provider@0.2.13 + - @backstage/plugin-auth-backend-module-openshift-provider@0.1.1 + - @backstage/plugin-auth-node@0.6.8 + - @backstage/plugin-catalog-backend-module-backstage-openapi@0.5.7 + - @backstage/plugin-catalog-backend-module-openapi@0.2.15 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.2.13 + - @backstage/plugin-catalog-backend-module-unprocessed@0.6.5 + - @backstage/plugin-devtools-backend@0.5.10 + - @backstage/plugin-events-backend@0.5.7 + - @backstage/plugin-events-backend-module-google-pubsub@0.1.5 + - @backstage/plugin-kubernetes-backend@0.20.3 + - @backstage/plugin-mcp-actions-backend@0.1.4 + - @backstage/plugin-permission-backend@0.7.5 + - @backstage/plugin-permission-backend-module-allow-all-policy@0.2.13 + - @backstage/plugin-permission-common@0.9.2 + - @backstage/plugin-permission-node@0.10.5 + - @backstage/plugin-proxy-backend@0.6.7 + - @backstage/plugin-search-backend@2.0.7 + - @backstage/plugin-search-backend-module-catalog@0.3.9 + - @backstage/plugin-search-backend-module-explore@0.3.8 + - @backstage/plugin-search-backend-module-techdocs@0.4.7 + - @backstage/plugin-search-backend-node@1.3.16 + - @backstage/plugin-signals-backend@0.3.9 + - @backstage/plugin-techdocs-backend@2.1.1 + +## e2e-test@0.2.33 + +### Patch Changes + +- Updated dependencies + - @backstage/create-app@0.7.5 + +## @internal/frontend@0.0.14 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.12.1 + +## @internal/scaffolder@0.0.14 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-react@1.19.2 + - @backstage/frontend-plugin-api@0.12.1 + +## techdocs-cli-embedded-app@0.2.113 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2 + - @backstage/cli@0.34.4 + - @backstage/config@1.3.5 + - @backstage/theme@0.7.0 + - @backstage/plugin-catalog@1.31.4 + - @backstage/app-defaults@1.7.1 + - @backstage/test-utils@1.7.12 + - @backstage/plugin-techdocs@1.15.1 + - @backstage/plugin-techdocs-react@1.3.4 + - @backstage/core-app-api@1.19.1 + - @backstage/core-plugin-api@1.11.1 + - @backstage/integration-react@1.2.11 + +## @internal/plugin-todo-list@1.0.44 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2 + - @backstage/core-plugin-api@1.11.1 + +## @internal/plugin-todo-list-backend@1.0.44 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4 + +## @internal/plugin-todo-list-common@1.0.27 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-common@0.9.2 diff --git a/docs/releases/v1.44.0-next.2-changelog.md b/docs/releases/v1.44.0-next.2-changelog.md new file mode 100644 index 0000000000..74b467ebba --- /dev/null +++ b/docs/releases/v1.44.0-next.2-changelog.md @@ -0,0 +1,2298 @@ +# Release v1.44.0-next.2 + +Upgrade Helper: [https://backstage.github.io/upgrade-helper/?to=1.44.0-next.2](https://backstage.github.io/upgrade-helper/?to=1.44.0-next.2) + +## @backstage/config@1.3.5-next.0 + +# @backstage/config + +## 1.3.4-next.0 + +### Patch Changes + +- b45b094: Allow colon to be used as config key. + +## 1.3.3 + +### Patch Changes + +- ff23618: Loosen the requirements for a key to be considered valid config. +- 3507fcd: Just some more circular dep cleanup + +## 1.3.3-next.0 + +### Patch Changes + +- ff23618: Loosen the requirements for a key to be considered valid config. +- 3507fcd: Just some more circular dep cleanup + +## 1.3.2 + +### Patch Changes + +- Updated dependencies + - @backstage/types@1.2.1 + - @backstage/errors@1.2.7 + +## 1.3.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/types@1.2.1-next.0 + - @backstage/errors@1.2.7-next.0 + +## 1.3.1 + +### Patch Changes + +- Updated dependencies + - @backstage/errors@1.2.6 + - @backstage/types@1.2.0 + +## 1.3.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/errors@1.2.6-next.0 + - @backstage/types@1.2.0 + +## 1.3.0 + +### Minor Changes + +- d52d7f9: Make `readDurationFromConfig` support both ISO and ms formats as well, to make it easier to enter time as an end user + +### Patch Changes + +- Updated dependencies + - @backstage/types@1.2.0 + - @backstage/errors@1.2.5 + +## 1.2.0 + +### Minor Changes + +- 50cf9df: The `ConfigReader` now treats `null` values as present but explicitly undefined, meaning it will not fall back to the next level of configuration. + +### Patch Changes + +- Updated dependencies + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + +## 1.2.0-next.1 + +### Minor Changes + +- 50cf9df: The `ConfigReader` now treats `null` values as present but explicitly undefined, meaning it will not fall back to the next level of configuration. + +### Patch Changes + +- Updated dependencies + - @backstage/errors@1.2.4-next.0 + - @backstage/types@1.1.1 + +## 1.1.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/errors@1.2.4-next.0 + - @backstage/types@1.1.1 + +## 1.1.1 + +### Patch Changes + +- Updated dependencies + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + +## 1.1.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/errors@1.2.3-next.0 + - @backstage/types@1.1.1 + +## 1.1.0 + +### Minor Changes + +- 62f448edb0b5: Added a `readDurationFromConfig` function + +### Patch Changes + +- 406b786a2a2c: Mark package as being free of side effects, allowing more optimized Webpack builds. +- 8cec7664e146: Removed `@types/node` dependency +- Updated dependencies + - @backstage/errors@1.2.2 + - @backstage/types@1.1.1 + +## 1.1.0-next.2 + +### Patch Changes + +- 406b786a2a2c: Mark package as being free of side effects, allowing more optimized Webpack builds. +- Updated dependencies + - @backstage/errors@1.2.2-next.0 + - @backstage/types@1.1.1-next.0 + +## 1.1.0-next.1 + +### Patch Changes + +- 8cec7664e146: Removed `@types/node` dependency +- Updated dependencies + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + +## 1.1.0-next.0 + +### Minor Changes + +- 62f448edb0b5: Added a `readDurationFromConfig` function + +### Patch Changes + +- Updated dependencies + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + +## 1.0.8 + +### Patch Changes + +- Updated dependencies + - @backstage/types@1.1.0 + +## 1.0.7 + +### Patch Changes + +- 482dae5de1c: Updated link to docs. +- Updated dependencies + - @backstage/types@1.0.2 + +## 1.0.7-next.0 + +### Patch Changes + +- 482dae5de1c: Updated link to docs. +- Updated dependencies + - @backstage/types@1.0.2 + +## 1.0.6 + +### Patch Changes + +- ba2d69ee17: Adds the ability to coerce values to their boolean representatives. + Values such as `"true"` `1` `on` and `y` will become `true` when using `getBoolean` and the opposites `false`. + This happens particularly when such parameters are used with environmental substitution as environment variables are always strings. +- Updated dependencies + - @backstage/types@1.0.2 + +## 1.0.6-next.0 + +### Patch Changes + +- ba2d69ee17: Adds the ability to coerce values to their boolean representatives. + Values such as `"true"` `1` `on` and `y` will become `true` when using `getBoolean` and the opposites `false`. + This happens particularly when such parameters are used with environmental substitution as environment variables are always strings. +- Updated dependencies + - @backstage/types@1.0.2 + +## 1.0.5 + +### Patch Changes + +- Updated dependencies + - @backstage/types@1.0.2 + +## 1.0.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/types@1.0.2-next.1 + +## 1.0.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/types@1.0.2-next.0 + +## 1.0.4 + +### Patch Changes + +- Updated dependencies + - @backstage/types@1.0.1 + +## 1.0.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/types@1.0.1-next.0 + +## 1.0.3 + +### Patch Changes + +- Updated dependencies + - @backstage/types@1.0.0 + +## 1.0.3-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/types@1.0.0 + +## 1.0.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/types@1.0.0 + +## 1.0.3-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/types@1.0.0 + +## 1.0.2 + +### Patch Changes + +- 7d47def9c4: Removed dependency on `@types/jest`. + +## 1.0.2-next.0 + +### Patch Changes + +- 7d47def9c4: Removed dependency on `@types/jest`. + +## 1.0.1 + +### Patch Changes + +- 6e830352d4: Updated dependency `@types/node` to `^16.0.0`. + +## 1.0.1-next.0 + +### Patch Changes + +- 6e830352d4: Updated dependency `@types/node` to `^16.0.0`. + +## 1.0.0 + +### Major Changes + +- b58c70c223: This package has been promoted to v1.0! To understand how this change affects the package, please check out our [versioning policy](https://backstage.io/docs/overview/versioning-policy). + +### Patch Changes + +- Updated dependencies + - @backstage/types@1.0.0 + +## 0.1.15 + +### Patch Changes + +- Fix for the previous release with missing type declarations. +- Updated dependencies + - @backstage/types@0.1.3 + +## 0.1.14 + +### Patch Changes + +- c77c5c7eb6: Added `backstage.role` to `package.json` +- Updated dependencies + - @backstage/types@0.1.2 + +## 0.1.13 + +### Patch Changes + +- f685e1398f: Loading of app configurations now reference the `@deprecated` construct from + JSDoc to determine if a property in-use has been deprecated. Users are notified + of deprecated keys in the format: + + ```txt + The configuration key 'catalog.processors.githubOrg' of app-config.yaml is deprecated and may be removed soon. Configure a GitHub integration instead. + ``` + + When the `withDeprecatedKeys` option is set to `true` in the `process` method + of `loadConfigSchema`, the user will be notified that deprecated keys have been + identified in their app configuration. + + The `backend-common` and `plugin-app-backend` packages have been updated to set + `withDeprecatedKeys` to true so that users are notified of deprecated settings + by default. + +## 0.1.13-next.0 + +### Patch Changes + +- f685e1398f: Loading of app configurations now reference the `@deprecated` construct from + JSDoc to determine if a property in-use has been deprecated. Users are notified + of deprecated keys in the format: + + ```txt + The configuration key 'catalog.processors.githubOrg' of app-config.yaml is deprecated and may be removed soon. Configure a GitHub integration instead. + ``` + + When the `withDeprecatedKeys` option is set to `true` in the `process` method + of `loadConfigSchema`, the user will be notified that deprecated keys have been + identified in their app configuration. + + The `backend-common` and `plugin-app-backend` packages have been updated to set + `withDeprecatedKeys` to true so that users are notified of deprecated settings + by default. + +## 0.1.12 + +### Patch Changes + +- f5343e7c1a: The `ConfigReader#get` method now always returns a deep clone of the configuration data. + +## 0.1.11 + +### Patch Changes + +- 10d267a1b7: Minor exports cleanup +- 41c49884d2: Start using the new `@backstage/types` package. Initially, this means using the `Observable` and `Json*` types from there. The types also remain in their old places but deprecated, and will be removed in a future release. +- 925a967f36: Replace usage of test-utils-core with test-utils + +## 0.1.10 + +### Patch Changes + +- febddedcb2: Bump `lodash` to remediate `SNYK-JS-LODASH-590103` security vulnerability + +## 0.1.9 + +### Patch Changes + +- f88b2c7db: Documented `Config` interface and mark types as public. + +## 0.1.8 + +### Patch Changes + +- 47113f1f1: Only warn once per key when trying to read visibility-filtered values + +## 0.1.7 + +### Patch Changes + +- 90f25476a: Extended the `Config` interface to have an optional `subscribe` method that can be used be notified of updates to the configuration. + +## 0.1.6 + +### Patch Changes + +- e9d3983ee: Add warning when trying to access configuration values that have been filtered out by visibility. + +## 0.1.5 + +### Patch Changes + +- d8b81fd28: Bump `json-schema` dependency from `0.2.5` to `0.3.0`. + +## 0.1.4 + +### Patch Changes + +- 0434853a5: Reformulate the json types to break type recursion + +## 0.1.3 + +### Patch Changes + +- a1f5e6545: Adds an optional type to `config.get` & `config.getOptional`. This avoids the need for casting. For example: + + ```ts + const config = useApi(configApiRef); + + const myConfig = config.get('myPlugin.complexConfig'); + // vs + const myConfig config.get('myPlugin.complexConfig') as SomeTypeDefinition; + ``` + +## 0.1.2 + +### Patch Changes + +- e3bd9fc2f: Fix unneeded defensive code +- e3bd9fc2f: Fix useless conditional + +## @backstage/config-loader@1.10.5-next.0 + +# @backstage/config-loader + +## 1.10.4-next.0 + +### Patch Changes + +- b45b094: Allow colon to be used as config key. +- Updated dependencies + - @backstage/config@1.3.4-next.0 + +## 1.10.3 + +### Patch Changes + +- a73f495: Allow using `BACKSTAGE_ENV` for loading environment specific config files +- Updated dependencies + - @backstage/types@1.2.2 + +## 1.10.3-next.0 + +### Patch Changes + +- a73f495: Allow using `BACKSTAGE_ENV` for loading environment specific config files + +## 1.10.2 + +### Patch Changes + +- ff23618: Loosen the requirements for a key to be considered valid config. +- Updated dependencies + - @backstage/config@1.3.3 + +## 1.10.2-next.0 + +### Patch Changes + +- ff23618: Loosen the requirements for a key to be considered valid config. +- Updated dependencies + - @backstage/config@1.3.3-next.0 + +## 1.10.1 + +### Patch Changes + +- 72d019d: Removed various typos +- Updated dependencies + - @backstage/config@1.3.2 + - @backstage/cli-common@0.1.15 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + +## 1.10.1-next.0 + +### Patch Changes + +- 72d019d: Removed various typos +- Updated dependencies + - @backstage/cli-common@0.1.15 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + +## 1.10.0 + +### Minor Changes + +- 2fd73aa: The include transforms applied during config loading will now only apply to the known keys `$file`, `$env`, and `$include`. Any other key that begins with a \`# @backstage/config-loader will now be passed through as is. + +### Patch Changes + +- f422984: Added `@types/minimist` to `devDependencies` +- Updated dependencies + - @backstage/cli-common@0.1.15 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + +## 1.10.0-next.0 + +### Minor Changes + +- 2fd73aa: The include transforms applied during config loading will now only apply to the known keys `$file`, `$env`, and `$include`. Any other key that begins with a \`# @backstage/config-loader will now be passed through as is. + +### Patch Changes + +- Updated dependencies + - @backstage/cli-common@0.1.15 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + +## 1.9.6 + +### Patch Changes + +- f866b86: Internal refactor to use explicit `require` for lazy-loading dependency. +- Updated dependencies + - @backstage/cli-common@0.1.15 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + +## 1.9.6-next.0 + +### Patch Changes + +- f866b86: Internal refactor to use explicit `require` for lazy-loading dependency. +- Updated dependencies + - @backstage/cli-common@0.1.15 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + +## 1.9.5 + +### Patch Changes + +- 8ecf8cb: Exclude `@backstage/backend-common` from schema collection if `@backstage/backend-defaults` is present +- Updated dependencies + - @backstage/types@1.2.1 + - @backstage/cli-common@0.1.15 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + +## 1.9.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/types@1.2.1-next.0 + - @backstage/config@1.3.2-next.0 + - @backstage/errors@1.2.7-next.0 + - @backstage/cli-common@0.1.15 + +## 1.9.5-next.0 + +### Patch Changes + +- 8ecf8cb: Exclude `@backstage/backend-common` from schema collection if `@backstage/backend-defaults` is present +- Updated dependencies + - @backstage/cli-common@0.1.15 + - @backstage/config@1.3.1 + - @backstage/errors@1.2.6 + - @backstage/types@1.2.0 + +## 1.9.3 + +### Patch Changes + +- 5c9cc05: Use native fetch instead of node-fetch +- Updated dependencies + - @backstage/errors@1.2.6 + - @backstage/cli-common@0.1.15 + - @backstage/config@1.3.1 + - @backstage/types@1.2.0 + +## 1.9.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/errors@1.2.6-next.0 + - @backstage/cli-common@0.1.15 + - @backstage/config@1.3.1-next.0 + - @backstage/types@1.2.0 + +## 1.9.3-next.0 + +### Patch Changes + +- 5c9cc05: Use native fetch instead of node-fetch +- Updated dependencies + - @backstage/cli-common@0.1.15 + - @backstage/config@1.3.0 + - @backstage/errors@1.2.5 + - @backstage/types@1.2.0 + +## 1.9.2 + +### Patch Changes + +- c5e39e7: Internal refactor to use the deferred from the types package +- Updated dependencies + - @backstage/config@1.3.0 + - @backstage/types@1.2.0 + - @backstage/cli-common@0.1.15 + - @backstage/errors@1.2.5 + +## 1.9.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/cli-common@0.1.15-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + +## 1.9.1 + +### Patch Changes + +- ef3c507: Updated dependency `typescript-json-schema` to `^0.65.0`. +- Updated dependencies + - @backstage/cli-common@0.1.14 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + +## 1.9.1-next.0 + +### Patch Changes + +- ef3c507: Updated dependency `typescript-json-schema` to `^0.65.0`. +- Updated dependencies + - @backstage/cli-common@0.1.14 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + +## 1.9.0 + +### Minor Changes + +- 274428f: Add configuration key to File and Remote `ConfigSource`s that enables configuration of parsing logic. Previously limited to yaml, these `ConfigSource`s now allow for a multitude of parsing options (e.g. JSON). + +### Patch Changes + +- 93095ee: Make sure node-fetch is version 2.7.0 or greater +- 1edd6c2: The `env` option of `ConfigSources.default` now correctly allows undefined members. +- 493feac: Add boolean `allowMissingDefaultConfig` option to `ConfigSources.default` and + `ConfigSources.defaultForTargets`, which results in omission of a ConfigSource + for the default app-config.yaml configuration file if it's not present. +- Updated dependencies + - @backstage/cli-common@0.1.14 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + +## 1.9.0-next.2 + +### Patch Changes + +- 93095ee: Make sure node-fetch is version 2.7.0 or greater +- Updated dependencies + - @backstage/cli-common@0.1.14 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + +## 1.9.0-next.1 + +### Minor Changes + +- 274428f: Add configuration key to File and Remote `ConfigSource`s that enables configuration of parsing logic. Previously limited to yaml, these `ConfigSource`s now allow for a multitude of parsing options (e.g. JSON). + +### Patch Changes + +- 1edd6c2: The `env` option of `ConfigSources.default` now correctly allows undefined members. +- Updated dependencies + - @backstage/cli-common@0.1.14 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + +## 1.8.2-next.0 + +### Patch Changes + +- 493feac: Add boolean `allowMissingDefaultConfig` option to `ConfigSources.default` and + `ConfigSources.defaultForTargets`, which results in omission of a ConfigSource + for the default app-config.yaml configuration file if it's not present. +- Updated dependencies + - @backstage/cli-common@0.1.14 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + +## 1.8.1 + +### Patch Changes + +- Updated dependencies + - @backstage/cli-common@0.1.14 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + +## 1.8.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/cli-common@0.1.14-next.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + +## 1.8.0 + +### Minor Changes + +- 2ce31b3: The default environment variable substitution function will now trim whitespace characters from the substituted value. This alleviates bugs where whitespace characters are mistakenly included in environment variables. + + If you depend on the old behavior, you can override the default substitution function with your own, for example: + + ```ts + ConfigSources.default({ + substitutionFunc: async name => process.env[name], + }); + ``` + +- 99bab65: Support parameter substitution for environment variables + +### Patch Changes + +- Updated dependencies + - @backstage/cli-common@0.1.13 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + +## 1.8.0-next.0 + +### Minor Changes + +- 99bab65: Support parameter substitution for environment variables + +### Patch Changes + +- Updated dependencies + - @backstage/cli-common@0.1.13 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/types@1.1.1 + +## 1.7.0 + +### Minor Changes + +- db8358d: Forward `null` values read from configuration files in configuration data, rather than treating them as an absence of config. + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/cli-common@0.1.13 + - @backstage/types@1.1.1 + +## 1.7.0-next.1 + +### Minor Changes + +- db8358d: Forward `null` values read from configuration files in configuration data, rather than treating them as an absence of config. + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.2.0-next.1 + - @backstage/cli-common@0.1.13 + - @backstage/errors@1.2.4-next.0 + - @backstage/types@1.1.1 + +## 1.6.3-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/errors@1.2.4-next.0 + - @backstage/config@1.1.2-next.0 + - @backstage/cli-common@0.1.13 + - @backstage/types@1.1.1 + +## 1.6.2 + +### Patch Changes + +- 0a9a03c: Make schema processing gracefully handle an empty config. +- 6bb6f3e: Updated dependency `fs-extra` to `^11.2.0`. + Updated dependency `@types/fs-extra` to `^11.0.0`. +- bf3da16: Updated dependency `typescript-json-schema` to `^0.63.0`. +- Updated dependencies + - @backstage/cli-common@0.1.13 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + +## 1.6.2-next.0 + +### Patch Changes + +- 0a9a03c: Make schema processing gracefully handle an empty config. +- Updated dependencies + - @backstage/cli-common@0.1.13 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + +## 1.6.1 + +### Patch Changes + +- 7acbb5a: Removed `mock-fs` dev dependency. +- Updated dependencies + - @backstage/cli-common@0.1.13 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + +## 1.6.1-next.0 + +### Patch Changes + +- 7acbb5a: Removed `mock-fs` dev dependency. +- Updated dependencies + - @backstage/config@1.1.1 + - @backstage/cli-common@0.1.13 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + +## 1.6.0 + +### Minor Changes + +- 24f5a85: Add "path" to `TransformFunc` context + +### Patch Changes + +- Updated dependencies + - @backstage/cli-common@0.1.13 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + +## 1.6.0-next.0 + +### Minor Changes + +- 24f5a85: Add "path" to `TransformFunc` context + +### Patch Changes + +- Updated dependencies + - @backstage/cli-common@0.1.13 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + +## 1.5.3 + +### Patch Changes + +- 22ca64f117: Correctly resolve config targets into absolute paths +- 087bab5b42: Updated dependency `typescript-json-schema` to `^0.62.0`. +- Updated dependencies + - @backstage/cli-common@0.1.13 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + +## 1.5.2-next.0 + +### Patch Changes + +- 22ca64f117: Correctly resolve config targets into absolute paths +- Updated dependencies + - @backstage/cli-common@0.1.13 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + +## 1.5.1 + +### Patch Changes + +- 0b55f773a7: Removed some unused dependencies +- 30c553c1d2: Updated dependency `typescript-json-schema` to `^0.61.0`. +- 773ea341d2: The `FileConfigSource` will now retry file reading after a short delay if it reads an empty file. This is to avoid flakiness during watch mode where change events can trigger before the file content has been written. +- a4617c422a: Added `watch` option to configuration loaders that can be used to disable file watching by setting it to `false`. +- Updated dependencies + - @backstage/errors@1.2.3 + - @backstage/cli-common@0.1.13 + - @backstage/config@1.1.1 + - @backstage/types@1.1.1 + +## 1.5.1-next.1 + +### Patch Changes + +- 0b55f773a7: Removed some unused dependencies +- 30c553c1d2: Updated dependency `typescript-json-schema` to `^0.61.0`. +- a4617c422a: Added `watch` option to configuration loaders that can be used to disable file watching by setting it to `false`. +- Updated dependencies + - @backstage/errors@1.2.3-next.0 + - @backstage/cli-common@0.1.13-next.0 + - @backstage/config@1.1.1-next.0 + - @backstage/types@1.1.1 + +## 1.5.1-next.0 + +### Patch Changes + +- 773ea341d2: The `FileConfigSource` will now retry file reading after a short delay if it reads an empty file. This is to avoid flakiness during watch mode where change events can trigger before the file content has been written. +- Updated dependencies + - @backstage/cli-common@0.1.13-next.0 + - @backstage/config@1.1.0 + - @backstage/errors@1.2.2 + - @backstage/types@1.1.1 + +## 1.5.0 + +### Minor Changes + +- 9606ba0939e6: Deep visibility now also applies to values that are not covered by the configuration schema. + + For example, given the following configuration schema: + + ```ts + // plugins/a/config.schema.ts + export interface Config { + /** @deepVisibility frontend */ + a?: unknown; + } + + // plugins/a/config.schema.ts + export interface Config { + a?: { + b?: string; + }; + } + ``` + + All values under `a` are now visible to the frontend, while previously only `a` and `a/b` would've been visible. + +### Patch Changes + +- 8cec7664e146: Removed `@types/node` dependency +- f9657b891b00: Do not unnecessarily notify subscribers when no-op updates to config happen +- Updated dependencies + - @backstage/config@1.1.0 + - @backstage/errors@1.2.2 + - @backstage/types@1.1.1 + - @backstage/cli-common@0.1.12 + +## 1.5.0-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.1.0-next.2 + - @backstage/errors@1.2.2-next.0 + - @backstage/types@1.1.1-next.0 + - @backstage/cli-common@0.1.12 + +## 1.5.0-next.2 + +### Patch Changes + +- 8cec7664e146: Removed `@types/node` dependency +- Updated dependencies + - @backstage/config@1.1.0-next.1 + - @backstage/cli-common@0.1.12 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + +## 1.5.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.1.0-next.0 + - @backstage/cli-common@0.1.12 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + +## 1.5.0-next.0 + +### Minor Changes + +- 9606ba0939e6: Deep visibility now also applies to values that are not covered by the configuration schema. + + For example, given the following configuration schema: + + ```ts + // plugins/a/config.schema.ts + export interface Config { + /** @deepVisibility frontend */ + a?: unknown; + } + + // plugins/a/config.schema.ts + export interface Config { + a?: { + b?: string; + }; + } + ``` + + All values under `a` are now visible to the frontend, while previously only `a` and `a/b` would've been visible. + +### Patch Changes + +- f9657b891b00: Do not unnecessarily notify subscribers when no-op updates to config happen +- Updated dependencies + - @backstage/cli-common@0.1.12 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + +## 1.4.0 + +### Minor Changes + +- 2f1859585998: Loading invalid TypeScript configuration schemas will now throw an error rather than silently being ignored. + + In particular this includes defining any additional types other than `Config` in the schema file, or use of unsupported types such as `Record` or `Partial`. + +- cd514545d1d0: Adds a new `deepVisibility` schema keyword that sets child visibility recursively to the defined value, respecting preexisting values or child `deepVisibility`. + + Example usage: + + ```ts + export interface Config { + /** + * Enforces a default of `secret` instead of `backend` for this object. + * @deepVisibility secret + */ + mySecretProperty: { + type: 'object'; + properties: { + secretValue: { + type: 'string'; + }; + + verySecretProperty: { + type: 'string'; + }; + }; + }; + } + ``` + + Example of a schema that would not be allowed: + + ```ts + export interface Config { + /** + * Set the top level property to secret, enforcing a default of `secret` instead of `backend` for this object. + * @deepVisibility secret + */ + mySecretProperty: { + type: 'object'; + properties: { + frontendUrl: { + /** + * We can NOT override the visibility to reveal a property to the front end. + * @visibility frontend + */ + type: 'string'; + }; + + verySecretProperty: { + type: 'string'; + }; + }; + }; + } + ``` + +### Patch Changes + +- Updated dependencies + - @backstage/cli-common@0.1.12 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + +## 1.4.0-next.1 + +### Minor Changes + +- 2f1859585998: Loading invalid TypeScript configuration schemas will now throw an error rather than silently being ignored. + + In particular this includes defining any additional types other than `Config` in the schema file, or use of unsupported types such as `Record` or `Partial`. + +### Patch Changes + +- Updated dependencies + - @backstage/cli-common@0.1.12 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + +## 1.4.0-next.0 + +### Minor Changes + +- cd514545d1d0: Adds a new `deepVisibility` schema keyword that sets child visibility recursively to the defined value, respecting preexisting values or child `deepVisibility`. + + Example usage: + + ```ts + export interface Config { + /** + * Enforces a default of `secret` instead of `backend` for this object. + * @deepVisibility secret + */ + mySecretProperty: { + type: 'object'; + properties: { + secretValue: { + type: 'string'; + }; + + verySecretProperty: { + type: 'string'; + }; + }; + }; + } + ``` + + Example of a schema that would not be allowed: + + ```ts + export interface Config { + /** + * Set the top level property to secret, enforcing a default of `secret` instead of `backend` for this object. + * @deepVisibility secret + */ + mySecretProperty: { + type: 'object'; + properties: { + frontendUrl: { + /** + * We can NOT override the visibility to reveal a property to the front end. + * @visibility frontend + */ + type: 'string'; + }; + + verySecretProperty: { + type: 'string'; + }; + }; + }; + } + ``` + +### Patch Changes + +- Updated dependencies + - @backstage/cli-common@0.1.12 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/types@1.1.0 + +## 1.3.2 + +### Patch Changes + +- Updated dependencies + - @backstage/errors@1.2.1 + - @backstage/cli-common@0.1.12 + - @backstage/config@1.0.8 + - @backstage/types@1.1.0 + +## 1.3.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/errors@1.2.1-next.0 + - @backstage/cli-common@0.1.12 + - @backstage/config@1.0.8 + - @backstage/types@1.1.0 + +## 1.3.1 + +### Patch Changes + +- f25427f665f7: Fix a bug where config items with `/` in the key were incorrectly handled. +- a5c5491ff50c: Use `durationToMilliseconds` from `@backstage/types` instead of our own +- Updated dependencies + - @backstage/types@1.1.0 + - @backstage/errors@1.2.0 + - @backstage/cli-common@0.1.12 + - @backstage/config@1.0.8 + +## 1.3.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/errors@1.2.0-next.0 + - @backstage/cli-common@0.1.12 + - @backstage/config@1.0.7 + - @backstage/types@1.0.2 + +## 1.3.1-next.0 + +### Patch Changes + +- f25427f665f7: Fix a bug where config items with `/` in the key were incorrectly handled. +- Updated dependencies + - @backstage/config@1.0.7 + - @backstage/cli-common@0.1.12 + - @backstage/errors@1.1.5 + - @backstage/types@1.0.2 + +## 1.3.0 + +### Minor Changes + +- 201206132da: Introduced a new config source system to replace `loadConfig`. There is a new `ConfigSource` interface along with utilities provided by `ConfigSources`, as well as a number of built-in configuration source implementations. The new system is more flexible and makes it easier to create new and reusable sources of configuration, such as loading configuration from secret providers. + + The following is an example of how to load configuration using the default behavior: + + ```ts + const source = ConfigSources.default({ + argv: options?.argv, + remote: options?.remote, + }); + const config = await ConfigSources.toConfig(source); + ``` + + The `ConfigSource` interface looks like this: + + ```ts + export interface ConfigSource { + readConfigData(options?: ReadConfigDataOptions): AsyncConfigSourceIterator; + } + ``` + + It is best implemented using an async iterator: + + ```ts + class MyConfigSource implements ConfigSource { + async *readConfigData() { + yield { + config: [ + { + context: 'example', + data: { backend: { baseUrl: 'http://localhost' } }, + }, + ], + }; + } + } + ``` + +### Patch Changes + +- 7c116bcac7f: Fixed the way that some request errors are thrown +- 473db605a4f: Added a new `noUndeclaredProperties` option to `SchemaLoader` to support enforcing that there are no extra keys when verifying config. +- Updated dependencies + - @backstage/cli-common@0.1.12 + - @backstage/config@1.0.7 + - @backstage/errors@1.1.5 + - @backstage/types@1.0.2 + +## 1.3.0-next.0 + +### Minor Changes + +- 201206132da: Introduced a new config source system to replace `loadConfig`. There is a new `ConfigSource` interface along with utilities provided by `ConfigSources`, as well as a number of built-in configuration source implementations. The new system is more flexible and makes it easier to create new and reusable sources of configuration, such as loading configuration from secret providers. + + The following is an example of how to load configuration using the default behavior: + + ```ts + const source = ConfigSources.default({ + argv: options?.argv, + remote: options?.remote, + }); + const config = await ConfigSources.toConfig(source); + ``` + + The `ConfigSource` interface looks like this: + + ```ts + export interface ConfigSource { + readConfigData(options?: ReadConfigDataOptions): AsyncConfigSourceIterator; + } + ``` + + It is best implemented using an async iterator: + + ```ts + class MyConfigSource implements ConfigSource { + async *readConfigData() { + yield { + config: [ + { + context: 'example', + data: { backend: { baseUrl: 'http://localhost' } }, + }, + ], + }; + } + } + ``` + +### Patch Changes + +- Updated dependencies + - @backstage/cli-common@0.1.12 + - @backstage/config@1.0.7 + - @backstage/errors@1.1.5 + - @backstage/types@1.0.2 + +## 1.2.0 + +### Minor Changes + +- c791fcd96b9: Configuration validation is now more permissive when it comes to config whose values are `string` but whose schemas declare them to be `boolean` or `number`. + + For example, configuration was previously marked invalid when a string `'true'` was set on a property expecting type `boolean` or a string `'146'` was set on a property expecting type `number` (as when providing configuration via variable substitution sourced from environment variables). Now, such configurations will be considered valid and their values will be coerced to the right type at read-time. + +### Patch Changes + +- Updated dependencies + - @backstage/cli-common@0.1.12 + - @backstage/config@1.0.7 + - @backstage/errors@1.1.5 + - @backstage/types@1.0.2 + +## 1.1.9 + +### Patch Changes + +- 52b0022dab7: Updated dependency `msw` to `^1.0.0`. +- 482dae5de1c: Updated link to docs. +- Updated dependencies + - @backstage/errors@1.1.5 + - @backstage/cli-common@0.1.12 + - @backstage/config@1.0.7 + - @backstage/types@1.0.2 + +## 1.1.9-next.0 + +### Patch Changes + +- 52b0022dab7: Updated dependency `msw` to `^1.0.0`. +- 482dae5de1c: Updated link to docs. +- Updated dependencies + - @backstage/errors@1.1.5-next.0 + - @backstage/cli-common@0.1.12-next.0 + - @backstage/config@1.0.7-next.0 + - @backstage/types@1.0.2 + +## 1.1.8 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.0.6 + - @backstage/cli-common@0.1.11 + - @backstage/errors@1.1.4 + - @backstage/types@1.0.2 + +## 1.1.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.0.6-next.0 + - @backstage/cli-common@0.1.11 + - @backstage/errors@1.1.4 + - @backstage/types@1.0.2 + +## 1.1.7 + +### Patch Changes + +- 3280711113: Updated dependency `msw` to `^0.49.0`. +- 40e7e6e1a2: Updated dependency `typescript-json-schema` to `^0.55.0`. +- Updated dependencies + - @backstage/errors@1.1.4 + - @backstage/types@1.0.2 + - @backstage/cli-common@0.1.11 + - @backstage/config@1.0.5 + +## 1.1.7-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/cli-common@0.1.11-next.0 + - @backstage/config@1.0.5-next.1 + - @backstage/errors@1.1.4-next.1 + - @backstage/types@1.0.2-next.1 + +## 1.1.7-next.1 + +### Patch Changes + +- 40e7e6e1a2: Updated dependency `typescript-json-schema` to `^0.55.0`. +- Updated dependencies + - @backstage/types@1.0.2-next.1 + - @backstage/config@1.0.5-next.1 + - @backstage/cli-common@0.1.10 + - @backstage/errors@1.1.4-next.1 + +## 1.1.7-next.0 + +### Patch Changes + +- 3280711113: Updated dependency `msw` to `^0.49.0`. +- Updated dependencies + - @backstage/types@1.0.2-next.0 + - @backstage/cli-common@0.1.10 + - @backstage/config@1.0.5-next.0 + - @backstage/errors@1.1.4-next.0 + +## 1.1.6 + +### Patch Changes + +- Updated dependencies + - @backstage/types@1.0.1 + - @backstage/cli-common@0.1.10 + - @backstage/config@1.0.4 + - @backstage/errors@1.1.3 + +## 1.1.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/types@1.0.1-next.0 + - @backstage/cli-common@0.1.10 + - @backstage/config@1.0.4-next.0 + - @backstage/errors@1.1.3-next.0 + +## 1.1.5 + +### Patch Changes + +- Updated dependencies + - @backstage/cli-common@0.1.10 + - @backstage/config@1.0.3 + - @backstage/errors@1.1.2 + - @backstage/types@1.0.0 + +## 1.1.5-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/cli-common@0.1.10 + - @backstage/config@1.0.3-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/types@1.0.0 + +## 1.1.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/cli-common@0.1.10 + - @backstage/config@1.0.3-next.1 + - @backstage/errors@1.1.2-next.1 + - @backstage/types@1.0.0 + +## 1.1.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/cli-common@0.1.10 + - @backstage/config@1.0.3-next.0 + - @backstage/errors@1.1.2-next.0 + - @backstage/types@1.0.0 + +## 1.1.4 + +### Patch Changes + +- 5ecca7e44b: No longer log when reloading remote config. +- 7d47def9c4: Removed dependency on `@types/jest`. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- Updated dependencies + - @backstage/cli-common@0.1.10 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + +## 1.1.4-next.2 + +### Patch Changes + +- 5ecca7e44b: No longer log when reloading remote config. +- 7d47def9c4: Removed dependency on `@types/jest`. +- Updated dependencies + - @backstage/cli-common@0.1.10-next.0 + - @backstage/config@1.0.2-next.0 + - @backstage/errors@1.1.1-next.0 + +## 1.1.4-next.1 + +### Patch Changes + +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. + +## 1.1.4-next.0 + +### Patch Changes + +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. + +## 1.1.3 + +### Patch Changes + +- bcada7cd9f: From now on the `$file` placeholder will trim the whitespaces and newline characters from the end of the file it reads. +- a70869e775: Updated dependency `msw` to `^0.43.0`. +- 72622d9143: Updated dependency `yaml` to `^2.0.0`. +- 8006d0f9bf: Updated dependency `msw` to `^0.44.0`. +- a3acec8819: Updated dependency `typescript-json-schema` to `^0.54.0`. +- Updated dependencies + - @backstage/errors@1.1.0 + +## 1.1.3-next.1 + +### Patch Changes + +- a70869e775: Updated dependency `msw` to `^0.43.0`. +- 72622d9143: Updated dependency `yaml` to `^2.0.0`. +- a3acec8819: Updated dependency `typescript-json-schema` to `^0.54.0`. + +## 1.1.3-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/errors@1.1.0-next.0 + +## 1.1.2 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. + +## 1.1.2-next.0 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. + +## 1.1.1 + +### Patch Changes + +- cfc0f19699: Updated dependency `fs-extra` to `10.1.0`. +- 9e8ef53498: Handle empty config files gracefully +- Updated dependencies + - @backstage/cli-common@0.1.9 + - @backstage/config@1.0.1 + +## 1.1.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/cli-common@0.1.9-next.0 + - @backstage/config@1.0.1-next.0 + +## 1.1.1-next.0 + +### Patch Changes + +- cfc0f19699: Updated dependency `fs-extra` to `10.1.0`. +- 9e8ef53498: Handle empty config files gracefully + +## 1.1.0 + +### Minor Changes + +- 19f6c6c32a: Added `ignoreSchemaErrors` to `schema.process`. + +### Patch Changes + +- e0a51384ac: build(deps): bump `ajv` from 7.0.3 to 8.10.0 +- 230ad0826f: Bump to using `@types/node` v16 +- c47509e1a0: Implemented changes suggested by Deepsource.io including multiple double non-null assertion operators and unexpected awaits for non-promise values. + +## 1.1.0-next.1 + +### Minor Changes + +- 19f6c6c32a: Added `ignoreSchemaErrors` to `schema.process`. + +### Patch Changes + +- 230ad0826f: Bump to using `@types/node` v16 + +## 1.0.1-next.0 + +### Patch Changes + +- e0a51384ac: build(deps): bump `ajv` from 7.0.3 to 8.10.0 +- c47509e1a0: Implemented changes suggested by Deepsource.io including multiple double non-null assertion operators and unexpected awaits for non-promise values. + +## 1.0.0 + +### Major Changes + +- b58c70c223: This package has been promoted to v1.0! To understand how this change affects the package, please check out our [versioning policy](https://backstage.io/docs/overview/versioning-policy). + +### Patch Changes + +- 664821371e: The `typescript-json-schema` dependency that is used during schema collection is now lazy loaded, as it eagerly loads in the TypeScript compiler. +- f910c2a3f8: build(deps): bump `typescript-json-schema` from 0.52.0 to 0.53.0 +- Updated dependencies + - @backstage/config@1.0.0 + - @backstage/errors@1.0.0 + - @backstage/types@1.0.0 + +## 0.9.7 + +### Patch Changes + +- e0a69ba49f: build(deps): bump `fs-extra` from 9.1.0 to 10.0.1 + +## 0.9.7-next.0 + +### Patch Changes + +- e0a69ba49f: build(deps): bump `fs-extra` from 9.1.0 to 10.0.1 + +## 0.9.6 + +### Patch Changes + +- c3a1300f79: Include any files included in configuration via $include or $file directives when watching for configuration changes. + +## 0.9.5 + +### Patch Changes + +- Fix for the previous release with missing type declarations. +- Updated dependencies + - @backstage/cli-common@0.1.8 + - @backstage/config@0.1.15 + - @backstage/errors@0.2.2 + - @backstage/types@0.1.3 + +## 0.9.4 + +### Patch Changes + +- 1ed305728b: Bump `node-fetch` to version 2.6.7 and `cross-fetch` to version 3.1.5 +- c77c5c7eb6: Added `backstage.role` to `package.json` +- Updated dependencies + - @backstage/errors@0.2.1 + - @backstage/cli-common@0.1.7 + - @backstage/config@0.1.14 + - @backstage/types@0.1.2 + +## 0.9.3 + +### Patch Changes + +- f685e1398f: Loading of app configurations now reference the `@deprecated` construct from + JSDoc to determine if a property in-use has been deprecated. Users are notified + of deprecated keys in the format: + + ```txt + The configuration key 'catalog.processors.githubOrg' of app-config.yaml is deprecated and may be removed soon. Configure a GitHub integration instead. + ``` + + When the `withDeprecatedKeys` option is set to `true` in the `process` method + of `loadConfigSchema`, the user will be notified that deprecated keys have been + identified in their app configuration. + + The `backend-common` and `plugin-app-backend` packages have been updated to set + `withDeprecatedKeys` to true so that users are notified of deprecated settings + by default. + +- Updated dependencies + - @backstage/config@0.1.13 + +## 0.9.3-next.0 + +### Patch Changes + +- f685e1398f: Loading of app configurations now reference the `@deprecated` construct from + JSDoc to determine if a property in-use has been deprecated. Users are notified + of deprecated keys in the format: + + ```txt + The configuration key 'catalog.processors.githubOrg' of app-config.yaml is deprecated and may be removed soon. Configure a GitHub integration instead. + ``` + + When the `withDeprecatedKeys` option is set to `true` in the `process` method + of `loadConfigSchema`, the user will be notified that deprecated keys have been + identified in their app configuration. + + The `backend-common` and `plugin-app-backend` packages have been updated to set + `withDeprecatedKeys` to true so that users are notified of deprecated settings + by default. + +- Updated dependencies + - @backstage/config@0.1.13-next.0 + +## 0.9.2 + +### Patch Changes + +- Updated dependencies + - @backstage/config@0.1.12 + - @backstage/errors@0.2.0 + +## 0.9.1 + +### Patch Changes + +- 84663d59a3: Bump `typescript-json-schema` from `^0.51.0` to `^0.52.0`. + +## 0.9.0 + +### Minor Changes + +- f6722d2458: Removed deprecated option `env` from `LoadConfigOptions` and associated tests +- 67d6cb3c7e: Removed deprecated option `configPaths` as it has been superseded by `configTargets` + +### Patch Changes + +- 1e7070443d: In case remote.reloadIntervalSeconds is passed, it must be a valid positive value + +## 0.8.1 + +### Patch Changes + +- b055a6addc: Align on usage of `cross-fetch` vs `node-fetch` in frontend vs backend packages, and remove some unnecessary imports of either one of them +- 4bea7b81d3: Uses key visibility as fallback in non-object arrays + +## 0.8.0 + +### Minor Changes + +- 1e99c73c75: Update `loadConfig` to return `LoadConfigResult` instead of an array of `AppConfig`. + + This function is primarily used internally by other config loaders like `loadBackendConfig` which means no changes are required for most users. + + If you use `loadConfig` directly you will need to update your usage from: + + ```diff + - const appConfigs = await loadConfig(options) + + const { appConfigs } = await loadConfig(options) + ``` + +### Patch Changes + +- 8809b6c0dd: Update the json-schema dependency version. +- Updated dependencies + - @backstage/cli-common@0.1.6 + +## 0.7.2 + +### Patch Changes + +- 0611f3b3e2: Reading app config from a remote server +- 26c5659c97: Bump msw to the same version as the rest + +## 0.7.1 + +### Patch Changes + +- 10615525f3: Switch to use the json and observable types from `@backstage/types` +- ea21f7f567: bump `typescript-json-schema` from 0.50.1 to 0.51.0 +- Updated dependencies + - @backstage/config@0.1.11 + - @backstage/cli-common@0.1.5 + - @backstage/errors@0.1.4 + +## 0.7.0 + +### Minor Changes + +- 7e97d0b8c1: Removed the `EnvFunc` public export. Its only usage was to be passed in to `LoadConfigOptions.experimentalEnvFunc`. If you were using this type, add a definition in your own project instead with the signature `(name: string) => Promise`. + +### Patch Changes + +- 223e8de6b4: Configuration schema errors are now filtered using the provided visibility option. This means that schema errors due to missing backend configuration will no longer break frontend builds. +- 7e97d0b8c1: Add public tags and documentation +- 36e67d2f24: Internal updates to apply more strict checks to throw errors. +- Updated dependencies + - @backstage/errors@0.1.3 + +## 0.6.10 + +### Patch Changes + +- 957e4b3351: Updated dependencies +- Updated dependencies + - @backstage/cli-common@0.1.4 + +## 0.6.9 + +### Patch Changes + +- ee7a1a4b64: Add option to collect configuration schemas from explicit package paths in addition to by package name. +- e68bd978e2: Allow collection of configuration schemas from multiple versions of the same package. + +## 0.6.8 + +### Patch Changes + +- d1da88a19: Properly export all used types. +- Updated dependencies + - @backstage/cli-common@0.1.3 + - @backstage/config@0.1.9 + +## 0.6.7 + +### Patch Changes + +- 0ade9d02b: Include `devDependencies` and `optionalDependencies` in the detection of Backstage packages when collecting configuration schema. +- 9b8cec063: Add support for config file watching through a new group of `watch` options to `loadConfig`. +- Updated dependencies + - @backstage/config@0.1.7 + +## 0.6.6 + +### Patch Changes + +- e9d3983ee: Add option to populate the `filteredKeys` property when processing configuration with a schema. +- Updated dependencies + - @backstage/config@0.1.6 + +## 0.6.5 + +### Patch Changes + +- ae84b20cf: Revert the upgrade to `fs-extra@10.0.0` as that seemed to have broken all installs inexplicably. + +## 0.6.4 + +### Patch Changes + +- f00493739: Removed workaround for breaking change in typescript 4.3 and bump `typescript-json-schema` instead. This should again allow the usage of `@items.visibility ` to set the visibility of array items. + +## 0.6.3 + +### Patch Changes + +- 2cf98d279: Resolve the path to app-config.yaml from the current working directory. This will allow use of `yarn link` or running the CLI in other directories and improve the experience for local backstage development. +- 438a512eb: Fixed configuration schema parsing when using TypeScript `4.3`. + +## 0.6.2 + +### Patch Changes + +- 290405276: Updated dependencies + +## 0.6.1 + +### Patch Changes + +- d8b81fd28: Bump `json-schema` dependency from `0.2.5` to `0.3.0`. +- Updated dependencies [d8b81fd28] + - @backstage/config@0.1.5 + +## 0.6.0 + +### Minor Changes + +- 82c66b8cd: Fix bug where `${...}` was not being escaped to `${...}` + + Add support for environment variable substitution in `$include`, `$file` and + `$env` transform values. + + - This change allows for including dynamic paths, such as environment specific + secrets by using the same environment variable substitution (`${..}`) already + supported outside of the various include transforms. + - If you are currently using the syntax `${...}` in your include transform values, + you will need to escape the substitution by using `${...}` instead to maintain + the same behavior. + +## 0.5.1 + +### Patch Changes + +- 062df71db: Bump `config-loader` to `ajv` 7, to enable v7 feature use elsewhere +- e9aab60c7: Each piece of the configuration schema is now validated upfront, in order to produce more informative errors. + +## 0.5.0 + +### Minor Changes + +- ef7957be4: Removed support for the deprecated `$data` placeholder. +- ef7957be4: Enable further processing of configuration files included using the `$include` placeholder. Meaning that for example for example `$env` includes will be processed as usual in included files. + +### Patch Changes + +- ef7957be4: Added support for environment variable substitutions in string configuration values using a `${VAR}` placeholder. All environment variables must be available, or the entire expression will be evaluated to `undefined`. To escape a substitution, use `${...}`, which will end up as `${...}`. + + For example: + + ```yaml + app: + baseUrl: https://${BASE_HOST} + ``` + +## 0.4.1 + +### Patch Changes + +- ad5c56fd9: Deprecate `$data` and replace it with `$include` which allows for any type of json value to be read from external files. In addition, `$include` can be used without a path, which causes the value at the root of the file to be loaded. + + Most usages of `$data` can be directly replaced with `$include`, except if the referenced value is not a string, in which case the value needs to be changed. For example: + + ```yaml + # app-config.yaml + foo: + $data: foo.yaml#myValue # replacing with $include will turn the value into a number + $data: bar.yaml#myValue # replacing with $include is safe + + # foo.yaml + myValue: 0xf00 + + # bar.yaml + myValue: bar + ``` + +## 0.4.0 + +### Minor Changes + +- 4e7091759: Fix typo of "visibility" in config schema reference + + If you have defined a config element named `visiblity`, you + will need to fix the spelling to `visibility`. For more info, + see . + +### Patch Changes + +- b4488ddb0: Added a type alias for PositionError = GeolocationPositionError + +## 0.3.0 + +### Minor Changes + +- 1722cb53c: Added support for loading and validating configuration schemas, as well as declaring config visibility through schemas. + + The new `loadConfigSchema` function exported by `@backstage/config-loader` allows for the collection and merging of configuration schemas from all nearby dependencies of the project. + + A configuration schema is declared using the `https://backstage.io/schema/config-v1` JSON Schema meta schema, which is based on draft07. The only difference to the draft07 schema is the custom `visibility` keyword, which is used to indicate whether the given config value should be visible in the frontend or not. The possible values are `frontend`, `backend`, and `secret`, where `backend` is the default. A visibility of `secret` has the same scope at runtime, but it will be treated with more care in certain contexts, and defining both `frontend` and `secret` for the same value in two different schemas will result in an error during schema merging. + + Packages that wish to contribute configuration schema should declare it in a root `"configSchema"` field in `package.json`. The field can either contain an inlined JSON schema, or a relative path to a schema file. Schema files can be in either `.json` or `.d.ts` format. + + TypeScript configuration schema files should export a single `Config` type, for example: + + ```ts + export interface Config { + app: { + /** + * Frontend root URL + * @visibility frontend + */ + baseUrl: string; + }; + } + ``` + +## 0.2.0 + +### Minor Changes + +- 8c2b76e45: **BREAKING CHANGE** + + The existing loading of additional config files like `app-config.development.yaml` using APP_ENV or NODE_ENV has been removed. + Instead, the CLI and backend process now accept one or more `--config` flags to load config files. + + Without passing any flags, `app-config.yaml` and, if it exists, `app-config.local.yaml` will be loaded. + If passing any `--config ` flags, only those files will be loaded, **NOT** the default `app-config.yaml` one. + + The old behaviour of for example `APP_ENV=development` can be replicated using the following flags: + + ```bash + --config ../../app-config.yaml --config ../../app-config.development.yaml + ``` + +- ce5512bc0: Added support for new shorthand when defining secrets, where `$env: ENV` can be used instead of `$secret: { env: ENV }` etc. + +## @backstage/backend-defaults@0.13.0-next.2 + +### Minor Changes + +- 8495b18: Add a new `externalTokenHandlersServiceRef` to allow custom external token validations + +## @backstage/plugin-mui-to-bui@0.2.0-next.0 + +### Minor Changes + +- d5cbdba: This is the first release of the Material UI to Backstage UI migration helper plugin. It adds a new page at `/mui-to-bui` that converts an existing MUI v5 theme into Backstage UI (BUI) CSS variables, with live preview and copy/download. + +### Patch Changes + +- Updated dependencies + - @backstage/ui@0.7.2-next.1 + - @backstage/theme@0.6.9-next.0 + +## @backstage/plugin-notifications-backend-module-slack@0.2.0-next.1 + +### Minor Changes + +- 3d09bb2: Adds username as optional config in order to send Slack notifications with a specific username in the case when using one Slack App for more than just Backstage. + +## @backstage/app-defaults@1.7.1-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.2 + - @backstage/theme@0.6.9-next.0 + +## @backstage/cli@0.34.4-next.2 + +### Patch Changes + +- ab96bb7: Added a new `--entrypoint` option to the `package start` command, which allows you to specify a custom entry directory/file for development applications. This is particularly useful when maintaining separate dev apps for different versions of your plugin (e.g., stable and alpha). + + **Example usage:** + + Consider the following plugin dev folder structure: + + dev/ + index.tsx + alpha/ + index.ts + + - The default `yarn package start` command uses the `dev/` folder as the entry point and executes `dev/index.tsx` file; + - Running `yarn package start --entrypoint dev/alpha` will instead use `dev/alpha/` as the entry point and execute `dev/alpha/index.ts` file. + +## @backstage/core-components@0.18.2-next.2 + +### Patch Changes + +- 95935fb: Fixed dependency graph automatically scrolling forever +- Updated dependencies + - @backstage/theme@0.6.9-next.0 + +## @backstage/create-app@0.7.5-next.2 + +### Patch Changes + +- Bumped create-app version. + +## @backstage/dev-utils@1.1.15-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.2 + - @backstage/theme@0.6.9-next.0 + - @backstage/app-defaults@1.7.1-next.2 + +## @backstage/test-utils@1.7.12-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/theme@0.6.9-next.0 + +## @backstage/theme@0.6.9-next.0 + +### Patch Changes + +- d5cbdba: The `UnifiedThemeProvider` now coordinates theme attributes on the document `body` in case multiple theme providers are rendered. + +## @backstage/ui@0.7.2-next.1 + +### Patch Changes + +- a9b88be: Enable tooltips on disabled buttons with automatic wrapper +- 4adbb03: Avoid overriding onChange when spreading props + +## @backstage/plugin-app@0.3.1-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.2 + - @backstage/theme@0.6.9-next.0 + +## @backstage/plugin-auth@0.1.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.2 + - @backstage/theme@0.6.9-next.0 + +## @backstage/plugin-catalog-backend@3.1.2-next.2 + +### Patch Changes + +- 6493c98: Log before provider-orphaning eviction happens +- 77516c5: Added new `catalog:validate-entity` action to actions registry. + + This action can be used to validate entities against the software catalog. + This is useful for validating `catalog-info.yaml` file changes locally using the + Backstage MCP server. + +## @backstage/plugin-home@0.8.13-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.2 + - @backstage/theme@0.6.9-next.0 + +## @backstage/plugin-kubernetes-react@0.5.12-next.2 + +### Patch Changes + +- ac405f2: The configmaps added to be rendered +- f7a4144: Fixes calculation of CPU utilization in the PodTable +- Updated dependencies + - @backstage/core-components@0.18.2-next.2 + +## @backstage/plugin-notifications@0.5.10-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.2 + - @backstage/theme@0.6.9-next.0 + +## @backstage/plugin-notifications-backend@0.5.11-next.1 + +### Patch Changes + +- 3b8e156: Fixed exclude entity reference not working in notification sending + +## @backstage/plugin-org@0.6.45-next.2 + +### Patch Changes + +- 8b7351f: Add `initialRelationAggregation` and `showAggregateMembersToggle` options to `EntityMembersListCard` as well to `EntityOwnershipCard` +- Updated dependencies + - @backstage/core-components@0.18.2-next.2 + +## @backstage/plugin-scaffolder@1.34.2-next.2 + +### Patch Changes + +- 075e064: Added missing form fields for the new frontend system. +- Updated dependencies + - @backstage/plugin-scaffolder-react@1.19.2-next.2 + - @backstage/core-components@0.18.2-next.2 + - @backstage/plugin-techdocs-react@1.3.4-next.1 + +## @backstage/plugin-scaffolder-react@1.19.2-next.2 + +### Patch Changes + +- e61f89e: Don't change loading to false until we've actually got some log state +- Updated dependencies + - @backstage/core-components@0.18.2-next.2 + - @backstage/theme@0.6.9-next.0 + +## @backstage/plugin-search-react@1.9.5-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.2 + - @backstage/theme@0.6.9-next.0 + +## @backstage/plugin-signals@0.0.24-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.2 + - @backstage/theme@0.6.9-next.0 + +## @backstage/plugin-techdocs@1.15.1-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.2 + - @backstage/theme@0.6.9-next.0 + - @backstage/plugin-search-react@1.9.5-next.2 + - @backstage/plugin-techdocs-react@1.3.4-next.1 + +## @backstage/plugin-user-settings@0.8.27-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.2 + - @backstage/theme@0.6.9-next.0 + +## example-app@0.2.114-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-mui-to-bui@0.2.0-next.0 + - @backstage/plugin-scaffolder-react@1.19.2-next.2 + - @backstage/plugin-org@0.6.45-next.2 + - @backstage/cli@0.34.4-next.2 + - @backstage/ui@0.7.2-next.1 + - @backstage/core-components@0.18.2-next.2 + - @backstage/theme@0.6.9-next.0 + - @backstage/plugin-scaffolder@1.34.2-next.2 + - @backstage/app-defaults@1.7.1-next.2 + - @backstage/plugin-home@0.8.13-next.2 + - @backstage/plugin-notifications@0.5.10-next.2 + - @backstage/plugin-search-react@1.9.5-next.2 + - @backstage/plugin-signals@0.0.24-next.2 + - @backstage/plugin-techdocs@1.15.1-next.2 + - @backstage/plugin-techdocs-react@1.3.4-next.1 + - @backstage/plugin-user-settings@0.8.27-next.2 + +## example-app-next@0.0.28-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-react@1.19.2-next.2 + - @backstage/plugin-org@0.6.45-next.2 + - @backstage/cli@0.34.4-next.2 + - @backstage/ui@0.7.2-next.1 + - @backstage/core-components@0.18.2-next.2 + - @backstage/theme@0.6.9-next.0 + - @backstage/plugin-scaffolder@1.34.2-next.2 + - @backstage/app-defaults@1.7.1-next.2 + - @backstage/plugin-app@0.3.1-next.2 + - @backstage/plugin-auth@0.1.1-next.1 + - @backstage/plugin-home@0.8.13-next.2 + - @backstage/plugin-notifications@0.5.10-next.2 + - @backstage/plugin-search-react@1.9.5-next.2 + - @backstage/plugin-signals@0.0.24-next.2 + - @backstage/plugin-techdocs@1.15.1-next.2 + - @backstage/plugin-techdocs-react@1.3.4-next.1 + - @backstage/plugin-user-settings@0.8.27-next.2 + +## techdocs-cli-embedded-app@0.2.113-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/cli@0.34.4-next.2 + - @backstage/core-components@0.18.2-next.2 + - @backstage/theme@0.6.9-next.0 + - @backstage/app-defaults@1.7.1-next.2 + - @backstage/test-utils@1.7.12-next.1 + - @backstage/plugin-techdocs@1.15.1-next.2 + - @backstage/plugin-techdocs-react@1.3.4-next.1 diff --git a/docs/releases/v1.44.0-next.3-changelog.md b/docs/releases/v1.44.0-next.3-changelog.md new file mode 100644 index 0000000000..8a65d6d6a6 --- /dev/null +++ b/docs/releases/v1.44.0-next.3-changelog.md @@ -0,0 +1,219 @@ +# Release v1.44.0-next.3 + +Upgrade Helper: [https://backstage.github.io/upgrade-helper/?to=1.44.0-next.3](https://backstage.github.io/upgrade-helper/?to=1.44.0-next.3) + +## @backstage/backend-app-api@1.2.8-next.1 + +### Patch Changes + +- dd69cf6: Moved up registration of unhandled rejections and errors listeners to be done as early as possible, avoiding flakiness in backend startups and instead always logging these failures rather than sometimes crashing the process. + +## @backstage/cli@0.34.4-next.3 + +### Patch Changes + +- f2cf564: Removed the script transform cache from the default Jest configuration. The script cache provided a moderate performance boost, but it is incompatible with Jest 30. + +- 024645e: Remove unused @octokit modules from cli package + + - @octokit/graphql + - @octokit/graphql-schema + - @octokit/oauth-app + +- d14ef24: Added automatic detection and support for the Backstage Yarn plugin when generating new packages with `yarn new`. When the plugin is installed, new packages will automatically use `backstage:^` ranges for `@backstage/*` dependencies. + +## @backstage/core-components@0.18.2-next.3 + +### Patch Changes + +- 431130c: Added `renderEdge` prop to `` component in `@backstage/core-components` to allow custom rendering of graph edges. + +## @backstage/create-app@0.7.5-next.3 + +### Patch Changes + +- Bumped create-app version. + +## @backstage/frontend-plugin-api@0.12.1-next.2 + +### Patch Changes + +- 8ed53eb: Added `coreExtensionData.title`, especially useful for creating extensible layout with tabbed pages, but available for use for other cases too. +- Updated dependencies + - @backstage/core-components@0.18.2-next.3 + +## @backstage/ui@0.7.2-next.2 + +### Patch Changes + +- 3c921c5: Making href mandatory in tabs that are part of a Header component +- 5c21e45: Add react router for internal routing for ButtonLinks +- 9781815: Remove auto selection of tabs for tabs that all have href defined +- f6dff5b: Using react router for internal links in the Menu component + +## @backstage/plugin-app-visualizer@0.1.24-next.2 + +### Patch Changes + +- 4406144: Ensure that the text rendering has react keys for all elements +- Updated dependencies + - @backstage/core-components@0.18.2-next.3 + - @backstage/frontend-plugin-api@0.12.1-next.2 + +## @backstage/plugin-catalog-backend-module-aws@0.4.16-next.2 + +### Patch Changes + +- 99fcf98: Removed unused dependencies + +## @backstage/plugin-catalog-backend-module-azure@0.3.10-next.2 + +### Patch Changes + +- 99fcf98: Removed unused dependencies + +## @backstage/plugin-catalog-backend-module-bitbucket-cloud@0.5.4-next.2 + +### Patch Changes + +- 99fcf98: Removed unused dependencies + +## @backstage/plugin-catalog-backend-module-bitbucket-server@0.5.4-next.2 + +### Patch Changes + +- 99fcf98: Removed unused dependencies + +## @backstage/plugin-catalog-backend-module-gerrit@0.3.7-next.2 + +### Patch Changes + +- 99fcf98: Removed unused dependencies + +## @backstage/plugin-catalog-backend-module-github@0.11.1-next.2 + +### Patch Changes + +- 99fcf98: Removed unused dependencies + +## @backstage/plugin-catalog-backend-module-github-org@0.3.15-next.2 + +### Patch Changes + +- 99fcf98: Removed unused dependencies +- Updated dependencies + - @backstage/plugin-catalog-backend-module-github@0.11.1-next.2 + +## @backstage/plugin-catalog-backend-module-gitlab@0.7.4-next.2 + +### Patch Changes + +- 0443119: Fixed an issue in `GitlabDiscoveryEntityProvider` where entity fetching could fail for projects with special characters or that had been renamed or moved. +- 99fcf98: Removed unused dependencies + +## @backstage/plugin-catalog-backend-module-gitlab-org@0.2.14-next.2 + +### Patch Changes + +- 99fcf98: Removed unused dependencies +- Updated dependencies + - @backstage/plugin-catalog-backend-module-gitlab@0.7.4-next.2 + +## @backstage/plugin-catalog-backend-module-msgraph@0.8.1-next.1 + +### Patch Changes + +- 99fcf98: Removed unused dependencies + +## @backstage/plugin-catalog-backend-module-puppetdb@0.2.15-next.1 + +### Patch Changes + +- 99fcf98: Removed unused dependencies + +## @backstage/plugin-catalog-graph@0.5.2-next.2 + +### Patch Changes + +- 431130c: Added `renderEdge` prop to `` component in `@backstage/core-components` to allow custom rendering of graph edges. +- Updated dependencies + - @backstage/core-components@0.18.2-next.3 + - @backstage/frontend-plugin-api@0.12.1-next.2 + +## @backstage/plugin-home@0.8.13-next.3 + +### Patch Changes + +- e7d59d3: fix(home): correct `clearAll` logic to properly handle `deletable` flag +- Updated dependencies + - @backstage/core-components@0.18.2-next.3 + - @backstage/frontend-plugin-api@0.12.1-next.2 + +## @backstage/plugin-kubernetes@0.12.12-next.2 + +### Patch Changes + +- 99fcf98: Removed unused dependencies +- Updated dependencies + - @backstage/core-components@0.18.2-next.3 + - @backstage/frontend-plugin-api@0.12.1-next.2 + +## @backstage/plugin-kubernetes-cluster@0.0.30-next.2 + +### Patch Changes + +- 99fcf98: Removed unused dependencies +- Updated dependencies + - @backstage/core-components@0.18.2-next.3 + +## @backstage/plugin-notifications@0.5.10-next.3 + +### Patch Changes + +- f5e0963: Removed unused dependencies +- Updated dependencies + - @backstage/core-components@0.18.2-next.3 + - @backstage/frontend-plugin-api@0.12.1-next.2 + +## @backstage/plugin-notifications-backend@0.5.11-next.2 + +### Patch Changes + +- f5e0963: Removed unused dependencies + +## @backstage/plugin-notifications-backend-module-email@0.3.14-next.1 + +### Patch Changes + +- b8cf31a: chore(deps): bump `nodemailer` from 6.9.16 to 7.0.7 +- f5e0963: Removed unused dependencies + +## @backstage/plugin-scaffolder@1.34.2-next.3 + +### Patch Changes + +- d9aed74: Forward `ui:disabled` in `OwnedEntityPicker` to allow disabling it +- Updated dependencies + - @backstage/core-components@0.18.2-next.3 + - @backstage/frontend-plugin-api@0.12.1-next.2 + +## @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.14-next.2 + +### Patch Changes + +- f5e0963: Removed unused dependencies + +## @backstage/plugin-scaffolder-backend-module-gitlab@0.9.6-next.2 + +### Patch Changes + +- f5e0963: Removed unused dependencies + +## @backstage/plugin-signals@0.0.24-next.3 + +### Patch Changes + +- 7b41d9d: Remove `app-root-element:signals/signals-display` which was not doing anything useful +- Updated dependencies + - @backstage/core-components@0.18.2-next.3 + - @backstage/frontend-plugin-api@0.12.1-next.2 diff --git a/docs/releases/v1.44.0.md b/docs/releases/v1.44.0.md new file mode 100644 index 0000000000..c57708a8ec --- /dev/null +++ b/docs/releases/v1.44.0.md @@ -0,0 +1,141 @@ +--- +id: v1.44.0 +title: v1.44.0 +description: Backstage Release v1.44.0 +--- + +These are the release notes for the v1.44.0 release of [Backstage](https://backstage.io/). + +A huge thanks to the whole team of maintainers and contributors as well as the amazing Backstage Community for all the hard work in getting this release developed and done. + +## Highlights + +### Scaffolder Backend 3.0 + +**BREAKING**: + +Removal of deprecated types and interfaces: `CreateWorkerOptions`, `CurrentClaimedTask`, `DatabaseTaskStore`, `TaskManager`, etc. + +The `TaskBroker` interface now requires the `cancel`, `recoverTasks`, and `retry` methods to be implemented. If your implementation of `TaskBroker` doesn’t use these methods, you can replace them with a `no-op () => void` function. +Please reach out to us in an issue as we're thinking about completely removing the `TaskBroker` extension point soon and would like to hear your use cases for the upcoming re-architecture of the `scaffolder-backend` plugin. + +The `scaffolderActionsExtensionPoint` has been moved from `/alpha` to the main export. + +```ts +// before +import { scaffolderActionsExtensionPoint } from '@backstage/plugin-scaffolder-node/alpha'; + +// after +import { scaffolderActionsExtensionPoint } from '@backstage/plugin-scaffolder-node'; +``` + +**DEPRECATION**: + +We're going to be working on refactoring a lot of the internals of the Scaffolder backend plugin, and with that come a lot of deprecations and removals for public types that are making these things hard. + +If you're using these types, please reach out to us either on Discord or a GitHub issue with your use cases. + +- `SerializedTask`, `SerializedTaskEvent`, `TaskBroker`, `TaskContext`, `TaskBrokerDispatchOptions`, `TaskBrokerDispatchResult`, `TaskCompletionState`, `TaskEventType`, `TaskFilter`, `TaskFilters`, `TaskStatus` are the types that have now been marked as deprecated, and will be removed in a future release. + +### Design System: Backstage Theme + +**BREAKING** Removed the built-in `CssBaseline` from `UnifiedThemeProvider`. If your Backstage instance looks broken after this update, you likely forgot to add our new Backstage UI global CSS. To do that, please import `@backstage/ui/css/styles.css` in `packages/app/src/index.tsx`: + +```tsx +import '@backstage/ui/css/styles.css'; +``` + +This change also removes the `noCssBaseline` prop, which became redundant. + +### Design System: Backstage UI + +**NEW** + +Adding a new `Dialog` component to Backstage UI. Contributed by [@ssjoblad](https://github.com/@ssjoblad) in [#31371](https://github.com/backstage/backstage/pull/31371) +Added new `virtualized`, `maxWidth` and `maxHeight` props to `Menu`, `MenuListBox`, `MenuAutocomplete` and `MenuAutocompleteListBox` to allow for virtualization of long lists inside menus. + +**BREAKING** + +New `PasswordField` component - removed `password` and `search` types from `TextField`. Contributed by [@birdhb](https://github.com/birdhb) in [#31238](https://github.com/backstage/backstage/pull/31238) +Restructured Backstage UI component styling to use CSS Modules instead of pure CSS. We don't expect this to be an issue in practice but it is important to call out that all styles are now loaded through CSS modules with generated class names. We are still providing fixed class names for all components to allow anyone to style their Backstage instance. +The `ScrollArea` component has been removed from Backstage UI because it did not meet our accessibility standards. +Removed Icon component in Backstage UI. This component was creating issues for tree-shaking. It is recommended to use icons from `@remixicon/react` until we find a better alternative in Backstage UI. + +### **BREAKING**: Extension testing with new `renderTestApp` + +The `extensions` option was removed from `renderInTestApp`, because it led to confusing effects where the old and new frontend worlds were mixed. If you need to pass extensions to the test app, use the new `renderTestApp` utility instead. + +### Backstage CLI: Yarn plugin + +Added automatic detection and support for the Backstage Yarn plugin when generating new packages with `yarn new`. When the plugin is installed, new packages will automatically use `backstage:^` ranges for `@backstage/*` dependencies. + +### Backstage CLI: App entrypoints + +Added a new `--entrypoint` option to the `package start` command, which allows you to specify a custom entry directory/file for development applications. This is particularly useful when maintaining separate dev apps for different versions of your plugin (e.g., stable and alpha). + +For example, consider the following plugin dev folder structure: + +``` +dev/ + index.tsx + alpha/ + index.ts +``` + +- The default `yarn package start` command uses the `dev/` folder as the entry point and executes `dev/index.tsx` file; +- Running `yarn package start --entrypoint dev/alpha` will instead use `dev/alpha/` as the entry point and execute `dev/alpha/index.ts` file. + +### Support for custom external service auth methods + +A new service ref has finally landed: the external token handler. This allows adopters to add custom handlers for authorization tokens that are sent to your backend plugins. So if your organization already has a settled service-to-service auth method in your backend flora, you can now seamlessly make Backstage accept those too. This complements the pre-existing support for static and JWKS-based tokens. + +As usual, it's all documented in [the corresponding service-to-service auth section](https://backstage.io/docs/auth/service-to-service-auth) on the microsite. + +Contributed by [@Sarabadu](https://github.com/Sarabadu) in [#28942](https://github.com/backstage/backstage/pull/28942) + +### New plugin: Themer + +There’s a new Material UI to Backstage UI migration helper plugin. It adds a new page at `/mui-to-bui` that converts an existing MUI v5 theme into Backstage UI (BUI) CSS variables, with live preview and copy/download. There are some more details and screenshots in [the pull request](https://github.com/backstage/backstage/pull/31140). + +### Relaxed linting in the new frontend system + +The built-in lint rules used to forbid frontend plugin packages from importing other frontend plugin packages - those dependencies should normally be moved to a `-react` or `-common` package instead. + +But in the new frontend system, it becomes a common desire to extend and adapt other plugins, which implies importing those plugins in code. So the lint rules have been relaxed to support this specific case, IF the plugin IDs of the two packages are the same. + +This means that you can now make an (internal) frontend plugin package that your app imports, and then that internal package in turn imports the open source plugin you are adapting. So you no longer have to keep your adaptations inside the app itself! + +Contributed by [@drodil](https://github.com/drodil) in [#31373](https://github.com/backstage/backstage/pull/31373) + +### TechDocs CLI serve now supports live reloading + +The TechDocs CLI and its embedded app now leverage `mkdocs` live reload support. Let us know how it works out for you! + +Contributed by [@GabDug](https://github.com/GabDug) in [#30541](https://github.com/backstage/backstage/pull/30541) + +### Easier server options through config + +You can now more easily set low level HTTP server options such as request timeouts for your backend, directly from your app-config without having to adapt your root HTTP router service. Check out [the relevant service docs](https://backstage.io/docs/backend-system/core-services/root-http-router) for more details! + +Contributed by [@BethGriggs](https://github.com/BethGriggs) in [#30483](https://github.com/backstage/backstage/pull/30483) + +## Security Fixes + +This release does not contain any security fixes. + +## Upgrade path + +We recommend that you keep your Backstage project up to date with this latest release. For more guidance on how to upgrade, check out the documentation for [keeping Backstage updated](https://backstage.io/docs/getting-started/keeping-backstage-updated). + +## Links and References + +Below you can find a list of links and references to help you learn about and start using this new release. + +- [Backstage official website](https://backstage.io/), [documentation](https://backstage.io/docs/), and [getting started guide](https://backstage.io/docs/getting-started/) +- [GitHub repository](https://github.com/backstage/backstage) +- Backstage's [versioning and support policy](https://backstage.io/docs/overview/versioning-policy) +- [Community Discord](https://discord.gg/backstage-687207715902193673) for discussions and support +- [Changelog](https://github.com/backstage/backstage/tree/master/docs/releases/v1.44.0-changelog.md) +- Backstage [Demos](https://backstage.io/demos), [Blog](https://backstage.io/blog), [Roadmap](https://backstage.io/docs/overview/roadmap) and [Plugins](https://backstage.io/plugins) + +Sign up for our [newsletter](https://info.backstage.spotify.com/newsletter_subscribe) if you want to be informed about what is happening in the world of Backstage. diff --git a/docs/releases/v1.45.0-next.0-changelog.md b/docs/releases/v1.45.0-next.0-changelog.md new file mode 100644 index 0000000000..da49684cfb --- /dev/null +++ b/docs/releases/v1.45.0-next.0-changelog.md @@ -0,0 +1,2372 @@ +# Release v1.45.0-next.0 + +Upgrade Helper: [https://backstage.github.io/upgrade-helper/?to=1.45.0-next.0](https://backstage.github.io/upgrade-helper/?to=1.45.0-next.0) + +## @backstage/backend-test-utils@1.10.0-next.0 + +### Minor Changes + +- d57b13b: Added support for Postgres 18 to the available `TestDatabases`. + + Note that the set of _default_ databases to test against for users of the `TestDatabases` class was also updated to include Postgres 14 and 18, instead of 13 and 17. If you need to override this, you can pass in an explicit `ids` argument, for example `ids: ['POSTGRES_17', 'POSTGRES_13', 'SQLITE_3']`. + +### Patch Changes + +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/backend-defaults@0.13.1-next.0 + - @backstage/plugin-events-node@0.4.17-next.0 + - @backstage/plugin-auth-node@0.6.9-next.0 + - @backstage/backend-app-api@1.2.9-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-permission-common@0.9.3-next.0 + +## @backstage/eslint-plugin@0.2.0-next.0 + +### Minor Changes + +- 926389b: Added `@backstage/no-ui-css-imports-in-non-frontend` rule, which ensures that CSS from `@backstage/ui` is not imported outside of the frontend app. + +## @backstage/app-defaults@1.7.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-react@0.4.38-next.0 + - @backstage/core-app-api@1.19.2-next.0 + - @backstage/core-plugin-api@1.11.2-next.0 + - @backstage/core-components@0.18.3-next.0 + - @backstage/theme@0.7.0 + +## @backstage/backend-app-api@1.2.9-next.0 + +### Patch Changes + +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/config@1.3.6-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/errors@1.2.7 + +## @backstage/backend-defaults@0.13.1-next.0 + +### Patch Changes + +- 9bcfa77: Adjusted the log line wording of task worker starting +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- b2f6a5a: Fix #31348 issue where BitbucketUrlReader ignored provided token and instead always used integration credentials +- Updated dependencies + - @backstage/plugin-events-node@0.4.17-next.0 + - @backstage/plugin-auth-node@0.6.9-next.0 + - @backstage/backend-app-api@1.2.9-next.0 + - @backstage/config-loader@1.10.6-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/cli-node@0.2.15-next.0 + - @backstage/integration@1.18.2-next.0 + - @backstage/integration-aws-node@0.1.19-next.0 + - @backstage/plugin-permission-node@0.10.6-next.0 + - @backstage/backend-dev-utils@0.1.5 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + +## @backstage/backend-dynamic-feature-service@0.7.6-next.0 + +### Patch Changes + +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/backend-defaults@0.13.1-next.0 + - @backstage/plugin-catalog-backend@3.1.3-next.0 + - @backstage/plugin-search-backend-node@1.3.17-next.0 + - @backstage/plugin-events-backend@0.5.8-next.0 + - @backstage/plugin-events-node@0.4.17-next.0 + - @backstage/plugin-auth-node@0.6.9-next.0 + - @backstage/config-loader@1.10.6-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/cli-node@0.2.15-next.0 + - @backstage/plugin-scaffolder-node@0.12.1-next.0 + - @backstage/plugin-permission-node@0.10.6-next.0 + - @backstage/backend-openapi-utils@0.6.3-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/cli-common@0.1.15 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-app-node@0.1.39-next.0 + - @backstage/plugin-permission-common@0.9.3-next.0 + - @backstage/plugin-search-common@1.2.21-next.0 + +## @backstage/backend-openapi-utils@0.6.3-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + +## @backstage/backend-plugin-api@1.4.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.6.9-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/plugin-permission-node@0.10.6-next.0 + - @backstage/cli-common@0.1.15 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-permission-common@0.9.3-next.0 + +## @backstage/catalog-client@1.12.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/errors@1.2.7 + +## @backstage/catalog-model@1.7.6-next.0 + +### Patch Changes + +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + +## @backstage/cli@0.34.5-next.0 + +### Patch Changes + +- fc7cbfc: The templates executed with the `yarn new` command now supports templating filenames. +- fc7cbfc: Added a template for the `yarn new` command to create an catalog entity provider. To add this template to an explicit list in the root `package.json`, use `@backstage/cli/templates/catalog-provider-module`. +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/eslint-plugin@0.2.0-next.0 + - @backstage/config-loader@1.10.6-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/cli-node@0.2.15-next.0 + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/integration@1.18.2-next.0 + - @backstage/cli-common@0.1.15 + - @backstage/errors@1.2.7 + - @backstage/release-manifests@0.0.13 + - @backstage/types@1.2.2 + +## @backstage/cli-node@0.2.15-next.0 + +### Patch Changes + +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/cli-common@0.1.15 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + +## @backstage/config@1.3.6-next.0 + +### Patch Changes + +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + +## @backstage/config-loader@1.10.6-next.0 + +### Patch Changes + +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/config@1.3.6-next.0 + - @backstage/cli-common@0.1.15 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + +## @backstage/core-app-api@1.19.2-next.0 + +### Patch Changes + +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/core-plugin-api@1.11.2-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/types@1.2.2 + - @backstage/version-bridge@1.0.11 + +## @backstage/core-compat-api@0.5.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.3-next.0 + - @backstage/core-plugin-api@1.11.2-next.0 + - @backstage/frontend-plugin-api@0.12.2-next.0 + - @backstage/version-bridge@1.0.11 + +## @backstage/core-components@0.18.3-next.0 + +### Patch Changes + +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/core-plugin-api@1.11.2-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/errors@1.2.7 + - @backstage/theme@0.7.0 + - @backstage/version-bridge@1.0.11 + +## @backstage/core-plugin-api@1.11.2-next.0 + +### Patch Changes + +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/config@1.3.6-next.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/version-bridge@1.0.11 + +## @backstage/create-app@0.7.6-next.0 + +### Patch Changes + +- fc7cbfc: Added the new `@backstage/cli/templates/catalog-provider-module` template to the explicit template configuration for the `next-app` template. +- Updated dependencies + - @backstage/cli-common@0.1.15 + +## @backstage/dev-utils@1.1.17-next.0 + +### Patch Changes + +- b29a856: Fixed styling of the dev app by adding a lazy import of `@backstage/ui/css/styles.css`. +- Updated dependencies + - @backstage/ui@0.8.2-next.0 + - @backstage/plugin-catalog-react@1.21.3-next.0 + - @backstage/core-app-api@1.19.2-next.0 + - @backstage/core-plugin-api@1.11.2-next.0 + - @backstage/core-components@0.18.3-next.0 + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/app-defaults@1.7.2-next.0 + - @backstage/integration-react@1.2.12-next.0 + - @backstage/theme@0.7.0 + +## @backstage/frontend-app-api@0.13.2-next.0 + +### Patch Changes + +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/core-app-api@1.19.2-next.0 + - @backstage/core-plugin-api@1.11.2-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/frontend-plugin-api@0.12.2-next.0 + - @backstage/errors@1.2.7 + - @backstage/frontend-defaults@0.3.3-next.0 + - @backstage/types@1.2.2 + - @backstage/version-bridge@1.0.11 + +## @backstage/frontend-defaults@0.3.3-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-app-api@0.13.2-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/core-components@0.18.3-next.0 + - @backstage/frontend-plugin-api@0.12.2-next.0 + - @backstage/errors@1.2.7 + - @backstage/plugin-app@0.3.2-next.0 + +## @backstage/frontend-dynamic-feature-loader@0.1.7-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.6-next.0 + - @backstage/frontend-plugin-api@0.12.2-next.0 + +## @backstage/frontend-plugin-api@0.12.2-next.0 + +### Patch Changes + +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/core-plugin-api@1.11.2-next.0 + - @backstage/core-components@0.18.3-next.0 + - @backstage/types@1.2.2 + - @backstage/version-bridge@1.0.11 + +## @backstage/frontend-test-utils@0.4.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-app-api@0.13.2-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/frontend-plugin-api@0.12.2-next.0 + - @backstage/test-utils@1.7.13-next.0 + - @backstage/types@1.2.2 + - @backstage/version-bridge@1.0.11 + - @backstage/plugin-app@0.3.2-next.0 + +## @backstage/integration@1.18.2-next.0 + +### Patch Changes + +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/config@1.3.6-next.0 + - @backstage/errors@1.2.7 + +## @backstage/integration-aws-node@0.1.19-next.0 + +### Patch Changes + +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/config@1.3.6-next.0 + - @backstage/errors@1.2.7 + +## @backstage/integration-react@1.2.12-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-plugin-api@1.11.2-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/integration@1.18.2-next.0 + +## @backstage/repo-tools@0.15.4-next.0 + +### Patch Changes + +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/config-loader@1.10.6-next.0 + - @backstage/cli-node@0.2.15-next.0 + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/cli-common@0.1.15 + - @backstage/errors@1.2.7 + +## @techdocs/cli@1.10.1-next.0 + +### Patch Changes + +- c2a2017: Fix for missing styles due to move to BUI. +- Updated dependencies + - @backstage/backend-defaults@0.13.1-next.0 + - @backstage/plugin-techdocs-node@1.13.9-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/cli-common@0.1.15 + +## @backstage/test-utils@1.7.13-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-react@0.4.38-next.0 + - @backstage/core-app-api@1.19.2-next.0 + - @backstage/core-plugin-api@1.11.2-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/theme@0.7.0 + - @backstage/types@1.2.2 + - @backstage/plugin-permission-common@0.9.3-next.0 + +## @backstage/ui@0.8.2-next.0 + +### Patch Changes + +- 26c6a78: Fix default text color in Backstage UI +- dac851f: Fix the default font size in Backstage UI. +- 3c0ea67: Fix CSS layer ordering in Backstage UI to make sure component styles are loaded after tokens and base declarations. +- 4eb455c: Fix font smoothing as default in Backstage UI. +- 00bfb83: Fix default font wight and font family in Backstage UI. + +## @backstage/plugin-api-docs@0.13.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-react@0.4.38-next.0 + - @backstage/plugin-catalog-react@1.21.3-next.0 + - @backstage/core-plugin-api@1.11.2-next.0 + - @backstage/core-components@0.18.3-next.0 + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/frontend-plugin-api@0.12.2-next.0 + - @backstage/core-compat-api@0.5.4-next.0 + - @backstage/plugin-catalog@1.31.5-next.0 + - @backstage/plugin-catalog-common@1.1.7-next.0 + +## @backstage/plugin-app@0.3.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-react@0.4.38-next.0 + - @backstage/core-plugin-api@1.11.2-next.0 + - @backstage/core-components@0.18.3-next.0 + - @backstage/frontend-plugin-api@0.12.2-next.0 + - @backstage/integration-react@1.2.12-next.0 + - @backstage/theme@0.7.0 + - @backstage/types@1.2.2 + - @backstage/version-bridge@1.0.11 + +## @backstage/plugin-app-backend@0.5.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.6.9-next.0 + - @backstage/config-loader@1.10.6-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-app-node@0.1.39-next.0 + +## @backstage/plugin-app-node@0.1.39-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config-loader@1.10.6-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + +## @backstage/plugin-app-visualizer@0.1.25-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-plugin-api@1.11.2-next.0 + - @backstage/core-components@0.18.3-next.0 + - @backstage/frontend-plugin-api@0.12.2-next.0 + +## @backstage/plugin-auth@0.1.2-next.0 + +### Patch Changes + +- 1609e79: Authentication content screen now uses application title. +- Updated dependencies + - @backstage/core-components@0.18.3-next.0 + - @backstage/frontend-plugin-api@0.12.2-next.0 + - @backstage/errors@1.2.7 + - @backstage/theme@0.7.0 + +## @backstage/plugin-auth-backend@0.25.6-next.0 + +### Patch Changes + +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/plugin-auth-node@0.6.9-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-catalog-node@1.19.2-next.0 + +## @backstage/plugin-auth-backend-module-atlassian-provider@0.4.9-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.6.9-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + +## @backstage/plugin-auth-backend-module-auth0-provider@0.2.9-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.6.9-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + +## @backstage/plugin-auth-backend-module-aws-alb-provider@0.4.9-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-backend@0.25.6-next.0 + - @backstage/plugin-auth-node@0.6.9-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/errors@1.2.7 + +## @backstage/plugin-auth-backend-module-azure-easyauth-provider@0.2.14-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.6.9-next.0 + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/errors@1.2.7 + +## @backstage/plugin-auth-backend-module-bitbucket-provider@0.3.9-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.6.9-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + +## @backstage/plugin-auth-backend-module-bitbucket-server-provider@0.2.9-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.6.9-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + +## @backstage/plugin-auth-backend-module-cloudflare-access-provider@0.4.9-next.0 + +### Patch Changes + +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/plugin-auth-node@0.6.9-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/errors@1.2.7 + +## @backstage/plugin-auth-backend-module-gcp-iap-provider@0.4.9-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.6.9-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + +## @backstage/plugin-auth-backend-module-github-provider@0.3.9-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.6.9-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + +## @backstage/plugin-auth-backend-module-gitlab-provider@0.3.9-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.6.9-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + +## @backstage/plugin-auth-backend-module-google-provider@0.3.9-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.6.9-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + +## @backstage/plugin-auth-backend-module-guest-provider@0.2.14-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.6.9-next.0 + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/errors@1.2.7 + +## @backstage/plugin-auth-backend-module-microsoft-provider@0.3.9-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.6.9-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + +## @backstage/plugin-auth-backend-module-oauth2-provider@0.4.9-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.6.9-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + +## @backstage/plugin-auth-backend-module-oauth2-proxy-provider@0.2.14-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.6.9-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/errors@1.2.7 + +## @backstage/plugin-auth-backend-module-oidc-provider@0.4.9-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-backend@0.25.6-next.0 + - @backstage/plugin-auth-node@0.6.9-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/types@1.2.2 + +## @backstage/plugin-auth-backend-module-okta-provider@0.2.9-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.6.9-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + +## @backstage/plugin-auth-backend-module-onelogin-provider@0.3.9-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.6.9-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + +## @backstage/plugin-auth-backend-module-openshift-provider@0.1.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.6.9-next.0 + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/types@1.2.2 + +## @backstage/plugin-auth-backend-module-pinniped-provider@0.3.9-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.6.9-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/types@1.2.2 + +## @backstage/plugin-auth-backend-module-vmware-cloud-provider@0.5.9-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.6.9-next.0 + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + +## @backstage/plugin-auth-node@0.6.9-next.0 + +### Patch Changes + +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/config@1.3.6-next.0 + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/catalog-client@1.12.1-next.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + +## @backstage/plugin-auth-react@0.1.21-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-plugin-api@1.11.2-next.0 + - @backstage/core-components@0.18.3-next.0 + - @backstage/errors@1.2.7 + +## @backstage/plugin-bitbucket-cloud-common@0.3.4-next.0 + +### Patch Changes + +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/integration@1.18.2-next.0 + +## @backstage/plugin-catalog@1.31.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-react@0.4.38-next.0 + - @backstage/plugin-search-react@1.9.6-next.0 + - @backstage/plugin-catalog-react@1.21.3-next.0 + - @backstage/core-plugin-api@1.11.2-next.0 + - @backstage/core-components@0.18.3-next.0 + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/frontend-plugin-api@0.12.2-next.0 + - @backstage/catalog-client@1.12.1-next.0 + - @backstage/core-compat-api@0.5.4-next.0 + - @backstage/errors@1.2.7 + - @backstage/integration-react@1.2.12-next.0 + - @backstage/types@1.2.2 + - @backstage/version-bridge@1.0.11 + - @backstage/plugin-catalog-common@1.1.7-next.0 + - @backstage/plugin-scaffolder-common@1.7.3-next.0 + - @backstage/plugin-search-common@1.2.21-next.0 + - @backstage/plugin-techdocs-common@0.1.1 + - @backstage/plugin-techdocs-react@1.3.5-next.0 + +## @backstage/plugin-catalog-backend@3.1.3-next.0 + +### Patch Changes + +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/plugin-events-node@0.4.17-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/integration@1.18.2-next.0 + - @backstage/plugin-permission-node@0.10.6-next.0 + - @backstage/backend-openapi-utils@0.6.3-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/catalog-client@1.12.1-next.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-catalog-common@1.1.7-next.0 + - @backstage/plugin-catalog-node@1.19.2-next.0 + - @backstage/plugin-permission-common@0.9.3-next.0 + +## @backstage/plugin-catalog-backend-module-aws@0.4.17-next.0 + +### Patch Changes + +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/backend-defaults@0.13.1-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/integration@1.18.2-next.0 + - @backstage/integration-aws-node@0.1.19-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/errors@1.2.7 + - @backstage/plugin-catalog-common@1.1.7-next.0 + - @backstage/plugin-catalog-node@1.19.2-next.0 + - @backstage/plugin-kubernetes-common@0.9.8-next.0 + +## @backstage/plugin-catalog-backend-module-azure@0.3.11-next.0 + +### Patch Changes + +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/config@1.3.6-next.0 + - @backstage/integration@1.18.2-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/plugin-catalog-common@1.1.7-next.0 + - @backstage/plugin-catalog-node@1.19.2-next.0 + +## @backstage/plugin-catalog-backend-module-backstage-openapi@0.5.8-next.0 + +### Patch Changes + +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/config@1.3.6-next.0 + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/backend-openapi-utils@0.6.3-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/errors@1.2.7 + - @backstage/plugin-catalog-node@1.19.2-next.0 + +## @backstage/plugin-catalog-backend-module-bitbucket-cloud@0.5.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-events-node@0.4.17-next.0 + - @backstage/plugin-bitbucket-cloud-common@0.3.4-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/integration@1.18.2-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/plugin-catalog-common@1.1.7-next.0 + - @backstage/plugin-catalog-node@1.19.2-next.0 + +## @backstage/plugin-catalog-backend-module-bitbucket-server@0.5.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-events-node@0.4.17-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/integration@1.18.2-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/errors@1.2.7 + - @backstage/plugin-catalog-common@1.1.7-next.0 + - @backstage/plugin-catalog-node@1.19.2-next.0 + +## @backstage/plugin-catalog-backend-module-gcp@0.3.14-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.6-next.0 + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/plugin-catalog-node@1.19.2-next.0 + - @backstage/plugin-kubernetes-common@0.9.8-next.0 + +## @backstage/plugin-catalog-backend-module-gerrit@0.3.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.6-next.0 + - @backstage/integration@1.18.2-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/errors@1.2.7 + - @backstage/plugin-catalog-common@1.1.7-next.0 + - @backstage/plugin-catalog-node@1.19.2-next.0 + +## @backstage/plugin-catalog-backend-module-gitea@0.1.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.6-next.0 + - @backstage/integration@1.18.2-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/errors@1.2.7 + - @backstage/plugin-catalog-common@1.1.7-next.0 + - @backstage/plugin-catalog-node@1.19.2-next.0 + +## @backstage/plugin-catalog-backend-module-github@0.11.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-events-node@0.4.17-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/integration@1.18.2-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/plugin-catalog-common@1.1.7-next.0 + - @backstage/plugin-catalog-node@1.19.2-next.0 + +## @backstage/plugin-catalog-backend-module-github-org@0.3.16-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-events-node@0.4.17-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/plugin-catalog-backend-module-github@0.11.2-next.0 + - @backstage/plugin-catalog-node@1.19.2-next.0 + +## @backstage/plugin-catalog-backend-module-gitlab@0.7.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.13.1-next.0 + - @backstage/plugin-events-node@0.4.17-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/integration@1.18.2-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/plugin-catalog-common@1.1.7-next.0 + - @backstage/plugin-catalog-node@1.19.2-next.0 + +## @backstage/plugin-catalog-backend-module-gitlab-org@0.2.15-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-events-node@0.4.17-next.0 + - @backstage/plugin-catalog-backend-module-gitlab@0.7.5-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/plugin-catalog-node@1.19.2-next.0 + +## @backstage/plugin-catalog-backend-module-incremental-ingestion@0.7.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.13.1-next.0 + - @backstage/plugin-catalog-backend@3.1.3-next.0 + - @backstage/plugin-events-node@0.4.17-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-catalog-node@1.19.2-next.0 + - @backstage/plugin-permission-common@0.9.3-next.0 + +## @backstage/plugin-catalog-backend-module-ldap@0.11.11-next.0 + +### Patch Changes + +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/config@1.3.6-next.0 + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-catalog-common@1.1.7-next.0 + - @backstage/plugin-catalog-node@1.19.2-next.0 + +## @backstage/plugin-catalog-backend-module-logs@0.1.16-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@3.1.3-next.0 + - @backstage/plugin-events-node@0.4.17-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + +## @backstage/plugin-catalog-backend-module-msgraph@0.8.2-next.0 + +### Patch Changes + +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/config@1.3.6-next.0 + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/plugin-catalog-common@1.1.7-next.0 + - @backstage/plugin-catalog-node@1.19.2-next.0 + +## @backstage/plugin-catalog-backend-module-openapi@0.2.16-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/integration@1.18.2-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/types@1.2.2 + - @backstage/plugin-catalog-common@1.1.7-next.0 + - @backstage/plugin-catalog-node@1.19.2-next.0 + +## @backstage/plugin-catalog-backend-module-puppetdb@0.2.16-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.6-next.0 + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-catalog-node@1.19.2-next.0 + +## @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.2.14-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/plugin-catalog-common@1.1.7-next.0 + - @backstage/plugin-catalog-node@1.19.2-next.0 + - @backstage/plugin-scaffolder-common@1.7.3-next.0 + +## @backstage/plugin-catalog-backend-module-unprocessed@0.6.6-next.0 + +### Patch Changes + +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/plugin-auth-node@0.6.9-next.0 + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/errors@1.2.7 + - @backstage/plugin-catalog-node@1.19.2-next.0 + - @backstage/plugin-catalog-unprocessed-entities-common@0.0.11-next.0 + - @backstage/plugin-permission-common@0.9.3-next.0 + +## @backstage/plugin-catalog-common@1.1.7-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/plugin-permission-common@0.9.3-next.0 + - @backstage/plugin-search-common@1.2.21-next.0 + +## @backstage/plugin-catalog-graph@0.5.3-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.3-next.0 + - @backstage/core-plugin-api@1.11.2-next.0 + - @backstage/core-components@0.18.3-next.0 + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/frontend-plugin-api@0.12.2-next.0 + - @backstage/catalog-client@1.12.1-next.0 + - @backstage/core-compat-api@0.5.4-next.0 + - @backstage/types@1.2.2 + +## @backstage/plugin-catalog-import@0.13.7-next.0 + +### Patch Changes + +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.3-next.0 + - @backstage/core-plugin-api@1.11.2-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/core-components@0.18.3-next.0 + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/integration@1.18.2-next.0 + - @backstage/frontend-plugin-api@0.12.2-next.0 + - @backstage/catalog-client@1.12.1-next.0 + - @backstage/core-compat-api@0.5.4-next.0 + - @backstage/errors@1.2.7 + - @backstage/integration-react@1.2.12-next.0 + - @backstage/plugin-catalog-common@1.1.7-next.0 + +## @backstage/plugin-catalog-node@1.19.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/plugin-permission-node@0.10.6-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/catalog-client@1.12.1-next.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-catalog-common@1.1.7-next.0 + - @backstage/plugin-permission-common@0.9.3-next.0 + +## @backstage/plugin-catalog-react@1.21.3-next.0 + +### Patch Changes + +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/plugin-permission-react@0.4.38-next.0 + - @backstage/core-plugin-api@1.11.2-next.0 + - @backstage/core-components@0.18.3-next.0 + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/frontend-plugin-api@0.12.2-next.0 + - @backstage/catalog-client@1.12.1-next.0 + - @backstage/core-compat-api@0.5.4-next.0 + - @backstage/errors@1.2.7 + - @backstage/frontend-test-utils@0.4.1-next.0 + - @backstage/integration-react@1.2.12-next.0 + - @backstage/types@1.2.2 + - @backstage/version-bridge@1.0.11 + - @backstage/plugin-catalog-common@1.1.7-next.0 + - @backstage/plugin-permission-common@0.9.3-next.0 + +## @backstage/plugin-catalog-unprocessed-entities@0.2.23-next.0 + +### Patch Changes + +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/core-plugin-api@1.11.2-next.0 + - @backstage/core-components@0.18.3-next.0 + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/frontend-plugin-api@0.12.2-next.0 + - @backstage/core-compat-api@0.5.4-next.0 + - @backstage/errors@1.2.7 + +## @backstage/plugin-catalog-unprocessed-entities-common@0.0.11-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-common@0.9.3-next.0 + +## @backstage/plugin-config-schema@0.1.74-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-plugin-api@1.11.2-next.0 + - @backstage/core-components@0.18.3-next.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + +## @backstage/plugin-devtools@0.1.33-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-react@0.4.38-next.0 + - @backstage/core-plugin-api@1.11.2-next.0 + - @backstage/core-components@0.18.3-next.0 + - @backstage/frontend-plugin-api@0.12.2-next.0 + - @backstage/core-compat-api@0.5.4-next.0 + - @backstage/errors@1.2.7 + - @backstage/plugin-devtools-common@0.1.19-next.0 + +## @backstage/plugin-devtools-backend@0.5.11-next.0 + +### Patch Changes + +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/backend-defaults@0.13.1-next.0 + - @backstage/config-loader@1.10.6-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/plugin-permission-node@0.10.6-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/cli-common@0.1.15 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-devtools-common@0.1.19-next.0 + - @backstage/plugin-permission-common@0.9.3-next.0 + +## @backstage/plugin-devtools-common@0.1.19-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/types@1.2.2 + - @backstage/plugin-permission-common@0.9.3-next.0 + +## @backstage/plugin-events-backend@0.5.8-next.0 + +### Patch Changes + +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/plugin-events-node@0.4.17-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/backend-openapi-utils@0.6.3-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + +## @backstage/plugin-events-backend-module-aws-sqs@0.4.17-next.0 + +### Patch Changes + +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/plugin-events-node@0.4.17-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/types@1.2.2 + +## @backstage/plugin-events-backend-module-azure@0.2.26-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-events-node@0.4.17-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + +## @backstage/plugin-events-backend-module-bitbucket-cloud@0.2.26-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-events-node@0.4.17-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + +## @backstage/plugin-events-backend-module-bitbucket-server@0.1.7-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-events-node@0.4.17-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + +## @backstage/plugin-events-backend-module-gerrit@0.2.26-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-events-node@0.4.17-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + +## @backstage/plugin-events-backend-module-github@0.4.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-events-node@0.4.17-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/integration@1.18.2-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/types@1.2.2 + +## @backstage/plugin-events-backend-module-gitlab@0.3.7-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-events-node@0.4.17-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + +## @backstage/plugin-events-backend-module-google-pubsub@0.1.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-events-node@0.4.17-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + +## @backstage/plugin-events-backend-module-kafka@0.1.5-next.0 + +### Patch Changes + +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/plugin-events-node@0.4.17-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/types@1.2.2 + +## @backstage/plugin-events-backend-test-utils@0.1.50-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-events-node@0.4.17-next.0 + +## @backstage/plugin-events-node@0.4.17-next.0 + +### Patch Changes + +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + +## @backstage/plugin-gateway-backend@1.0.7-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.5-next.0 + +## @backstage/plugin-home@0.8.14-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.3-next.0 + - @backstage/core-app-api@1.19.2-next.0 + - @backstage/core-plugin-api@1.11.2-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/core-components@0.18.3-next.0 + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/frontend-plugin-api@0.12.2-next.0 + - @backstage/catalog-client@1.12.1-next.0 + - @backstage/core-compat-api@0.5.4-next.0 + - @backstage/theme@0.7.0 + - @backstage/plugin-home-react@0.1.32-next.0 + +## @backstage/plugin-home-react@0.1.32-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-plugin-api@1.11.2-next.0 + - @backstage/core-components@0.18.3-next.0 + - @backstage/frontend-plugin-api@0.12.2-next.0 + +## @backstage/plugin-kubernetes@0.12.13-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-kubernetes-react@0.5.13-next.0 + - @backstage/plugin-permission-react@0.4.38-next.0 + - @backstage/plugin-catalog-react@1.21.3-next.0 + - @backstage/core-plugin-api@1.11.2-next.0 + - @backstage/core-components@0.18.3-next.0 + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/frontend-plugin-api@0.12.2-next.0 + - @backstage/core-compat-api@0.5.4-next.0 + - @backstage/plugin-kubernetes-common@0.9.8-next.0 + +## @backstage/plugin-kubernetes-backend@0.20.4-next.0 + +### Patch Changes + +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/plugin-auth-node@0.6.9-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/integration-aws-node@0.1.19-next.0 + - @backstage/plugin-kubernetes-node@0.3.6-next.0 + - @backstage/plugin-permission-node@0.10.6-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/catalog-client@1.12.1-next.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-catalog-node@1.19.2-next.0 + - @backstage/plugin-kubernetes-common@0.9.8-next.0 + - @backstage/plugin-permission-common@0.9.3-next.0 + +## @backstage/plugin-kubernetes-cluster@0.0.31-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-kubernetes-react@0.5.13-next.0 + - @backstage/plugin-permission-react@0.4.38-next.0 + - @backstage/plugin-catalog-react@1.21.3-next.0 + - @backstage/core-plugin-api@1.11.2-next.0 + - @backstage/core-components@0.18.3-next.0 + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/plugin-kubernetes-common@0.9.8-next.0 + +## @backstage/plugin-kubernetes-common@0.9.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/types@1.2.2 + - @backstage/plugin-permission-common@0.9.3-next.0 + +## @backstage/plugin-kubernetes-node@0.3.6-next.0 + +### Patch Changes + +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/types@1.2.2 + - @backstage/plugin-kubernetes-common@0.9.8-next.0 + +## @backstage/plugin-kubernetes-react@0.5.13-next.0 + +### Patch Changes + +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/core-plugin-api@1.11.2-next.0 + - @backstage/core-components@0.18.3-next.0 + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-kubernetes-common@0.9.8-next.0 + +## @backstage/plugin-mcp-actions-backend@0.1.5-next.0 + +### Patch Changes + +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/backend-defaults@0.13.1-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/catalog-client@1.12.1-next.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-catalog-node@1.19.2-next.0 + +## @backstage/plugin-mui-to-bui@0.2.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/ui@0.8.2-next.0 + - @backstage/core-plugin-api@1.11.2-next.0 + - @backstage/frontend-plugin-api@0.12.2-next.0 + - @backstage/core-compat-api@0.5.4-next.0 + - @backstage/theme@0.7.0 + +## @backstage/plugin-notifications@0.5.11-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-plugin-api@1.11.2-next.0 + - @backstage/core-components@0.18.3-next.0 + - @backstage/frontend-plugin-api@0.12.2-next.0 + - @backstage/core-compat-api@0.5.4-next.0 + - @backstage/errors@1.2.7 + - @backstage/theme@0.7.0 + - @backstage/plugin-notifications-common@0.1.2-next.0 + - @backstage/plugin-signals-react@0.0.17-next.0 + +## @backstage/plugin-notifications-backend@0.5.12-next.0 + +### Patch Changes + +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/plugin-notifications-node@0.2.21-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-catalog-node@1.19.2-next.0 + - @backstage/plugin-notifications-common@0.1.2-next.0 + - @backstage/plugin-signals-node@0.1.26-next.0 + +## @backstage/plugin-notifications-backend-module-email@0.3.15-next.0 + +### Patch Changes + +- 22a5362: Updated `AWS SES` client to version 2 to support `nodemailer` version 7. +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/plugin-notifications-node@0.2.21-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/integration-aws-node@0.1.19-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/catalog-client@1.12.1-next.0 + - @backstage/types@1.2.2 + - @backstage/plugin-catalog-node@1.19.2-next.0 + - @backstage/plugin-notifications-common@0.1.2-next.0 + +## @backstage/plugin-notifications-backend-module-slack@0.2.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-notifications-node@0.2.21-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-catalog-node@1.19.2-next.0 + - @backstage/plugin-notifications-common@0.1.2-next.0 + +## @backstage/plugin-notifications-common@0.1.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.6-next.0 + - @backstage/types@1.2.2 + +## @backstage/plugin-notifications-node@0.2.21-next.0 + +### Patch Changes + +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/catalog-client@1.12.1-next.0 + - @backstage/plugin-notifications-common@0.1.2-next.0 + - @backstage/plugin-signals-node@0.1.26-next.0 + +## @backstage/plugin-org@0.6.46-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.3-next.0 + - @backstage/core-plugin-api@1.11.2-next.0 + - @backstage/core-components@0.18.3-next.0 + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/frontend-plugin-api@0.12.2-next.0 + - @backstage/core-compat-api@0.5.4-next.0 + - @backstage/plugin-catalog-common@1.1.7-next.0 + +## @backstage/plugin-org-react@0.1.44-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.3-next.0 + - @backstage/core-plugin-api@1.11.2-next.0 + - @backstage/core-components@0.18.3-next.0 + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/catalog-client@1.12.1-next.0 + +## @backstage/plugin-permission-backend@0.7.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.6.9-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/plugin-permission-node@0.10.6-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/errors@1.2.7 + - @backstage/plugin-permission-common@0.9.3-next.0 + +## @backstage/plugin-permission-backend-module-allow-all-policy@0.2.14-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.6.9-next.0 + - @backstage/plugin-permission-node@0.10.6-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/plugin-permission-common@0.9.3-next.0 + +## @backstage/plugin-permission-common@0.9.3-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.6-next.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + +## @backstage/plugin-permission-node@0.10.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.6.9-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/errors@1.2.7 + - @backstage/plugin-permission-common@0.9.3-next.0 + +## @backstage/plugin-permission-react@0.4.38-next.0 + +### Patch Changes + +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/core-plugin-api@1.11.2-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/plugin-permission-common@0.9.3-next.0 + +## @backstage/plugin-proxy-backend@0.6.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/types@1.2.2 + - @backstage/plugin-proxy-node@0.1.10-next.0 + +## @backstage/plugin-proxy-node@0.1.10-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.5-next.0 + +## @backstage/plugin-scaffolder@1.34.3-next.0 + +### Patch Changes + +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/plugin-scaffolder-react@1.19.3-next.0 + - @backstage/plugin-permission-react@0.4.38-next.0 + - @backstage/plugin-catalog-react@1.21.3-next.0 + - @backstage/core-plugin-api@1.11.2-next.0 + - @backstage/core-components@0.18.3-next.0 + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/integration@1.18.2-next.0 + - @backstage/frontend-plugin-api@0.12.2-next.0 + - @backstage/catalog-client@1.12.1-next.0 + - @backstage/core-compat-api@0.5.4-next.0 + - @backstage/errors@1.2.7 + - @backstage/integration-react@1.2.12-next.0 + - @backstage/types@1.2.2 + - @backstage/plugin-catalog-common@1.1.7-next.0 + - @backstage/plugin-scaffolder-common@1.7.3-next.0 + - @backstage/plugin-techdocs-common@0.1.1 + - @backstage/plugin-techdocs-react@1.3.5-next.0 + +## @backstage/plugin-scaffolder-backend@3.0.1-next.0 + +### Patch Changes + +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/backend-defaults@0.13.1-next.0 + - @backstage/plugin-events-node@0.4.17-next.0 + - @backstage/plugin-bitbucket-cloud-common@0.3.4-next.0 + - @backstage/plugin-auth-node@0.6.9-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/integration@1.18.2-next.0 + - @backstage/plugin-scaffolder-node@0.12.1-next.0 + - @backstage/plugin-permission-node@0.10.6-next.0 + - @backstage/backend-openapi-utils@0.6.3-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.2.14-next.0 + - @backstage/plugin-catalog-node@1.19.2-next.0 + - @backstage/plugin-permission-common@0.9.3-next.0 + - @backstage/plugin-scaffolder-backend-module-azure@0.2.15-next.0 + - @backstage/plugin-scaffolder-backend-module-bitbucket@0.3.16-next.0 + - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.15-next.0 + - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.15-next.0 + - @backstage/plugin-scaffolder-backend-module-gerrit@0.2.15-next.0 + - @backstage/plugin-scaffolder-backend-module-gitea@0.2.15-next.0 + - @backstage/plugin-scaffolder-backend-module-github@0.9.2-next.0 + - @backstage/plugin-scaffolder-backend-module-gitlab@0.9.7-next.0 + - @backstage/plugin-scaffolder-common@1.7.3-next.0 + +## @backstage/plugin-scaffolder-backend-module-azure@0.2.15-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.6-next.0 + - @backstage/integration@1.18.2-next.0 + - @backstage/plugin-scaffolder-node@0.12.1-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/errors@1.2.7 + +## @backstage/plugin-scaffolder-backend-module-bitbucket@0.3.16-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.6-next.0 + - @backstage/integration@1.18.2-next.0 + - @backstage/plugin-scaffolder-node@0.12.1-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/errors@1.2.7 + - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.15-next.0 + - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.15-next.0 + +## @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.15-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-bitbucket-cloud-common@0.3.4-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/integration@1.18.2-next.0 + - @backstage/plugin-scaffolder-node@0.12.1-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/errors@1.2.7 + +## @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.15-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.6-next.0 + - @backstage/integration@1.18.2-next.0 + - @backstage/plugin-scaffolder-node@0.12.1-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/errors@1.2.7 + +## @backstage/plugin-scaffolder-backend-module-confluence-to-markdown@0.3.15-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.6-next.0 + - @backstage/integration@1.18.2-next.0 + - @backstage/plugin-scaffolder-node@0.12.1-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/errors@1.2.7 + +## @backstage/plugin-scaffolder-backend-module-cookiecutter@0.3.17-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.13.1-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/integration@1.18.2-next.0 + - @backstage/plugin-scaffolder-node@0.12.1-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + +## @backstage/plugin-scaffolder-backend-module-gcp@0.2.15-next.0 + +### Patch Changes + +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/config@1.3.6-next.0 + - @backstage/integration@1.18.2-next.0 + - @backstage/plugin-scaffolder-node@0.12.1-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/errors@1.2.7 + +## @backstage/plugin-scaffolder-backend-module-gerrit@0.2.15-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.6-next.0 + - @backstage/integration@1.18.2-next.0 + - @backstage/plugin-scaffolder-node@0.12.1-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/errors@1.2.7 + +## @backstage/plugin-scaffolder-backend-module-gitea@0.2.15-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.6-next.0 + - @backstage/integration@1.18.2-next.0 + - @backstage/plugin-scaffolder-node@0.12.1-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/errors@1.2.7 + +## @backstage/plugin-scaffolder-backend-module-github@0.9.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.6-next.0 + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/integration@1.18.2-next.0 + - @backstage/plugin-scaffolder-node@0.12.1-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-catalog-node@1.19.2-next.0 + +## @backstage/plugin-scaffolder-backend-module-gitlab@0.9.7-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.6-next.0 + - @backstage/integration@1.18.2-next.0 + - @backstage/plugin-scaffolder-node@0.12.1-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/errors@1.2.7 + +## @backstage/plugin-scaffolder-backend-module-notifications@0.1.16-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-notifications-node@0.2.21-next.0 + - @backstage/plugin-scaffolder-node@0.12.1-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/plugin-notifications-common@0.1.2-next.0 + +## @backstage/plugin-scaffolder-backend-module-rails@0.5.15-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.6-next.0 + - @backstage/integration@1.18.2-next.0 + - @backstage/plugin-scaffolder-node@0.12.1-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + +## @backstage/plugin-scaffolder-backend-module-sentry@0.2.15-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.6-next.0 + - @backstage/plugin-scaffolder-node@0.12.1-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/errors@1.2.7 + +## @backstage/plugin-scaffolder-backend-module-yeoman@0.4.16-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node-test-utils@0.3.5-next.0 + - @backstage/plugin-scaffolder-node@0.12.1-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/types@1.2.2 + +## @backstage/plugin-scaffolder-common@1.7.3-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/integration@1.18.2-next.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-permission-common@0.9.3-next.0 + +## @backstage/plugin-scaffolder-node@0.12.1-next.0 + +### Patch Changes + +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/integration@1.18.2-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-permission-common@0.9.3-next.0 + - @backstage/plugin-scaffolder-common@1.7.3-next.0 + +## @backstage/plugin-scaffolder-node-test-utils@0.3.5-next.0 + +### Patch Changes + +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/backend-test-utils@1.10.0-next.0 + - @backstage/plugin-scaffolder-node@0.12.1-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/types@1.2.2 + +## @backstage/plugin-scaffolder-react@1.19.3-next.0 + +### Patch Changes + +- 886a8a1: Fixed a bug in the Scaffolder's template parsing in the `useTemplateSchema` hook by removing the title instead of setting it to `undefined` +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/plugin-permission-react@0.4.38-next.0 + - @backstage/plugin-catalog-react@1.21.3-next.0 + - @backstage/core-plugin-api@1.11.2-next.0 + - @backstage/core-components@0.18.3-next.0 + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/frontend-plugin-api@0.12.2-next.0 + - @backstage/catalog-client@1.12.1-next.0 + - @backstage/theme@0.7.0 + - @backstage/types@1.2.2 + - @backstage/version-bridge@1.0.11 + - @backstage/plugin-scaffolder-common@1.7.3-next.0 + +## @backstage/plugin-search@1.4.32-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-react@1.9.6-next.0 + - @backstage/plugin-catalog-react@1.21.3-next.0 + - @backstage/core-plugin-api@1.11.2-next.0 + - @backstage/core-components@0.18.3-next.0 + - @backstage/frontend-plugin-api@0.12.2-next.0 + - @backstage/core-compat-api@0.5.4-next.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/version-bridge@1.0.11 + - @backstage/plugin-search-common@1.2.21-next.0 + +## @backstage/plugin-search-backend@2.0.8-next.0 + +### Patch Changes + +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/backend-defaults@0.13.1-next.0 + - @backstage/plugin-search-backend-node@1.3.17-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/plugin-permission-node@0.10.6-next.0 + - @backstage/backend-openapi-utils@0.6.3-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-permission-common@0.9.3-next.0 + - @backstage/plugin-search-common@1.2.21-next.0 + +## @backstage/plugin-search-backend-module-catalog@0.3.10-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-backend-node@1.3.17-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/catalog-client@1.12.1-next.0 + - @backstage/errors@1.2.7 + - @backstage/plugin-catalog-common@1.1.7-next.0 + - @backstage/plugin-catalog-node@1.19.2-next.0 + - @backstage/plugin-permission-common@0.9.3-next.0 + - @backstage/plugin-search-common@1.2.21-next.0 + +## @backstage/plugin-search-backend-module-elasticsearch@1.7.8-next.0 + +### Patch Changes + +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/plugin-search-backend-node@1.3.17-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/integration-aws-node@0.1.19-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/plugin-search-common@1.2.21-next.0 + +## @backstage/plugin-search-backend-module-explore@0.3.9-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-backend-node@1.3.17-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/plugin-search-common@1.2.21-next.0 + +## @backstage/plugin-search-backend-module-pg@0.5.50-next.0 + +### Patch Changes + +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/plugin-search-backend-node@1.3.17-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/plugin-search-common@1.2.21-next.0 + +## @backstage/plugin-search-backend-module-stack-overflow-collator@0.3.15-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-backend-node@1.3.17-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/plugin-search-common@1.2.21-next.0 + +## @backstage/plugin-search-backend-module-techdocs@0.4.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-backend-node@1.3.17-next.0 + - @backstage/plugin-techdocs-node@1.13.9-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/catalog-client@1.12.1-next.0 + - @backstage/plugin-catalog-common@1.1.7-next.0 + - @backstage/plugin-catalog-node@1.19.2-next.0 + - @backstage/plugin-permission-common@0.9.3-next.0 + - @backstage/plugin-search-common@1.2.21-next.0 + +## @backstage/plugin-search-backend-node@1.3.17-next.0 + +### Patch Changes + +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/config@1.3.6-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/errors@1.2.7 + - @backstage/plugin-permission-common@0.9.3-next.0 + - @backstage/plugin-search-common@1.2.21-next.0 + +## @backstage/plugin-search-common@1.2.21-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/types@1.2.2 + - @backstage/plugin-permission-common@0.9.3-next.0 + +## @backstage/plugin-search-react@1.9.6-next.0 + +### Patch Changes + +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/core-plugin-api@1.11.2-next.0 + - @backstage/core-components@0.18.3-next.0 + - @backstage/frontend-plugin-api@0.12.2-next.0 + - @backstage/theme@0.7.0 + - @backstage/types@1.2.2 + - @backstage/version-bridge@1.0.11 + - @backstage/plugin-search-common@1.2.21-next.0 + +## @backstage/plugin-signals@0.0.25-next.0 + +### Patch Changes + +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/core-plugin-api@1.11.2-next.0 + - @backstage/core-components@0.18.3-next.0 + - @backstage/frontend-plugin-api@0.12.2-next.0 + - @backstage/core-compat-api@0.5.4-next.0 + - @backstage/theme@0.7.0 + - @backstage/types@1.2.2 + - @backstage/plugin-signals-react@0.0.17-next.0 + +## @backstage/plugin-signals-backend@0.3.10-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-events-node@0.4.17-next.0 + - @backstage/plugin-auth-node@0.6.9-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/types@1.2.2 + - @backstage/plugin-signals-node@0.1.26-next.0 + +## @backstage/plugin-signals-node@0.1.26-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-events-node@0.4.17-next.0 + - @backstage/plugin-auth-node@0.6.9-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/types@1.2.2 + +## @backstage/plugin-signals-react@0.0.17-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-plugin-api@1.11.2-next.0 + - @backstage/types@1.2.2 + +## @backstage/plugin-techdocs@1.15.2-next.0 + +### Patch Changes + +- a4d4a70: Fixed an issue where the entire TechDocs page would re-render when navigating between pages within the same entity's documentation. +- Updated dependencies + - @backstage/plugin-search-react@1.9.6-next.0 + - @backstage/plugin-catalog-react@1.21.3-next.0 + - @backstage/core-plugin-api@1.11.2-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/core-components@0.18.3-next.0 + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/integration@1.18.2-next.0 + - @backstage/frontend-plugin-api@0.12.2-next.0 + - @backstage/catalog-client@1.12.1-next.0 + - @backstage/core-compat-api@0.5.4-next.0 + - @backstage/errors@1.2.7 + - @backstage/integration-react@1.2.12-next.0 + - @backstage/theme@0.7.0 + - @backstage/plugin-auth-react@0.1.21-next.0 + - @backstage/plugin-search-common@1.2.21-next.0 + - @backstage/plugin-techdocs-common@0.1.1 + - @backstage/plugin-techdocs-react@1.3.5-next.0 + +## @backstage/plugin-techdocs-addons-test-utils@1.1.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-techdocs@1.15.2-next.0 + - @backstage/plugin-search-react@1.9.6-next.0 + - @backstage/plugin-catalog-react@1.21.3-next.0 + - @backstage/core-app-api@1.19.2-next.0 + - @backstage/core-plugin-api@1.11.2-next.0 + - @backstage/integration-react@1.2.12-next.0 + - @backstage/test-utils@1.7.13-next.0 + - @backstage/plugin-catalog@1.31.5-next.0 + - @backstage/plugin-techdocs-react@1.3.5-next.0 + +## @backstage/plugin-techdocs-backend@2.1.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.13.1-next.0 + - @backstage/plugin-techdocs-node@1.13.9-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/integration@1.18.2-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/catalog-client@1.12.1-next.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-catalog-common@1.1.7-next.0 + - @backstage/plugin-catalog-node@1.19.2-next.0 + - @backstage/plugin-permission-common@0.9.3-next.0 + - @backstage/plugin-search-backend-module-techdocs@0.4.8-next.0 + - @backstage/plugin-techdocs-common@0.1.1 + +## @backstage/plugin-techdocs-module-addons-contrib@1.1.30-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-plugin-api@1.11.2-next.0 + - @backstage/core-components@0.18.3-next.0 + - @backstage/integration@1.18.2-next.0 + - @backstage/frontend-plugin-api@0.12.2-next.0 + - @backstage/integration-react@1.2.12-next.0 + - @backstage/plugin-techdocs-react@1.3.5-next.0 + +## @backstage/plugin-techdocs-node@1.13.9-next.0 + +### Patch Changes + +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/config@1.3.6-next.0 + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/integration@1.18.2-next.0 + - @backstage/integration-aws-node@0.1.19-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/errors@1.2.7 + - @backstage/plugin-search-common@1.2.21-next.0 + - @backstage/plugin-techdocs-common@0.1.1 + +## @backstage/plugin-techdocs-react@1.3.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-plugin-api@1.11.2-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/core-components@0.18.3-next.0 + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/frontend-plugin-api@0.12.2-next.0 + - @backstage/version-bridge@1.0.11 + - @backstage/plugin-techdocs-common@0.1.1 + +## @backstage/plugin-user-settings@0.8.29-next.0 + +### Patch Changes + +- 2b6fda3: Revert `storageApiRef` implementation +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.3-next.0 + - @backstage/core-app-api@1.19.2-next.0 + - @backstage/core-plugin-api@1.11.2-next.0 + - @backstage/core-components@0.18.3-next.0 + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/frontend-plugin-api@0.12.2-next.0 + - @backstage/core-compat-api@0.5.4-next.0 + - @backstage/errors@1.2.7 + - @backstage/theme@0.7.0 + - @backstage/types@1.2.2 + - @backstage/plugin-signals-react@0.0.17-next.0 + - @backstage/plugin-user-settings-common@0.0.1 + +## @backstage/plugin-user-settings-backend@0.3.8-next.0 + +### Patch Changes + +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/backend-defaults@0.13.1-next.0 + - @backstage/plugin-auth-node@0.6.9-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-signals-node@0.1.26-next.0 + - @backstage/plugin-user-settings-common@0.0.1 + +## example-app@0.2.115-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/cli@0.34.5-next.0 + - @backstage/ui@0.8.2-next.0 + - @backstage/plugin-scaffolder-react@1.19.3-next.0 + - @backstage/plugin-techdocs@1.15.2-next.0 + - @backstage/plugin-user-settings@0.8.29-next.0 + - @backstage/plugin-permission-react@0.4.38-next.0 + - @backstage/plugin-search-react@1.9.6-next.0 + - @backstage/plugin-signals@0.0.25-next.0 + - @backstage/plugin-catalog-react@1.21.3-next.0 + - @backstage/plugin-scaffolder@1.34.3-next.0 + - @backstage/plugin-catalog-unprocessed-entities@0.2.23-next.0 + - @backstage/core-app-api@1.19.2-next.0 + - @backstage/core-plugin-api@1.11.2-next.0 + - @backstage/frontend-app-api@0.13.2-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/core-components@0.18.3-next.0 + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/plugin-catalog-import@0.13.7-next.0 + - @backstage/app-defaults@1.7.2-next.0 + - @backstage/integration-react@1.2.12-next.0 + - @backstage/theme@0.7.0 + - @backstage/plugin-api-docs@0.13.1-next.0 + - @backstage/plugin-auth-react@0.1.21-next.0 + - @backstage/plugin-catalog@1.31.5-next.0 + - @backstage/plugin-catalog-common@1.1.7-next.0 + - @backstage/plugin-catalog-graph@0.5.3-next.0 + - @backstage/plugin-devtools@0.1.33-next.0 + - @backstage/plugin-home@0.8.14-next.0 + - @backstage/plugin-kubernetes@0.12.13-next.0 + - @backstage/plugin-kubernetes-cluster@0.0.31-next.0 + - @backstage/plugin-mui-to-bui@0.2.1-next.0 + - @backstage/plugin-notifications@0.5.11-next.0 + - @backstage/plugin-org@0.6.46-next.0 + - @backstage/plugin-search@1.4.32-next.0 + - @backstage/plugin-search-common@1.2.21-next.0 + - @backstage/plugin-techdocs-module-addons-contrib@1.1.30-next.0 + - @backstage/plugin-techdocs-react@1.3.5-next.0 + +## example-app-next@0.0.29-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/cli@0.34.5-next.0 + - @backstage/ui@0.8.2-next.0 + - @backstage/plugin-scaffolder-react@1.19.3-next.0 + - @backstage/plugin-techdocs@1.15.2-next.0 + - @backstage/plugin-user-settings@0.8.29-next.0 + - @backstage/plugin-auth@0.1.2-next.0 + - @backstage/plugin-permission-react@0.4.38-next.0 + - @backstage/plugin-search-react@1.9.6-next.0 + - @backstage/plugin-signals@0.0.25-next.0 + - @backstage/plugin-catalog-react@1.21.3-next.0 + - @backstage/plugin-scaffolder@1.34.3-next.0 + - @backstage/plugin-catalog-unprocessed-entities@0.2.23-next.0 + - @backstage/core-app-api@1.19.2-next.0 + - @backstage/core-plugin-api@1.11.2-next.0 + - @backstage/frontend-app-api@0.13.2-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/core-components@0.18.3-next.0 + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/frontend-plugin-api@0.12.2-next.0 + - @backstage/plugin-catalog-import@0.13.7-next.0 + - @backstage/app-defaults@1.7.2-next.0 + - @backstage/core-compat-api@0.5.4-next.0 + - @backstage/frontend-defaults@0.3.3-next.0 + - @backstage/integration-react@1.2.12-next.0 + - @backstage/theme@0.7.0 + - @backstage/plugin-api-docs@0.13.1-next.0 + - @backstage/plugin-app@0.3.2-next.0 + - @backstage/plugin-app-visualizer@0.1.25-next.0 + - @backstage/plugin-auth-react@0.1.21-next.0 + - @backstage/plugin-catalog@1.31.5-next.0 + - @backstage/plugin-catalog-common@1.1.7-next.0 + - @backstage/plugin-catalog-graph@0.5.3-next.0 + - @backstage/plugin-home@0.8.14-next.0 + - @backstage/plugin-kubernetes@0.12.13-next.0 + - @backstage/plugin-kubernetes-cluster@0.0.31-next.0 + - @backstage/plugin-notifications@0.5.11-next.0 + - @backstage/plugin-org@0.6.46-next.0 + - @backstage/plugin-search@1.4.32-next.0 + - @backstage/plugin-search-common@1.2.21-next.0 + - @backstage/plugin-techdocs-module-addons-contrib@1.1.30-next.0 + - @backstage/plugin-techdocs-react@1.3.5-next.0 + +## app-next-example-plugin@0.0.29-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.3-next.0 + - @backstage/frontend-plugin-api@0.12.2-next.0 + +## example-backend@0.0.44-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.13.1-next.0 + - @backstage/plugin-catalog-backend@3.1.3-next.0 + - @backstage/plugin-notifications-backend@0.5.12-next.0 + - @backstage/plugin-scaffolder-backend@3.0.1-next.0 + - @backstage/plugin-search-backend-module-elasticsearch@1.7.8-next.0 + - @backstage/plugin-search-backend@2.0.8-next.0 + - @backstage/plugin-search-backend-node@1.3.17-next.0 + - @backstage/plugin-kubernetes-backend@0.20.4-next.0 + - @backstage/plugin-events-backend@0.5.8-next.0 + - @backstage/plugin-catalog-backend-module-backstage-openapi@0.5.8-next.0 + - @backstage/plugin-auth-backend@0.25.6-next.0 + - @backstage/plugin-devtools-backend@0.5.11-next.0 + - @backstage/plugin-catalog-backend-module-unprocessed@0.6.6-next.0 + - @backstage/plugin-auth-node@0.6.9-next.0 + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/plugin-mcp-actions-backend@0.1.5-next.0 + - @backstage/plugin-app-backend@0.5.8-next.0 + - @backstage/plugin-auth-backend-module-github-provider@0.3.9-next.0 + - @backstage/plugin-auth-backend-module-openshift-provider@0.1.2-next.0 + - @backstage/plugin-events-backend-module-google-pubsub@0.1.6-next.0 + - @backstage/plugin-permission-backend@0.7.6-next.0 + - @backstage/plugin-permission-node@0.10.6-next.0 + - @backstage/plugin-proxy-backend@0.6.8-next.0 + - @backstage/plugin-signals-backend@0.3.10-next.0 + - @backstage/plugin-techdocs-backend@2.1.2-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/plugin-auth-backend-module-guest-provider@0.2.14-next.0 + - @backstage/plugin-catalog-backend-module-openapi@0.2.16-next.0 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.2.14-next.0 + - @backstage/plugin-permission-backend-module-allow-all-policy@0.2.14-next.0 + - @backstage/plugin-permission-common@0.9.3-next.0 + - @backstage/plugin-scaffolder-backend-module-github@0.9.2-next.0 + - @backstage/plugin-scaffolder-backend-module-notifications@0.1.16-next.0 + - @backstage/plugin-search-backend-module-catalog@0.3.10-next.0 + - @backstage/plugin-search-backend-module-explore@0.3.9-next.0 + - @backstage/plugin-search-backend-module-techdocs@0.4.8-next.0 + +## e2e-test@0.2.34-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/create-app@0.7.6-next.0 + - @backstage/cli-common@0.1.15 + - @backstage/errors@1.2.7 + +## @internal/frontend@0.0.15-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.12.2-next.0 + - @backstage/types@1.2.2 + - @backstage/version-bridge@1.0.11 + +## @internal/scaffolder@0.0.15-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-react@1.19.3-next.0 + - @backstage/frontend-plugin-api@0.12.2-next.0 + +## techdocs-cli-embedded-app@0.2.114-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/cli@0.34.5-next.0 + - @backstage/ui@0.8.2-next.0 + - @backstage/plugin-techdocs@1.15.2-next.0 + - @backstage/core-app-api@1.19.2-next.0 + - @backstage/core-plugin-api@1.11.2-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/core-components@0.18.3-next.0 + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/app-defaults@1.7.2-next.0 + - @backstage/integration-react@1.2.12-next.0 + - @backstage/test-utils@1.7.13-next.0 + - @backstage/theme@0.7.0 + - @backstage/plugin-catalog@1.31.5-next.0 + - @backstage/plugin-techdocs-react@1.3.5-next.0 + +## @internal/plugin-todo-list@1.0.45-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-plugin-api@1.11.2-next.0 + - @backstage/core-components@0.18.3-next.0 + +## @internal/plugin-todo-list-backend@1.0.45-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/errors@1.2.7 + +## @internal/plugin-todo-list-common@1.0.28-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-common@0.9.3-next.0 diff --git a/docs/releases/v1.45.0-next.1-changelog.md b/docs/releases/v1.45.0-next.1-changelog.md new file mode 100644 index 0000000000..098dd21e02 --- /dev/null +++ b/docs/releases/v1.45.0-next.1-changelog.md @@ -0,0 +1,9366 @@ +# Release v1.45.0-next.1 + +Upgrade Helper: [https://backstage.github.io/upgrade-helper/?to=1.45.0-next.1](https://backstage.github.io/upgrade-helper/?to=1.45.0-next.1) + +## @techdocs/cli@1.10.2-next.0 + +# @techdocs/cli + +## 1.10.1-next.0 + +### Patch Changes + +- c2a2017: Fix for missing styles due to move to BUI. +- Updated dependencies + - @backstage/backend-defaults@0.13.1-next.0 + - @backstage/plugin-techdocs-node@1.13.9-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/cli-common@0.1.15 + +## 1.10.0 + +### Minor Changes + +- 43afbe5: Techdocs CLI serve supports automatic refresh, relying on `mkdocs` `watch` feature. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.13.0 + - @backstage/config@1.3.5 + - @backstage/plugin-techdocs-node@1.13.8 + +## 1.9.9-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/backend-defaults@0.13.0-next.1 + - @backstage/plugin-techdocs-node@1.13.8-next.1 + +## 1.9.9-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.13.0-next.0 + - @backstage/plugin-techdocs-node@1.13.8-next.0 + - @backstage/catalog-model@1.7.5 + - @backstage/cli-common@0.1.15 + - @backstage/config@1.3.3 + +## 1.9.8 + +### Patch Changes + +- db63208: Fixed an issue where `@techdocs/cli serve` command did not pick up the latest changes to TechDocs. +- Updated dependencies + - @backstage/backend-defaults@0.12.1 + - @backstage/plugin-techdocs-node@1.13.7 + +## 1.9.8-next.0 + +### Patch Changes + +- db63208: Fixed an issue where `@techdocs/cli serve` command did not pick up the latest changes to TechDocs. +- Updated dependencies + - @backstage/backend-defaults@0.12.1-next.0 + - @backstage/plugin-techdocs-node@1.13.7-next.0 + +## 1.9.6 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.12.0 + - @backstage/plugin-techdocs-node@1.13.6 + +## 1.9.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.11.2-next.0 + - @backstage/plugin-techdocs-node@1.13.6-next.0 + - @backstage/catalog-model@1.7.5 + - @backstage/cli-common@0.1.15 + - @backstage/config@1.3.3 + +## 1.9.5 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3 + - @backstage/catalog-model@1.7.5 + - @backstage/backend-defaults@0.11.1 + - @backstage/plugin-techdocs-node@1.13.5 + +## 1.9.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/catalog-model@1.7.5-next.0 + - @backstage/backend-defaults@0.11.1-next.1 + - @backstage/plugin-techdocs-node@1.13.5-next.1 + +## 1.9.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.11.1-next.0 + - @backstage/plugin-techdocs-node@1.13.5-next.0 + - @backstage/catalog-model@1.7.4 + - @backstage/cli-common@0.1.15 + - @backstage/config@1.3.2 + +## 1.9.4 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.11.0 + - @backstage/catalog-model@1.7.4 + - @backstage/cli-common@0.1.15 + - @backstage/config@1.3.2 + - @backstage/plugin-techdocs-node@1.13.4 + +## 1.9.4-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.11.0-next.2 + - @backstage/catalog-model@1.7.4 + - @backstage/cli-common@0.1.15 + - @backstage/config@1.3.2 + - @backstage/plugin-techdocs-node@1.13.4-next.1 + +## 1.9.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.10.1-next.1 + - @backstage/catalog-model@1.7.4 + - @backstage/cli-common@0.1.15 + - @backstage/config@1.3.2 + - @backstage/plugin-techdocs-node@1.13.4-next.1 + +## 1.9.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.10.1-next.0 + - @backstage/plugin-techdocs-node@1.13.4-next.0 + +## 1.9.3 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.10.0 + - @backstage/catalog-model@1.7.4 + - @backstage/plugin-techdocs-node@1.13.3 + - @backstage/config@1.3.2 + - @backstage/cli-common@0.1.15 + +## 1.9.3-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.10.0-next.3 + - @backstage/catalog-model@1.7.3 + - @backstage/cli-common@0.1.15 + - @backstage/config@1.3.2 + - @backstage/plugin-techdocs-node@1.13.3-next.3 + +## 1.9.3-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.10.0-next.2 + - @backstage/config@1.3.2 + - @backstage/plugin-techdocs-node@1.13.3-next.2 + - @backstage/catalog-model@1.7.3 + - @backstage/cli-common@0.1.15 + +## 1.9.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.10.0-next.1 + - @backstage/plugin-techdocs-node@1.13.3-next.1 + - @backstage/catalog-model@1.7.3 + - @backstage/cli-common@0.1.15 + - @backstage/config@1.3.2 + +## 1.9.3-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.9.1-next.0 + - @backstage/plugin-techdocs-node@1.13.3-next.0 + - @backstage/catalog-model@1.7.3 + - @backstage/cli-common@0.1.15 + - @backstage/config@1.3.2 + +## 1.9.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.9.0 + - @backstage/plugin-techdocs-node@1.13.2 + - @backstage/catalog-model@1.7.3 + - @backstage/cli-common@0.1.15 + - @backstage/config@1.3.2 + +## 1.9.2-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.9.0-next.2 + - @backstage/plugin-techdocs-node@1.13.2-next.2 + - @backstage/catalog-model@1.7.3 + - @backstage/cli-common@0.1.15 + - @backstage/config@1.3.2 + +## 1.9.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.9.0-next.1 + - @backstage/plugin-techdocs-node@1.13.2-next.1 + - @backstage/catalog-model@1.7.3 + - @backstage/cli-common@0.1.15 + - @backstage/config@1.3.2 + +## 1.9.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.9.0-next.0 + - @backstage/plugin-techdocs-node@1.13.2-next.0 + - @backstage/catalog-model@1.7.3 + - @backstage/cli-common@0.1.15 + - @backstage/config@1.3.2 + +## 1.9.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.8.2 + - @backstage/plugin-techdocs-node@1.13.1 + - @backstage/catalog-model@1.7.3 + - @backstage/cli-common@0.1.15 + - @backstage/config@1.3.2 + +## 1.9.1-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.8.2-next.2 + - @backstage/catalog-model@1.7.3 + - @backstage/cli-common@0.1.15 + - @backstage/config@1.3.2 + - @backstage/plugin-techdocs-node@1.13.1-next.2 + +## 1.9.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.8.2-next.1 + - @backstage/catalog-model@1.7.3 + - @backstage/cli-common@0.1.15 + - @backstage/config@1.3.2 + - @backstage/plugin-techdocs-node@1.13.1-next.1 + +## 1.9.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.8.2-next.0 + - @backstage/plugin-techdocs-node@1.13.1-next.0 + +## 1.9.0 + +### Minor Changes + +- 8de3d2d: Allow configurable optional retries for publisher AWS S3 operations. + +### Patch Changes + +- 69f84ac: Internal update to work with dynamic imports. +- Updated dependencies + - @backstage/backend-defaults@0.8.0 + - @backstage/plugin-techdocs-node@1.13.0 + - @backstage/catalog-model@1.7.3 + - @backstage/cli-common@0.1.15 + - @backstage/config@1.3.2 + +## 1.9.0-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.8.0-next.3 + - @backstage/plugin-techdocs-node@1.13.0-next.2 + - @backstage/catalog-model@1.7.3 + - @backstage/cli-common@0.1.15 + - @backstage/config@1.3.2 + +## 1.9.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.8.0-next.2 + - @backstage/catalog-model@1.7.3 + - @backstage/cli-common@0.1.15 + - @backstage/config@1.3.2 + - @backstage/plugin-techdocs-node@1.13.0-next.1 + +## 1.9.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.8.0-next.1 + - @backstage/catalog-model@1.7.3 + - @backstage/cli-common@0.1.15 + - @backstage/config@1.3.2 + - @backstage/plugin-techdocs-node@1.13.0-next.0 + +## 1.9.0-next.0 + +### Minor Changes + +- 8de3d2d: Allow configurable optional retries for publisher AWS S3 operations. + +### Patch Changes + +- 69f84ac: Internal update to work with dynamic imports. +- Updated dependencies + - @backstage/backend-defaults@0.8.0-next.0 + - @backstage/plugin-techdocs-node@1.13.0-next.0 + - @backstage/catalog-model@1.7.3 + - @backstage/cli-common@0.1.15 + - @backstage/config@1.3.2 + +## 1.8.25 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.7.0 + - @backstage/catalog-model@1.7.3 + - @backstage/cli-common@0.1.15 + - @backstage/config@1.3.2 + - @backstage/plugin-techdocs-node@1.12.16 + +## 1.8.25-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.7.0-next.1 + - @backstage/catalog-model@1.7.3-next.0 + - @backstage/config@1.3.2-next.0 + - @backstage/plugin-techdocs-node@1.12.16-next.1 + - @backstage/cli-common@0.1.15 + +## 1.8.25-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.7.0-next.0 + - @backstage/catalog-model@1.7.2 + - @backstage/cli-common@0.1.15 + - @backstage/config@1.3.1 + - @backstage/plugin-techdocs-node@1.12.16-next.0 + +## 1.8.24 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.6.0 + - @backstage/plugin-techdocs-node@1.12.15 + - @backstage/catalog-model@1.7.2 + - @backstage/cli-common@0.1.15 + - @backstage/config@1.3.1 + +## 1.8.24-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.6.0-next.2 + - @backstage/plugin-techdocs-node@1.12.15-next.2 + - @backstage/catalog-model@1.7.2-next.0 + - @backstage/cli-common@0.1.15 + - @backstage/config@1.3.1-next.0 + +## 1.8.24-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.6.0-next.1 + - @backstage/catalog-model@1.7.1 + - @backstage/cli-common@0.1.15 + - @backstage/config@1.3.0 + - @backstage/plugin-techdocs-node@1.12.15-next.1 + +## 1.8.24-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.6.0-next.0 + - @backstage/plugin-techdocs-node@1.12.15-next.0 + - @backstage/catalog-model@1.7.1 + - @backstage/cli-common@0.1.15 + - @backstage/config@1.3.0 + +## 1.8.22 + +### Patch Changes + +- 702f41d: Bumped dev dependencies `@types/node` +- Updated dependencies + - @backstage/config@1.3.0 + - @backstage/backend-defaults@0.5.3 + - @backstage/cli-common@0.1.15 + - @backstage/catalog-model@1.7.1 + - @backstage/plugin-techdocs-node@1.12.13 + +## 1.8.22-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.5.3-next.3 + - @backstage/catalog-model@1.7.0 + - @backstage/cli-common@0.1.15-next.0 + - @backstage/config@1.2.0 + - @backstage/plugin-techdocs-node@1.12.13-next.3 + +## 1.8.22-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.5.3-next.2 + - @backstage/catalog-model@1.7.0 + - @backstage/cli-common@0.1.15-next.0 + - @backstage/config@1.2.0 + - @backstage/plugin-techdocs-node@1.12.13-next.2 + +## 1.8.22-next.1 + +### Patch Changes + +- 702f41d: Bumped dev dependencies `@types/node` +- Updated dependencies + - @backstage/cli-common@0.1.15-next.0 + - @backstage/backend-defaults@0.5.3-next.1 + - @backstage/catalog-model@1.7.0 + - @backstage/config@1.2.0 + - @backstage/plugin-techdocs-node@1.12.13-next.1 + +## 1.8.22-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.5.3-next.0 + - @backstage/catalog-model@1.7.0 + - @backstage/cli-common@0.1.14 + - @backstage/config@1.2.0 + - @backstage/plugin-techdocs-node@1.12.13-next.0 + +## 1.8.20 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.5.1 + - @backstage/plugin-techdocs-node@1.12.12 + - @backstage/catalog-model@1.7.0 + - @backstage/cli-common@0.1.14 + - @backstage/config@1.2.0 + +## 1.8.20-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.5.1-next.2 + - @backstage/plugin-techdocs-node@1.12.12-next.2 + - @backstage/catalog-model@1.7.0 + - @backstage/cli-common@0.1.14 + - @backstage/config@1.2.0 + +## 1.8.20-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.5.1-next.1 + - @backstage/catalog-model@1.7.0 + - @backstage/cli-common@0.1.14 + - @backstage/config@1.2.0 + - @backstage/plugin-techdocs-node@1.12.12-next.1 + +## 1.8.20-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.5.1-next.0 + - @backstage/catalog-model@1.7.0 + - @backstage/cli-common@0.1.14 + - @backstage/config@1.2.0 + - @backstage/plugin-techdocs-node@1.12.12-next.0 + +## 1.8.19 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.5.0 + - @backstage/plugin-techdocs-node@1.12.11 + - @backstage/catalog-model@1.7.0 + - @backstage/cli-common@0.1.14 + - @backstage/config@1.2.0 + +## 1.8.19-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.5.0-next.2 + - @backstage/plugin-techdocs-node@1.12.11-next.2 + - @backstage/catalog-model@1.6.0 + - @backstage/cli-common@0.1.14 + - @backstage/config@1.2.0 + +## 1.8.19-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.5.0-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/cli-common@0.1.14 + - @backstage/config@1.2.0 + - @backstage/plugin-techdocs-node@1.12.11-next.1 + +## 1.8.19-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-techdocs-node@1.12.11-next.0 + - @backstage/backend-defaults@0.5.0-next.0 + - @backstage/catalog-model@1.6.0 + - @backstage/cli-common@0.1.14 + - @backstage/config@1.2.0 + +## 1.8.17 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.4.2 + - @backstage/plugin-techdocs-node@1.12.9 + - @backstage/catalog-model@1.6.0 + - @backstage/cli-common@0.1.14 + - @backstage/config@1.2.0 + +## 1.8.17-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.4.2-next.3 + - @backstage/plugin-techdocs-node@1.12.9-next.3 + - @backstage/catalog-model@1.6.0-next.0 + - @backstage/cli-common@0.1.14 + - @backstage/config@1.2.0 + +## 1.8.17-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.4.2-next.2 + - @backstage/plugin-techdocs-node@1.12.9-next.2 + - @backstage/catalog-model@1.5.0 + - @backstage/cli-common@0.1.14 + - @backstage/config@1.2.0 + +## 1.8.17-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-techdocs-node@1.12.9-next.1 + - @backstage/backend-defaults@0.4.2-next.1 + - @backstage/catalog-model@1.5.0 + - @backstage/cli-common@0.1.14 + - @backstage/config@1.2.0 + +## 1.8.17-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.4.2-next.0 + - @backstage/catalog-model@1.5.0 + - @backstage/cli-common@0.1.14 + - @backstage/config@1.2.0 + - @backstage/plugin-techdocs-node@1.12.9-next.0 + +## 1.8.16 + +### Patch Changes + +- c964a3d: Import discovery from backend-defaults instead of backend-common +- Updated dependencies + - @backstage/backend-defaults@0.4.0 + - @backstage/plugin-techdocs-node@1.12.8 + - @backstage/catalog-model@1.5.0 + - @backstage/cli-common@0.1.14 + - @backstage/config@1.2.0 + +## 1.8.16-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-techdocs-node@1.12.8-next.1 + - @backstage/backend-defaults@0.3.4-next.1 + - @backstage/catalog-model@1.5.0 + - @backstage/cli-common@0.1.14 + - @backstage/config@1.2.0 + +## 1.8.15-next.0 + +### Patch Changes + +- c964a3d: Import discovery from backend-defaults instead of backend-common +- Updated dependencies + - @backstage/backend-defaults@0.3.3-next.0 + - @backstage/plugin-techdocs-node@1.12.7-next.0 + - @backstage/catalog-model@1.5.0 + - @backstage/cli-common@0.1.14 + - @backstage/config@1.2.0 + +## 1.8.12 + +### Patch Changes + +- 2110d76: Removed `dockerode` dependency. +- Updated dependencies + - @backstage/backend-common@0.23.0 + - @backstage/plugin-techdocs-node@1.12.5 + - @backstage/cli-common@0.1.14 + - @backstage/catalog-model@1.5.0 + - @backstage/config@1.2.0 + +## 1.8.12-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-techdocs-node@1.12.5-next.3 + - @backstage/cli-common@0.1.14-next.0 + - @backstage/backend-common@0.23.0-next.3 + - @backstage/catalog-model@1.5.0 + - @backstage/config@1.2.0 + +## 1.8.12-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-techdocs-node@1.12.5-next.2 + - @backstage/backend-common@0.23.0-next.2 + - @backstage/catalog-model@1.5.0 + - @backstage/cli-common@0.1.13 + - @backstage/config@1.2.0 + +## 1.8.12-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.23.0-next.1 + - @backstage/plugin-techdocs-node@1.12.5-next.1 + +## 1.8.12-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.1-next.0 + - @backstage/plugin-techdocs-node@1.12.5-next.0 + - @backstage/catalog-model@1.5.0 + - @backstage/cli-common@0.1.13 + - @backstage/config@1.2.0 + +## 1.8.11 + +### Patch Changes + +- 1a0e009: Fix cookie endpoint mock for `serve` +- Updated dependencies + - @backstage/catalog-model@1.5.0 + - @backstage/backend-common@0.22.0 + - @backstage/plugin-techdocs-node@1.12.4 + +## 1.8.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.0-next.1 + - @backstage/plugin-techdocs-node@1.12.4-next.1 + +## 1.8.10-next.0 + +### Patch Changes + +- 1a0e009: Fix cookie endpoint mock for `serve` +- Updated dependencies + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/backend-common@0.21.8-next.0 + - @backstage/plugin-techdocs-node@1.12.4-next.0 + - @backstage/cli-common@0.1.13 + - @backstage/config@1.2.0 + +## 1.8.9 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.21.7 + - @backstage/plugin-techdocs-node@1.12.3 + - @backstage/catalog-model@1.4.5 + - @backstage/cli-common@0.1.13 + - @backstage/config@1.2.0 + +## 1.8.9-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.21.7-next.1 + - @backstage/plugin-techdocs-node@1.12.3-next.1 + - @backstage/catalog-model@1.4.5 + - @backstage/cli-common@0.1.13 + - @backstage/config@1.2.0 + +## 1.8.9-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-techdocs-node@1.12.3-next.0 + - @backstage/backend-common@0.21.7-next.0 + - @backstage/catalog-model@1.4.5 + - @backstage/cli-common@0.1.13 + - @backstage/config@1.2.0 + +## 1.8.8 + +### Patch Changes + +- 8caf2f7: Fix how the cli server mocks the new auth cookie endpoint. +- Updated dependencies + - @backstage/backend-common@0.21.6 + - @backstage/plugin-techdocs-node@1.12.2 + - @backstage/catalog-model@1.4.5 + - @backstage/cli-common@0.1.13 + - @backstage/config@1.2.0 + +## 1.8.7 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.21.5 + - @backstage/plugin-techdocs-node@1.12.1 + - @backstage/catalog-model@1.4.5 + - @backstage/cli-common@0.1.13 + - @backstage/config@1.2.0 + +## 1.8.6 + +### Patch Changes + +- 1bd4596: Removed the `ts-node` dev dependency. +- Updated dependencies + - @backstage/backend-common@0.21.4 + - @backstage/config@1.2.0 + - @backstage/plugin-techdocs-node@1.12.0 + - @backstage/catalog-model@1.4.5 + - @backstage/cli-common@0.1.13 + +## 1.8.6-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-techdocs-node@1.12.0-next.2 + - @backstage/backend-common@0.21.4-next.2 + - @backstage/catalog-model@1.4.5-next.0 + - @backstage/cli-common@0.1.13 + - @backstage/config@1.2.0-next.1 + +## 1.8.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.2.0-next.1 + - @backstage/backend-common@0.21.4-next.1 + - @backstage/plugin-techdocs-node@1.11.6-next.1 + - @backstage/catalog-model@1.4.5-next.0 + - @backstage/cli-common@0.1.13 + +## 1.8.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.21.3-next.0 + - @backstage/plugin-techdocs-node@1.11.5-next.0 + - @backstage/catalog-model@1.4.5-next.0 + - @backstage/config@1.1.2-next.0 + - @backstage/cli-common@0.1.13 + +## 1.8.2 + +### Patch Changes + +- 6bb6f3e: Updated dependency `fs-extra` to `^11.2.0`. + Updated dependency `@types/fs-extra` to `^11.0.0`. +- d2e3ab9: Updated dependency `dockerode` to `^4.0.0`. +- 6ba64c4: Updated dependency `commander` to `^12.0.0`. +- d8d243c: fix: mkdocs parameter casing +- Updated dependencies + - @backstage/backend-common@0.21.0 + - @backstage/catalog-model@1.4.4 + - @backstage/plugin-techdocs-node@1.11.2 + - @backstage/cli-common@0.1.13 + - @backstage/config@1.1.1 + +## 1.8.2-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.21.0-next.3 + - @backstage/plugin-techdocs-node@1.11.2-next.3 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/cli-common@0.1.13 + - @backstage/config@1.1.1 + +## 1.8.2-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.21.0-next.2 + - @backstage/plugin-techdocs-node@1.11.2-next.2 + - @backstage/config@1.1.1 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/cli-common@0.1.13 + +## 1.8.2-next.1 + +### Patch Changes + +- d8d243c: fix: mkdocs parameter casing +- Updated dependencies + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/backend-common@0.21.0-next.1 + - @backstage/plugin-techdocs-node@1.11.2-next.1 + - @backstage/cli-common@0.1.13 + - @backstage/config@1.1.1 + +## 1.8.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.21.0-next.0 + - @backstage/plugin-techdocs-node@1.11.2-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/cli-common@0.1.13 + - @backstage/config@1.1.1 + +## 1.8.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.1 + - @backstage/plugin-techdocs-node@1.11.1 + - @backstage/catalog-model@1.4.3 + - @backstage/cli-common@0.1.13 + - @backstage/config@1.1.1 + +## 1.8.1-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.1-next.2 + - @backstage/plugin-techdocs-node@1.11.1-next.2 + +## 1.8.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.1-next.1 + - @backstage/config@1.1.1 + - @backstage/plugin-techdocs-node@1.11.1-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/cli-common@0.1.13 + +## 1.8.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.1-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/cli-common@0.1.13 + - @backstage/config@1.1.1 + - @backstage/plugin-techdocs-node@1.11.1-next.0 + +## 1.8.0 + +### Minor Changes + +- d15d483: Add command `--runAsDefaultUser` for `@techdocs/cli generate` to bypass running the docker builds as host user for macOS and Linux. +- b2dccad: Support passing additional `mkdocs-server` CLI parameters (`--dirtyreload`, `--strict` and `--clean`) when run in containerized mode. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0 + - @backstage/plugin-techdocs-node@1.11.0 + - @backstage/catalog-model@1.4.3 + - @backstage/cli-common@0.1.13 + - @backstage/config@1.1.1 + +## 1.8.0-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.3 + - @backstage/catalog-model@1.4.3 + - @backstage/cli-common@0.1.13 + - @backstage/config@1.1.1 + - @backstage/plugin-techdocs-node@1.11.0-next.3 + +## 1.8.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.2 + - @backstage/catalog-model@1.4.3 + - @backstage/cli-common@0.1.13 + - @backstage/config@1.1.1 + - @backstage/plugin-techdocs-node@1.11.0-next.2 + +## 1.8.0-next.1 + +### Minor Changes + +- b2dccad7b3: Support passing additional `mkdocs-server` CLI parameters (`--dirtyreload`, `--strict` and `--clean`) when run in containerized mode. + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-techdocs-node@1.11.0-next.1 + - @backstage/backend-common@0.20.0-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/cli-common@0.1.13 + - @backstage/config@1.1.1 + +## 1.8.0-next.0 + +### Minor Changes + +- d15d483a49: Add command `--runAsDefaultUser` for `@techdocs/cli generate` to bypass running the docker builds as host user for macOS and Linux. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.20.0-next.0 + - @backstage/plugin-techdocs-node@1.11.0-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/cli-common@0.1.13 + - @backstage/config@1.1.1 + +## 1.7.0 + +### Minor Changes + +- 8600b86820: validate Docker status before running mkdocs server + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-techdocs-node@1.10.0 + - @backstage/backend-common@0.19.9 + - @backstage/catalog-model@1.4.3 + - @backstage/cli-common@0.1.13 + - @backstage/config@1.1.1 + +## 1.6.1-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.2 + - @backstage/plugin-techdocs-node@1.10.0-next.2 + +## 1.6.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-techdocs-node@1.10.0-next.1 + - @backstage/backend-common@0.19.9-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/cli-common@0.1.13 + - @backstage/config@1.1.1 + +## 1.6.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/cli-common@0.1.13 + - @backstage/config@1.1.1 + - @backstage/plugin-techdocs-node@1.9.1-next.0 + +## 1.6.0 + +### Minor Changes + +- d06b30b050: Add possibility to use a mkdocs config file with a different name than `mkdocs. with the serve command using the `--mkdocs-config-file-name\` argument + +### Patch Changes + +- de42eebaaf: Bumped dev dependencies `@types/node` and `mock-fs`. +- 2b6e572051: Restructured tests. +- Updated dependencies + - @backstage/backend-common@0.19.8 + - @backstage/plugin-techdocs-node@1.9.0 + - @backstage/catalog-model@1.4.3 + - @backstage/cli-common@0.1.13 + - @backstage/config@1.1.1 + +## 1.6.0-next.2 + +### Minor Changes + +- d06b30b050: Add possibility to use a mkdocs config file with a different name than `mkdocs. with the serve command using the `--mkdocs-config-file-name\` argument + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.8-next.2 + - @backstage/plugin-techdocs-node@1.9.0-next.2 + - @backstage/catalog-model@1.4.3-next.0 + - @backstage/cli-common@0.1.13-next.0 + - @backstage/config@1.1.1-next.0 + +## 1.5.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.7-next.1 + - @backstage/plugin-techdocs-node@1.8.2-next.1 + - @backstage/config@1.1.0 + - @backstage/catalog-model@1.4.2 + - @backstage/cli-common@0.1.13-next.0 + +## 1.5.2-next.0 + +### Patch Changes + +- de42eebaaf: Bumped dev dependencies `@types/node` and `mock-fs`. +- 2b6e572051: Restructured tests. +- Updated dependencies + - @backstage/cli-common@0.1.13-next.0 + - @backstage/backend-common@0.19.7-next.0 + - @backstage/plugin-techdocs-node@1.8.2-next.0 + - @backstage/config@1.1.0 + - @backstage/catalog-model@1.4.2 + +## 1.5.0 + +### Minor Changes + +- 10a86bd4ae12: Add optional config and cli option for techdocs to specify default mkdocs plugins. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.5 + - @backstage/config@1.1.0 + - @backstage/catalog-model@1.4.2 + - @backstage/plugin-techdocs-node@1.8.0 + - @backstage/cli-common@0.1.12 + +## 1.5.0-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.4.2-next.2 + - @backstage/config@1.1.0-next.2 + - @backstage/backend-common@0.19.5-next.3 + - @backstage/cli-common@0.1.12 + - @backstage/plugin-techdocs-node@1.8.0-next.3 + +## 1.5.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.1.0-next.1 + - @backstage/backend-common@0.19.5-next.2 + - @backstage/catalog-model@1.4.2-next.1 + - @backstage/plugin-techdocs-node@1.8.0-next.2 + - @backstage/cli-common@0.1.12 + +## 1.5.0-next.1 + +### Minor Changes + +- 10a86bd4ae12: Add optional config and cli option for techdocs to specify default mkdocs plugins. + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.1.0-next.0 + - @backstage/plugin-techdocs-node@1.8.0-next.1 + - @backstage/backend-common@0.19.5-next.1 + - @backstage/catalog-model@1.4.2-next.0 + - @backstage/cli-common@0.1.12 + +## 1.4.7-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.4-next.0 + - @backstage/catalog-model@1.4.1 + - @backstage/cli-common@0.1.12 + - @backstage/config@1.0.8 + - @backstage/plugin-techdocs-node@1.7.6-next.0 + +## 1.4.5 + +### Patch Changes + +- 971bdd6a4732: Bumped internal `nodemon` dependency. +- Updated dependencies + - @backstage/backend-common@0.19.2 + - @backstage/plugin-techdocs-node@1.7.4 + - @backstage/catalog-model@1.4.1 + - @backstage/cli-common@0.1.12 + - @backstage/config@1.0.8 + +## 1.4.5-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.2 + - @backstage/plugin-techdocs-node@1.7.4-next.2 + +## 1.4.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.1 + - @backstage/plugin-techdocs-node@1.7.4-next.1 + - @backstage/catalog-model@1.4.1 + - @backstage/cli-common@0.1.12 + - @backstage/config@1.0.8 + +## 1.4.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.2-next.0 + - @backstage/catalog-model@1.4.1 + - @backstage/cli-common@0.1.12 + - @backstage/config@1.0.8 + - @backstage/plugin-techdocs-node@1.7.4-next.0 + +## 1.4.4 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.1 + - @backstage/catalog-model@1.4.1 + - @backstage/cli-common@0.1.12 + - @backstage/config@1.0.8 + - @backstage/plugin-techdocs-node@1.7.3 + +## 1.4.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.1-next.0 + - @backstage/catalog-model@1.4.1-next.0 + - @backstage/cli-common@0.1.12 + - @backstage/config@1.0.8 + - @backstage/plugin-techdocs-node@1.7.3-next.0 + +## 1.4.3 + +### Patch Changes + +- 68a21956ef52: Remove reference to deprecated import +- Updated dependencies + - @backstage/backend-common@0.19.0 + - @backstage/plugin-techdocs-node@1.7.2 + - @backstage/catalog-model@1.4.0 + - @backstage/cli-common@0.1.12 + - @backstage/config@1.0.8 + +## 1.4.3-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.0-next.2 + - @backstage/plugin-techdocs-node@1.7.2-next.2 + - @backstage/catalog-model@1.4.0-next.1 + - @backstage/cli-common@0.1.12 + - @backstage/config@1.0.7 + +## 1.4.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.0-next.1 + - @backstage/plugin-techdocs-node@1.7.2-next.1 + - @backstage/catalog-model@1.4.0-next.0 + - @backstage/cli-common@0.1.12 + - @backstage/config@1.0.7 + +## 1.4.3-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.18.6-next.0 + - @backstage/config@1.0.7 + - @backstage/catalog-model@1.3.0 + - @backstage/cli-common@0.1.12 + - @backstage/plugin-techdocs-node@1.7.2-next.0 + +## 1.4.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.18.5 + - @backstage/plugin-techdocs-node@1.7.1 + - @backstage/catalog-model@1.3.0 + - @backstage/cli-common@0.1.12 + - @backstage/config@1.0.7 + +## 1.4.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.18.5-next.1 + - @backstage/plugin-techdocs-node@1.7.1-next.1 + - @backstage/config@1.0.7 + +## 1.4.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.18.5-next.0 + - @backstage/plugin-techdocs-node@1.7.1-next.0 + - @backstage/catalog-model@1.3.0 + - @backstage/cli-common@0.1.12 + - @backstage/config@1.0.7 + +## 1.4.1 + +### Patch Changes + +- b348420a804: Adding global-agent to enable the ability to publish through a proxy +- Updated dependencies + - @backstage/backend-common@0.18.4 + - @backstage/plugin-techdocs-node@1.7.0 + - @backstage/catalog-model@1.3.0 + - @backstage/cli-common@0.1.12 + - @backstage/config@1.0.7 + +## 1.4.1-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.3.0-next.0 + - @backstage/backend-common@0.18.4-next.2 + - @backstage/cli-common@0.1.12 + - @backstage/config@1.0.7 + - @backstage/plugin-techdocs-node@1.6.1-next.3 + +## 1.4.1-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.18.4-next.2 + - @backstage/catalog-model@1.2.1 + - @backstage/cli-common@0.1.12 + - @backstage/config@1.0.7 + - @backstage/plugin-techdocs-node@1.6.1-next.2 + +## 1.4.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.18.4-next.1 + - @backstage/catalog-model@1.2.1 + - @backstage/cli-common@0.1.12 + - @backstage/config@1.0.7 + - @backstage/plugin-techdocs-node@1.6.1-next.1 + +## 1.4.1-next.0 + +### Patch Changes + +- b348420a804: Adding global-agent to enable the ability to publish through a proxy +- Updated dependencies + - @backstage/backend-common@0.18.4-next.0 + - @backstage/config@1.0.7 + - @backstage/catalog-model@1.2.1 + - @backstage/cli-common@0.1.12 + - @backstage/plugin-techdocs-node@1.6.1-next.0 + +## 1.4.0 + +### Minor Changes + +- 8e465ce52e2: Running `@techdocs/cli generate` with the `--verbose` flag will now print the mkdocs output. +- ea2bbef1b16: Added support for an HTTPS proxy for techdocs AWS S3 requests + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-techdocs-node@1.6.0 + - @backstage/backend-common@0.18.3 + - @backstage/catalog-model@1.2.1 + - @backstage/cli-common@0.1.12 + - @backstage/config@1.0.7 + +## 1.4.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-techdocs-node@1.6.0-next.2 + - @backstage/backend-common@0.18.3-next.2 + - @backstage/config@1.0.7-next.0 + +## 1.4.0-next.1 + +### Minor Changes + +- 8e465ce52e2: Running `@techdocs/cli generate` with the `--verbose` flag will now print the mkdocs output. +- ea2bbef1b16: Added support for an HTTPS proxy for techdocs AWS S3 requests + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-techdocs-node@1.6.0-next.1 + - @backstage/backend-common@0.18.3-next.1 + - @backstage/cli-common@0.1.12-next.0 + - @backstage/config@1.0.7-next.0 + - @backstage/catalog-model@1.2.1-next.1 + +## 1.3.3-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.18.3-next.0 + - @backstage/catalog-model@1.2.1-next.0 + - @backstage/cli-common@0.1.11 + - @backstage/config@1.0.6 + - @backstage/plugin-techdocs-node@1.5.1-next.0 + +## 1.3.2 + +### Patch Changes + +- dd1e37649f: Deprecated getMkDocsYml in favor of getMkdocsYml (lowercase 'd') + +- dcacf94912: Fix proxying to mkdocs + + The domain localhost may point to both 127.0.0.1 and ::1, ipv4 and ipv6 + and when node tries to lookup localhost it might prefer ipv6 while mkdocs + is only listening on ipv4. This tells node-proxy to target the ipv4 address + instead of relying on localhost hostname lookup. + +- 339d9a5b5c: Added support for using a default `mkdocs.yml` configuration file when none is provided + +- 6e0b6a0d50: Fixed publish command missing awsBucketRootPath option. + Fixed publish command having the gcsBucketRootPath option misconfigured, previously returning a boolean vs a string. + +- Updated dependencies + - @backstage/backend-common@0.18.2 + - @backstage/plugin-techdocs-node@1.5.0 + - @backstage/catalog-model@1.2.0 + - @backstage/cli-common@0.1.11 + - @backstage/config@1.0.6 + +## 1.3.2-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.18.2-next.2 + - @backstage/catalog-model@1.2.0-next.1 + - @backstage/cli-common@0.1.11 + - @backstage/config@1.0.6 + - @backstage/plugin-techdocs-node@1.4.6-next.2 + +## 1.3.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.18.2-next.1 + - @backstage/catalog-model@1.1.6-next.0 + - @backstage/cli-common@0.1.11 + - @backstage/config@1.0.6 + - @backstage/plugin-techdocs-node@1.4.6-next.1 + +## 1.3.2-next.0 + +### Patch Changes + +- 6e0b6a0d50: Fixed publish command missing awsBucketRootPath option. + Fixed publish command having the gcsBucketRootPath option misconfigured, previously returning a boolean vs a string. +- Updated dependencies + - @backstage/plugin-techdocs-node@1.4.6-next.0 + - @backstage/catalog-model@1.1.6-next.0 + - @backstage/backend-common@0.18.2-next.0 + +## 1.3.0 + +### Minor Changes + +- bc18c902a2: Add `--preview-app-bundle-path` and `--preview-app-port` options to the `serve` command enabling previewing with apps other than the provided one + +### Patch Changes + +- 9f2b786fc9: Provide context for logged errors. +- Updated dependencies + - @backstage/backend-common@0.18.0 + - @backstage/catalog-model@1.1.5 + - @backstage/config@1.0.6 + - @backstage/cli-common@0.1.11 + - @backstage/plugin-techdocs-node@1.4.4 + +## 1.3.0-next.2 + +### Minor Changes + +- bc18c902a2: Add `--preview-app-bundle-path` and `--preview-app-port` options to the `serve` command enabling previewing with apps other than the provided one + +### Patch Changes + +- 9f2b786fc9: Provide context for logged errors. +- Updated dependencies + - @backstage/backend-common@0.18.0-next.1 + - @backstage/plugin-techdocs-node@1.4.4-next.2 + - @backstage/catalog-model@1.1.5-next.1 + - @backstage/cli-common@0.1.11 + - @backstage/config@1.0.6-next.0 + +## 1.2.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.18.0-next.0 + - @backstage/config@1.0.6-next.0 + - @backstage/catalog-model@1.1.5-next.1 + - @backstage/cli-common@0.1.11 + - @backstage/plugin-techdocs-node@1.4.4-next.1 + +## 1.2.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.5-next.0 + - @backstage/backend-common@0.17.0 + - @backstage/cli-common@0.1.11 + - @backstage/config@1.0.5 + - @backstage/plugin-techdocs-node@1.4.4-next.0 + +## 1.2.4 + +### Patch Changes + +- 8015ff1258: Tweaked wording to use inclusive terminology +- Updated dependencies + - @backstage/backend-common@0.17.0 + - @backstage/plugin-techdocs-node@1.4.3 + - @backstage/cli-common@0.1.11 + - @backstage/catalog-model@1.1.4 + - @backstage/config@1.0.5 + +## 1.2.4-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.17.0-next.3 + - @backstage/plugin-techdocs-node@1.4.3-next.3 + - @backstage/catalog-model@1.1.4-next.1 + - @backstage/cli-common@0.1.11-next.0 + - @backstage/config@1.0.5-next.1 + +## 1.2.4-next.2 + +### Patch Changes + +- 8015ff1258: Tweaked wording to use inclusive terminology +- Updated dependencies + - @backstage/backend-common@0.17.0-next.2 + - @backstage/cli-common@0.1.11-next.0 + - @backstage/plugin-techdocs-node@1.4.3-next.2 + - @backstage/catalog-model@1.1.4-next.1 + - @backstage/config@1.0.5-next.1 + +## 1.2.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.17.0-next.1 + - @backstage/plugin-techdocs-node@1.4.3-next.1 + - @backstage/config@1.0.5-next.1 + - @backstage/catalog-model@1.1.4-next.1 + - @backstage/cli-common@0.1.10 + +## 1.2.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.16.1-next.0 + - @backstage/catalog-model@1.1.4-next.0 + - @backstage/cli-common@0.1.10 + - @backstage/config@1.0.5-next.0 + - @backstage/plugin-techdocs-node@1.4.3-next.0 + +## 1.2.3 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.16.0 + - @backstage/plugin-techdocs-node@1.4.2 + - @backstage/catalog-model@1.1.3 + - @backstage/cli-common@0.1.10 + - @backstage/config@1.0.4 + +## 1.2.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.16.0-next.1 + - @backstage/plugin-techdocs-node@1.4.2-next.1 + - @backstage/catalog-model@1.1.3-next.0 + - @backstage/cli-common@0.1.10 + - @backstage/config@1.0.4-next.0 + +## 1.2.3-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.16.0-next.0 + - @backstage/plugin-techdocs-node@1.4.2-next.0 + - @backstage/catalog-model@1.1.3-next.0 + - @backstage/cli-common@0.1.10 + - @backstage/config@1.0.4-next.0 + +## 1.2.2 + +### Patch Changes + +- 0b2a30dead: fixing techdocs-cli Docker client creation + + Docker client does not need to be created when --no-docker + option is provided. + + If you had DOCKER_CERT_PATH environment variable defined + the Docker client was looking for certificates + and breaking techdocs-cli generate command even with --no-docker + option. + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/backend-common@0.15.2 + - @backstage/plugin-techdocs-node@1.4.1 + - @backstage/cli-common@0.1.10 + - @backstage/config@1.0.3 + +## 1.2.2-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.15.2-next.2 + - @backstage/plugin-techdocs-node@1.4.1-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/cli-common@0.1.10 + - @backstage/config@1.0.3-next.2 + +## 1.2.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.15.2-next.1 + - @backstage/catalog-model@1.1.2-next.1 + - @backstage/cli-common@0.1.10 + - @backstage/config@1.0.3-next.1 + - @backstage/plugin-techdocs-node@1.4.1-next.1 + +## 1.2.2-next.0 + +### Patch Changes + +- 0b2a30dead: fixing techdocs-cli Docker client creation + + Docker client does not need to be created when --no-docker + option is provided. + + If you had DOCKER_CERT_PATH environment variable defined + the Docker client was looking for certificates + and breaking techdocs-cli generate command even with --no-docker + option. + +- Updated dependencies + - @backstage/catalog-model@1.1.2-next.0 + - @backstage/plugin-techdocs-node@1.4.1-next.0 + - @backstage/backend-common@0.15.2-next.0 + - @backstage/cli-common@0.1.10 + - @backstage/config@1.0.3-next.0 + +## 1.2.1 + +### Patch Changes + +- 7d47def9c4: Removed dependency on `@types/jest`. +- Updated dependencies + - @backstage/backend-common@0.15.1 + - @backstage/plugin-techdocs-node@1.4.0 + - @backstage/catalog-model@1.1.1 + - @backstage/cli-common@0.1.10 + - @backstage/config@1.0.2 + +## 1.2.1-next.2 + +### Patch Changes + +- 7d47def9c4: Removed dependency on `@types/jest`. +- Updated dependencies + - @backstage/catalog-model@1.1.1-next.0 + - @backstage/cli-common@0.1.10-next.0 + - @backstage/config@1.0.2-next.0 + - @backstage/backend-common@0.15.1-next.3 + - @backstage/plugin-techdocs-node@1.4.0-next.2 + +## 1.2.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-techdocs-node@1.4.0-next.1 + - @backstage/backend-common@0.15.1-next.2 + +## 1.2.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.15.1-next.0 + - @backstage/plugin-techdocs-node@1.3.1-next.0 + +## 1.2.0 + +### Minor Changes + +- 855952db53: Added CLI option `--docker-option` to allow passing additional options to the `docker run` command executed my `serve` and `serve:mkdocs`. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.15.0 + - @backstage/plugin-techdocs-node@1.3.0 + +## 1.2.0-next.2 + +### Minor Changes + +- 855952db53: Added CLI option `--docker-option` to allow passing additional options to the `docker run` command executed my `serve` and `serve:mkdocs`. + +## 1.1.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.15.0-next.1 + - @backstage/plugin-techdocs-node@1.3.0-next.1 + +## 1.1.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.15.0-next.0 + - @backstage/plugin-techdocs-node@1.2.1-next.0 + +## 1.1.3 + +### Patch Changes + +- a5d73da942: Fix the flag parsing for `legacyCopyReadmeMdToIndexMd` in `techdocs-cli generate` command, and decouple it's logic from the `techdocs-ref` flag. +- 14ce0d9347: Fixed a bug that prevented docker images from being pulled by default when generating TechDocs. +- Updated dependencies + - @backstage/plugin-techdocs-node@1.2.0 + - @backstage/backend-common@0.14.1 + - @backstage/catalog-model@1.1.0 + +## 1.1.3-next.1 + +### Patch Changes + +- a5d73da942: Fix the flag parsing for `legacyCopyReadmeMdToIndexMd` in `techdocs-cli generate` command, and decouple it's logic from the `techdocs-ref` flag. +- Updated dependencies + - @backstage/plugin-techdocs-node@1.2.0-next.1 + - @backstage/catalog-model@1.1.0-next.1 + - @backstage/backend-common@0.14.1-next.1 + +## 1.1.3-next.0 + +### Patch Changes + +- 14ce0d9347: Fixed a bug that prevented docker images from being pulled by default when generating TechDocs. +- Updated dependencies + - @backstage/backend-common@0.14.1-next.0 + - @backstage/catalog-model@1.1.0-next.0 + - @backstage/plugin-techdocs-node@1.1.3-next.0 + +## 1.1.2 + +### Patch Changes + +- f96e98f4cd: Updated dependency `cypress` to `^10.0.0`. +- bff65e6958: Updated sidebar-related logic to use `` + `useSidebarPinState()` and/or `` + `useSidebarOpenState()` from `@backstage/core-components`. +- Updated dependencies + - @backstage/backend-common@0.14.0 + - @backstage/plugin-techdocs-node@1.1.2 + - @backstage/catalog-model@1.0.3 + +## 1.1.2-next.2 + +### Patch Changes + +- f96e98f4cd: Updated dependency `cypress` to `^10.0.0`. +- Updated dependencies + - @backstage/backend-common@0.14.0-next.2 + - @backstage/plugin-techdocs-node@1.1.2-next.2 + +## 1.1.2-next.1 + +### Patch Changes + +- bff65e6958: Updated sidebar-related logic to use `` + `useSidebarPinState()` and/or `` + `useSidebarOpenState()` from `@backstage/core-components`. +- Updated dependencies + - @backstage/backend-common@0.13.6-next.1 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-techdocs-node@1.1.2-next.1 + +## 1.1.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.13.6-next.0 + - @backstage/plugin-techdocs-node@1.1.2-next.0 + +## 1.1.1 + +### Patch Changes + +- 344ea56acc: Bump `commander` to version 9.1.0 +- 52fddad92d: The TechDocs CLI's embedded app now imports all API refs from the `@backstage/plugin-techdocs-react` package. +- c14e78a367: Update `techdocs-cli serve`'s `proxyEndpoint` to match the base URL of the embedded techdocs app. +- Updated dependencies + - @backstage/backend-common@0.13.3 + - @backstage/cli-common@0.1.9 + - @backstage/config@1.0.1 + - @backstage/plugin-techdocs-node@1.1.1 + - @backstage/catalog-model@1.0.2 + +## 1.1.1-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.13.3-next.2 + - @backstage/cli-common@0.1.9-next.0 + - @backstage/config@1.0.1-next.0 + - @backstage/catalog-model@1.0.2-next.0 + - @backstage/plugin-techdocs-node@1.1.1-next.1 + +## 1.1.1-next.1 + +### Patch Changes + +- 52fddad92d: The TechDocs CLI's embedded app now imports all API refs from the `@backstage/plugin-techdocs-react` package. +- Updated dependencies + - @backstage/backend-common@0.13.3-next.1 + +## 1.1.1-next.0 + +### Patch Changes + +- 344ea56acc: Bump `commander` to version 9.1.0 +- Updated dependencies + - @backstage/backend-common@0.13.3-next.0 + - @backstage/plugin-techdocs-node@1.1.1-next.0 + +## 1.1.0 + +### Minor Changes + +- 733187987b: Removed an undocumented, broken behavior where `README.md` files would be copied to `index.md` if it did not exist, leading to broken links in the TechDocs UI. + + **WARNING**: If you notice 404s in TechDocs after updating, check to make sure that all markdown files referenced in your `mkdocs.yml`s' `nav` sections exist. The following flag may be passed to the `generate` command to temporarily revert to the broken behavior. + + ```sh + techdocs-cli generate --legacyCopyReadmeMdToIndexMd + ``` + +### Patch Changes + +- 230ad0826f: Bump to using `@types/node` v16 +- eb470ea54c: Adds a new flag to override the entrypoint when using a custom docker image. It could be used to reuse existing images with different entrypoints. +- Updated dependencies + - @backstage/catalog-model@1.0.1 + - @backstage/backend-common@0.13.2 + - @backstage/plugin-techdocs-node@1.1.0 + +## 1.1.0-next.1 + +### Minor Changes + +- bcf1a2496c: BREAKING: The default Techdocs behavior will no longer attempt to copy `docs/README.md` or `README.md` to `docs/index.md` (if not found). To retain this behavior in your instance, you can set the following config in your `app-config.yaml`: + + ```yaml + techdocs: + generator: + mkdocs: + legacyCopyReadmeMdToIndexMd: true + ``` + +### Patch Changes + +- 230ad0826f: Bump to using `@types/node` v16 +- eb470ea54c: Adds a new flag to override the entrypoint when using a custom docker image. It could be used to reuse existing images with different entrypoints. +- Updated dependencies + - @backstage/backend-common@0.13.2-next.2 + - @backstage/plugin-techdocs-node@1.1.0-next.2 + +## 1.0.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.0.1-next.0 + - @backstage/backend-common@0.13.2-next.0 + - @backstage/plugin-techdocs-node@1.0.1-next.0 + +## 1.0.0 + +### Major Changes + +- b58c70c223: This package has been promoted to v1.0! To understand how this change affects the package, please check out our [versioning policy](https://backstage.io/docs/overview/versioning-policy). + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.13.1 + - @backstage/catalog-model@1.0.0 + - @backstage/config@1.0.0 + - @backstage/plugin-techdocs-node@1.0.0 + +## 0.8.17 + +### Patch Changes + +- e0a69ba49f: build(deps): bump `fs-extra` from 9.1.0 to 10.0.1 +- 91bf1e6c1a: Use `@backstage/plugin-techdocs-node` package instead of `@backstage/techdocs-common`. +- Updated dependencies + - @backstage/backend-common@0.13.0 + - @backstage/plugin-techdocs-node@0.11.12 + - @backstage/catalog-model@0.13.0 + +## 0.8.17-next.0 + +### Patch Changes + +- e0a69ba49f: build(deps): bump `fs-extra` from 9.1.0 to 10.0.1 +- 91bf1e6c1a: Use `@backstage/plugin-techdocs-node` package instead of `@backstage/techdocs-common`. +- Updated dependencies + - @backstage/backend-common@0.13.0-next.0 + - @backstage/plugin-techdocs-node@0.11.12-next.0 + - @backstage/catalog-model@0.13.0-next.0 + +## 0.8.16 + +### Patch Changes + +- 853efd42bd: Bump `@backstage/techdocs-common` to `0.11.10` to use `spotify/techdocs:v0.3.7` which upgrades `mkdocs-theme` as a dependency of `mkdocs-techdocs-core`. +- Updated dependencies + - @backstage/catalog-model@0.12.0 + - @backstage/backend-common@0.12.0 + - @backstage/techdocs-common@0.11.11 + +## 0.8.15 + +### Patch Changes + +- ed78516480: chore(deps-dev): bump `cypress` from 7.3.0 to 9.5.0 +- 209fd128e6: Updated usage of `github:` location types in docs to use `url:` instead. +- 61ff215e08: - Adds `cypress` and `cypress-plugin-snapshots` as dependencies for integration and visual regression tests. + - Updates README documentation with instructions for how to run tests. + - Clarifies output text for prepack script. +- Updated dependencies + - @backstage/backend-common@0.11.0 + - @backstage/catalog-model@0.11.0 + - @backstage/techdocs-common@0.11.10 + +## 0.8.14 + +### Patch Changes + +- c77c5c7eb6: Added `backstage.role` to `package.json` +- Updated dependencies + - @backstage/backend-common@0.10.8 + - @backstage/catalog-model@0.10.0 + - @backstage/cli-common@0.1.7 + - @backstage/config@0.1.14 + - @backstage/techdocs-common@0.11.8 + +## 0.8.13 + +### Patch Changes + +- b70c186194: Updated the HTTP server to allow for simplification of the development of the CLI itself. +- Updated dependencies + - @backstage/backend-common@0.10.7 + - @backstage/techdocs-common@0.11.7 + +## 0.8.13-next.0 + +### Patch Changes + +- b70c186194: Updated the HTTP server to allow for simplification of the development of the CLI itself. +- Updated dependencies + - @backstage/backend-common@0.10.7-next.0 + - @backstage/techdocs-common@0.11.7-next.0 + +## 0.8.12 + +### Patch Changes + +- 14472509a3: Use a local file dependency for techdocs-cli-embedded-app, to ensure that it's always pulled out of the workspace +- Updated dependencies + - @backstage/backend-common@0.10.6 + - @backstage/techdocs-common@0.11.6 + +## 0.8.12-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.10.6-next.0 + - @backstage/techdocs-common@0.11.6-next.0 + +## 0.8.12-next.0 + +### Patch Changes + +- 14472509a3: Use a local file dependency for techdocs-cli-embedded-app, to ensure that it's always pulled out of the workspace + +## 0.8.11 + +### Patch Changes + +- 10086f5873: Bumped `react-dev-utils` from `^12.0.0-next.47` to `^12.0.0-next.60`. +- Updated dependencies + - @backstage/techdocs-common@0.11.5 + - @backstage/backend-common@0.10.5 + +## 0.8.11-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.10.4-next.0 + - @backstage/config@0.1.13-next.0 + - @backstage/techdocs-common@0.11.4-next.0 + - @backstage/catalog-model@0.9.10-next.0 + +## 0.8.10 + +### Patch Changes + +- 8fbc988bfc: remove internal and inline CSS from index.html +- Updated dependencies + - @backstage/techdocs-common@0.11.2 + - @backstage/backend-common@0.10.1 + +## 0.8.9 + +### Patch Changes + +- 5fdc8df0e8: The `index.html` template was updated to use the new `config` global. +- Updated dependencies + - @backstage/backend-common@0.10.0 + - @backstage/techdocs-common@0.11.1 + +## 0.8.8 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.9.13 + - @backstage/techdocs-common@0.11.0 + +## 0.8.7 + +### Patch Changes + +- e7230ef814: Bump react-dev-utils to v12 +- Updated dependencies + - @backstage/backend-common@0.9.12 + +## 0.8.6 + +### Patch Changes + +- e21e3c6102: Bumping minimum requirements for `dockerode` and `testcontainers` +- 1578ad341b: Add support for specifying bucketRootPath for AWS and GCS publishers +- f2694e3750: Adds ability to use encrypted S3 buckets by utilizing the SSE option in the AWS SDK +- Updated dependencies + - @backstage/catalog-model@0.9.7 + - @backstage/backend-common@0.9.10 + - @backstage/techdocs-common@0.10.7 + +## 0.8.5 + +### Patch Changes + +- Reunified the [techdocs-cli](https://github.com/backstage/techdocs-cli) monorepo code back into the main [backstage](https://github.com/backstage/backstage) repo + + See [7288](https://github.com/backstage/backstage/issues/7288)). The changes include some internal refactoring that do not affect functionality beyond the local development setup. + +## 0.8.4 + +### Patch Changes + +- 8333394: The [change](https://github.com/backstage/techdocs-cli/commit/b25014cec313d46ce1c9b4f324cc09047a00fc1f) updated the `@backstage/techdocs-common` from version `0.9.0` to `0.10.2` and one of the intermediate versions, the [0.10.0](https://github.com/backstage/backstage/blob/cac4afb95fdbd130a66e53a1b0430a1e62787a7f/packages/techdocs-common/CHANGELOG.md#patch-changes-2), introduced the use of search in context that requires an implementation for the Search API. + + Created a custom techdocs page to disable search in the Reader component, preventing it from using the Search API, as we don't want to provide search in preview mode. + +## 0.8.3 + +### Patch Changes + +- edbb988: Upgrades the techdocs common page to the latest version 0.10.2. + + See [@backstage/techdocs-common changelog](https://github.com/backstage/backstage/blob/cac4afb95fdbd130a66e53a1b0430a1e62787a7f/packages/techdocs-common/CHANGELOG.md#L3). + +- db4ebfc: Add an `etag` flag to the `generate` command that is stored in the `techdocs_metadata.json` file. + +## 0.8.2 + +### Patch Changes + +- 8fc7384: Allow to execute techdocs-cli serve using docker techdocs-container on Windows + +## 0.8.1 + +### Patch Changes + +- 0187424: Separate build and publish release steps + +## 0.8.0 + +### Minor Changes + +- c6f437a: OpenStack Swift configuration changed due to OSS SDK Client change in @backstage/techdocs-common, it was a breaking change. + PR Reference: + +### Patch Changes + +- 05f0409: Merge Jobs for Release Pull Requests and Package Publishes + +## 0.7.0 + +### Minor Changes + +- 9d1f8d8: The `techdocs-cli publish` command will now publish TechDocs content to remote + storage using the lowercase'd entity triplet as the storage path. This is in + line with the beta release of the TechDocs plugin (`v0.11.0`). + + If you have been running `techdocs-cli` prior to this version, you will need to + follow this [migration guide](https://backstage.io/docs/features/techdocs/how-to-guides#how-to-migrate-from-techdocs-alpha-to-beta). + +## 0.6.2 + +### Patch Changes + +- f1bcf1a: Changelog (from v0.6.1 to v0.6.2) + + #### :bug: Bug Fix + + - `techdocs-cli` + - [#105](https://github.com/backstage/techdocs-cli/pull/105) Add azureAccountKey parameter back to the publish command ([@emmaindal](https://github.com/emmaindal)) + + #### :house: Internal + + - `techdocs-cli-embedded-app` + - [#122](https://github.com/backstage/techdocs-cli/pull/122) chore(deps-dev): bump @types/node from 12.20.20 to 16.7.1 in /packages/techdocs-cli-embedded-app ([@dependabot\[bot\]](https://github.com/apps/dependabot)) + - [#120](https://github.com/backstage/techdocs-cli/pull/120) chore(deps-dev): bump @types/react-dom from 16.9.14 to 17.0.9 in /packages/techdocs-cli-embedded-app ([@dependabot\[bot\]](https://github.com/apps/dependabot)) + - [#119](https://github.com/backstage/techdocs-cli/pull/119) chore(deps-dev): bump @testing-library/user-event from 12.8.3 to 13.2.1 in /packages/techdocs-cli-embedded-app ([@dependabot\[bot\]](https://github.com/apps/dependabot)) + - [#118](https://github.com/backstage/techdocs-cli/pull/118) chore(deps-dev): bump @testing-library/react from 10.4.9 to 12.0.0 ([@dependabot\[bot\]](https://github.com/apps/dependabot)) + - Other + - [#117](https://github.com/backstage/techdocs-cli/pull/117) chore(deps): bump @backstage/plugin-catalog from 0.6.11 to 0.6.12 ([@dependabot\[bot\]](https://github.com/apps/dependabot)) + - [#124](https://github.com/backstage/techdocs-cli/pull/124) Update release process docs ([@emmaindal](https://github.com/emmaindal)) + - [#116](https://github.com/backstage/techdocs-cli/pull/116) ignore dependabot branches for project board workflow ([@emmaindal](https://github.com/emmaindal)) + - [#106](https://github.com/backstage/techdocs-cli/pull/106) Configure dependabot for all packages ([@emmaindal](https://github.com/emmaindal)) + - [#102](https://github.com/backstage/techdocs-cli/pull/102) readme: add information about running techdocs-common locally ([@vcapretz](https://github.com/vcapretz)) + - [#103](https://github.com/backstage/techdocs-cli/pull/103) Introduce changesets and improve the publish workflow ([@minkimcello](https://github.com/minkimcello)) + - [#101](https://github.com/backstage/techdocs-cli/pull/101) update yarn lockfile to get rid of old version of node-forge ([@emmaindal](https://github.com/emmaindal)) + + #### Committers: 3 + + Thank you for contributing ❤️ + + - `Emma Indal` ([@emmaindal](https://github.com/emmaindal)) + - `Min Kim` ([@minkimcello](https://github.com/minkimcello)) + - `Vitor Capretz` ([@vcapretz](https://github.com/vcapretz)) + +## @backstage/plugin-techdocs@1.15.3-next.0 + +# @backstage/plugin-techdocs + +## 1.15.2-next.0 + +### Patch Changes + +- a4d4a70: Fixed an issue where the entire TechDocs page would re-render when navigating between pages within the same entity's documentation. +- Updated dependencies + - @backstage/plugin-search-react@1.9.6-next.0 + - @backstage/plugin-catalog-react@1.21.3-next.0 + - @backstage/core-plugin-api@1.11.2-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/core-components@0.18.3-next.0 + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/integration@1.18.2-next.0 + - @backstage/frontend-plugin-api@0.12.2-next.0 + - @backstage/catalog-client@1.12.1-next.0 + - @backstage/core-compat-api@0.5.4-next.0 + - @backstage/errors@1.2.7 + - @backstage/integration-react@1.2.12-next.0 + - @backstage/theme@0.7.0 + - @backstage/plugin-auth-react@0.1.21-next.0 + - @backstage/plugin-search-common@1.2.21-next.0 + - @backstage/plugin-techdocs-common@0.1.1 + - @backstage/plugin-techdocs-react@1.3.5-next.0 + +## 1.15.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.2 + - @backstage/core-components@0.18.2 + - @backstage/integration@1.18.1 + - @backstage/frontend-plugin-api@0.12.1 + - @backstage/plugin-search-react@1.9.5 + - @backstage/config@1.3.5 + - @backstage/theme@0.7.0 + - @backstage/core-compat-api@0.5.3 + - @backstage/plugin-techdocs-react@1.3.4 + - @backstage/core-plugin-api@1.11.1 + - @backstage/integration-react@1.2.11 + - @backstage/plugin-auth-react@0.1.20 + - @backstage/plugin-search-common@1.2.20 + +## 1.15.1-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.2 + - @backstage/theme@0.6.9-next.0 + - @backstage/plugin-search-react@1.9.5-next.2 + - @backstage/plugin-techdocs-react@1.3.4-next.1 + +## 1.15.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/integration@1.18.1-next.1 + - @backstage/core-components@0.18.2-next.1 + - @backstage/core-plugin-api@1.11.1-next.0 + - @backstage/integration-react@1.2.11-next.1 + - @backstage/plugin-techdocs-react@1.3.4-next.1 + - @backstage/core-compat-api@0.5.3-next.1 + - @backstage/plugin-catalog-react@1.21.2-next.1 + - @backstage/plugin-search-react@1.9.5-next.1 + - @backstage/frontend-plugin-api@0.12.1-next.1 + - @backstage/plugin-auth-react@0.1.20-next.1 + - @backstage/plugin-search-common@1.2.20-next.0 + - @backstage/catalog-client@1.12.0 + +## 1.15.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.2-next.0 + - @backstage/core-components@0.18.2-next.0 + - @backstage/integration@1.18.1-next.0 + - @backstage/core-compat-api@0.5.3-next.0 + - @backstage/frontend-plugin-api@0.12.1-next.0 + - @backstage/integration-react@1.2.11-next.0 + - @backstage/plugin-auth-react@0.1.20-next.0 + - @backstage/plugin-search-react@1.9.5-next.0 + - @backstage/plugin-techdocs-react@1.3.4-next.0 + - @backstage/catalog-client@1.12.0 + - @backstage/catalog-model@1.7.5 + - @backstage/config@1.3.3 + - @backstage/core-plugin-api@1.11.0 + - @backstage/errors@1.2.7 + - @backstage/theme@0.6.8 + - @backstage/plugin-search-common@1.2.19 + - @backstage/plugin-techdocs-common@0.1.1 + +## 1.15.0 + +### Minor Changes + +- a0b604c: Adding redirect handling for TechDocs URLs that reference entities that now reference an external entity for TechDocs. Including tests and documentation. + +### Patch Changes + +- 313cec7: Updated dependency `dompurify` to `^3.2.4`. +- 8d18d23: TechDocs page titles have been improved, especially for deeply nested pages. +- 1dfee19: Reverts a change in CSS layout that shifted the content of the Techdocs too far to the left. +- 4ce5831: Support Techdocs redirect with dompurify 3.2.6+ +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.0 + - @backstage/plugin-techdocs-react@1.3.3 + - @backstage/frontend-plugin-api@0.12.0 + - @backstage/plugin-auth-react@0.1.19 + - @backstage/core-plugin-api@1.11.0 + - @backstage/catalog-client@1.12.0 + - @backstage/integration@1.18.0 + - @backstage/core-components@0.18.0 + - @backstage/core-compat-api@0.5.2 + - @backstage/plugin-search-react@1.9.4 + - @backstage/integration-react@1.2.10 + +## 1.14.2-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-react@0.1.19-next.1 + - @backstage/catalog-client@1.12.0-next.0 + - @backstage/plugin-catalog-react@1.21.0-next.2 + - @backstage/core-components@0.17.6-next.1 + - @backstage/integration@1.18.0-next.0 + - @backstage/core-compat-api@0.5.2-next.2 + +## 1.14.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.20.2-next.1 + - @backstage/frontend-plugin-api@0.11.1-next.0 + - @backstage/integration@1.18.0-next.0 + - @backstage/core-components@0.17.6-next.0 + - @backstage/core-compat-api@0.5.2-next.1 + - @backstage/plugin-search-react@1.9.4-next.0 + - @backstage/plugin-techdocs-react@1.3.3-next.0 + - @backstage/integration-react@1.2.10-next.0 + - @backstage/plugin-auth-react@0.1.19-next.0 + +## 1.14.2-next.0 + +### Patch Changes + +- 1dfee19: Reverts a change in CSS layout that shifted the content of the Techdocs too far to the left. +- Updated dependencies + - @backstage/core-compat-api@0.5.2-next.0 + - @backstage/plugin-catalog-react@1.20.2-next.0 + - @backstage/catalog-client@1.11.0 + - @backstage/catalog-model@1.7.5 + - @backstage/config@1.3.3 + - @backstage/core-components@0.17.5 + - @backstage/core-plugin-api@1.10.9 + - @backstage/errors@1.2.7 + - @backstage/frontend-plugin-api@0.11.0 + - @backstage/integration@1.17.1 + - @backstage/integration-react@1.2.9 + - @backstage/theme@0.6.8 + - @backstage/plugin-auth-react@0.1.18 + - @backstage/plugin-search-common@1.2.19 + - @backstage/plugin-search-react@1.9.3 + - @backstage/plugin-techdocs-common@0.1.1 + - @backstage/plugin-techdocs-react@1.3.2 + +## 1.14.0 + +### Minor Changes + +- cb0541f: Adds `additionalAllowedURIProtocols` to sanitizer config + +### Patch Changes + +- e4ddf22: Internal update to align with new blueprint parameter naming in the new frontend system. +- f2f133c: Internal update to use the new variant of `ApiBlueprint`. +- f231c2b: Fixes CSS to adjust based on whether or not the global Backstage sidebar is on the page. +- Updated dependencies + - @backstage/core-components@0.17.5 + - @backstage/frontend-plugin-api@0.11.0 + - @backstage/core-compat-api@0.5.0 + - @backstage/plugin-search-react@1.9.3 + - @backstage/plugin-catalog-react@1.20.0 + - @backstage/theme@0.6.8 + - @backstage/catalog-client@1.11.0 + - @backstage/plugin-auth-react@0.1.18 + - @backstage/plugin-techdocs-react@1.3.2 + +## 1.14.0-next.2 + +### Patch Changes + +- e4ddf22: Internal update to align with new blueprint parameter naming in the new frontend system. +- Updated dependencies + - @backstage/frontend-plugin-api@0.11.0-next.1 + - @backstage/core-compat-api@0.5.0-next.2 + - @backstage/plugin-search-react@1.9.3-next.1 + - @backstage/plugin-catalog-react@1.20.0-next.2 + - @backstage/core-components@0.17.5-next.1 + - @backstage/catalog-client@1.11.0-next.0 + - @backstage/catalog-model@1.7.5 + - @backstage/config@1.3.3 + - @backstage/core-plugin-api@1.10.9 + - @backstage/errors@1.2.7 + - @backstage/integration@1.17.1 + - @backstage/integration-react@1.2.9 + - @backstage/theme@0.6.8-next.0 + - @backstage/plugin-auth-react@0.1.18-next.0 + - @backstage/plugin-search-common@1.2.19 + - @backstage/plugin-techdocs-common@0.1.1 + - @backstage/plugin-techdocs-react@1.3.2-next.0 + +## 1.14.0-next.1 + +### Minor Changes + +- cb0541f: Adds `additionalAllowedURIProtocols` to sanitizer config + +### Patch Changes + +- f2f133c: Internal update to use the new variant of `ApiBlueprint`. +- Updated dependencies + - @backstage/core-compat-api@0.4.5-next.1 + - @backstage/plugin-catalog-react@1.20.0-next.1 + - @backstage/frontend-plugin-api@0.11.0-next.0 + - @backstage/theme@0.6.8-next.0 + - @backstage/catalog-client@1.11.0-next.0 + - @backstage/plugin-search-react@1.9.3-next.0 + - @backstage/plugin-techdocs-react@1.3.2-next.0 + - @backstage/core-components@0.17.5-next.0 + - @backstage/catalog-model@1.7.5 + - @backstage/config@1.3.3 + - @backstage/core-plugin-api@1.10.9 + - @backstage/errors@1.2.7 + - @backstage/integration@1.17.1 + - @backstage/integration-react@1.2.9 + - @backstage/plugin-auth-react@0.1.18-next.0 + - @backstage/plugin-search-common@1.2.19 + - @backstage/plugin-techdocs-common@0.1.1 + +## 1.13.3-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.19.2-next.0 + - @backstage/core-compat-api@0.4.5-next.0 + - @backstage/integration-react@1.2.9 + - @backstage/frontend-plugin-api@0.10.4 + - @backstage/plugin-search-react@1.9.2 + +## 1.13.2 + +### Patch Changes + +- 1debf7f: Fixed an issue causing TechDocs to not properly handle initial redirect. +- Updated dependencies + - @backstage/plugin-catalog-react@1.19.1 + - @backstage/config@1.3.3 + - @backstage/catalog-model@1.7.5 + - @backstage/catalog-client@1.10.2 + - @backstage/core-components@0.17.4 + - @backstage/core-plugin-api@1.10.9 + - @backstage/integration@1.17.1 + - @backstage/theme@0.6.7 + - @backstage/integration-react@1.2.9 + - @backstage/core-compat-api@0.4.4 + - @backstage/frontend-plugin-api@0.10.4 + - @backstage/plugin-auth-react@0.1.17 + - @backstage/plugin-search-common@1.2.19 + - @backstage/plugin-search-react@1.9.2 + - @backstage/plugin-techdocs-react@1.3.1 + +## 1.13.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/catalog-model@1.7.5-next.0 + - @backstage/plugin-catalog-react@1.19.1-next.1 + - @backstage/catalog-client@1.10.2-next.0 + - @backstage/integration@1.17.1-next.1 + - @backstage/core-components@0.17.4-next.1 + - @backstage/core-plugin-api@1.10.9-next.0 + - @backstage/integration-react@1.2.9-next.1 + - @backstage/plugin-techdocs-react@1.3.1-next.1 + - @backstage/plugin-search-common@1.2.19-next.0 + - @backstage/core-compat-api@0.4.4-next.1 + - @backstage/plugin-search-react@1.9.2-next.1 + - @backstage/frontend-plugin-api@0.10.4-next.1 + - @backstage/plugin-auth-react@0.1.17-next.1 + +## 1.13.2-next.0 + +### Patch Changes + +- 1debf7f: Fixed an issue causing TechDocs to not properly handle initial redirect. +- Updated dependencies + - @backstage/integration@1.17.1-next.0 + - @backstage/integration-react@1.2.9-next.0 + - @backstage/theme@0.6.7-next.0 + - @backstage/plugin-catalog-react@1.19.1-next.0 + - @backstage/core-components@0.17.4-next.0 + - @backstage/plugin-search-react@1.9.2-next.0 + - @backstage/plugin-techdocs-react@1.3.1-next.0 + - @backstage/catalog-client@1.10.1 + - @backstage/catalog-model@1.7.4 + - @backstage/config@1.3.2 + - @backstage/core-compat-api@0.4.4-next.0 + - @backstage/core-plugin-api@1.10.8 + - @backstage/errors@1.2.7 + - @backstage/frontend-plugin-api@0.10.4-next.0 + - @backstage/plugin-auth-react@0.1.17-next.0 + - @backstage/plugin-search-common@1.2.18 + - @backstage/plugin-techdocs-common@0.1.1 + +## 1.13.0 + +### Minor Changes + +- 3c59ece: **New Frontend System Only:** + The `TechDocs` plugin is now responsible for providing an entity icon link extension to read documentation from the catalog entity page. +- ec7b35d: Introduced `backstage.io/techdocs-entity-path` annotation which allows deep linking into another entities TechDocs in conjunction with `backstage.io/techdocs-entity`. + +### Patch Changes + +- 18c64e9: Added the `info.packageJson` option to the plugin instance for the new frontend system. +- 9dde3ba: Improved Keyboard accessibility in techdocs. +- Updated dependencies + - @backstage/core-components@0.17.3 + - @backstage/catalog-client@1.10.1 + - @backstage/core-plugin-api@1.10.8 + - @backstage/frontend-plugin-api@0.10.3 + - @backstage/plugin-catalog-react@1.19.0 + - @backstage/plugin-techdocs-react@1.3.0 + - @backstage/plugin-techdocs-common@0.1.1 + - @backstage/plugin-search-react@1.9.1 + - @backstage/integration-react@1.2.8 + - @backstage/plugin-auth-react@0.1.16 + - @backstage/catalog-model@1.7.4 + - @backstage/config@1.3.2 + - @backstage/core-compat-api@0.4.3 + - @backstage/errors@1.2.7 + - @backstage/integration@1.17.0 + - @backstage/theme@0.6.6 + - @backstage/plugin-search-common@1.2.18 + +## 1.13.0-next.2 + +### Minor Changes + +- 3c59ece: **New Frontend System Only:** + The `TechDocs` plugin is now responsible for providing an entity icon link extension to read documentation from the catalog entity page. + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.17.3-next.0 + - @backstage/plugin-catalog-react@1.19.0-next.2 + - @backstage/plugin-search-react@1.9.1-next.1 + - @backstage/frontend-plugin-api@0.10.3-next.1 + - @backstage/integration-react@1.2.7 + - @backstage/plugin-auth-react@0.1.16-next.0 + - @backstage/plugin-techdocs-react@1.3.0-next.1 + - @backstage/catalog-client@1.10.1-next.0 + - @backstage/catalog-model@1.7.4 + - @backstage/config@1.3.2 + - @backstage/core-compat-api@0.4.3-next.2 + - @backstage/core-plugin-api@1.10.7 + - @backstage/errors@1.2.7 + - @backstage/integration@1.17.0 + - @backstage/theme@0.6.6 + - @backstage/plugin-search-common@1.2.18 + - @backstage/plugin-techdocs-common@0.1.1-next.0 + +## 1.13.0-next.1 + +### Patch Changes + +- 9dde3ba: Improved Keyboard accessibility in techdocs. +- Updated dependencies + - @backstage/catalog-client@1.10.1-next.0 + - @backstage/plugin-catalog-react@1.18.1-next.1 + - @backstage/catalog-model@1.7.4 + - @backstage/config@1.3.2 + - @backstage/core-compat-api@0.4.3-next.1 + - @backstage/core-components@0.17.2 + - @backstage/core-plugin-api@1.10.7 + - @backstage/errors@1.2.7 + - @backstage/frontend-plugin-api@0.10.3-next.0 + - @backstage/integration@1.17.0 + - @backstage/integration-react@1.2.7 + - @backstage/theme@0.6.6 + - @backstage/plugin-auth-react@0.1.15 + - @backstage/plugin-search-common@1.2.18 + - @backstage/plugin-search-react@1.9.1-next.0 + - @backstage/plugin-techdocs-common@0.1.1-next.0 + - @backstage/plugin-techdocs-react@1.3.0-next.0 + +## 1.13.0-next.0 + +### Minor Changes + +- ec7b35d: Introduced `backstage.io/techdocs-entity-path` annotation which allows deep linking into another entities TechDocs in conjunction with `backstage.io/techdocs-entity`. + +### Patch Changes + +- 18c64e9: Added the `info.packageJson` option to the plugin instance for the new frontend system. +- Updated dependencies + - @backstage/frontend-plugin-api@0.10.3-next.0 + - @backstage/plugin-techdocs-react@1.3.0-next.0 + - @backstage/plugin-techdocs-common@0.1.1-next.0 + - @backstage/core-compat-api@0.4.3-next.0 + - @backstage/plugin-catalog-react@1.18.1-next.0 + - @backstage/plugin-search-react@1.9.1-next.0 + - @backstage/integration-react@1.2.7 + +## 1.12.6 + +### Patch Changes + +- fb58f20: Internal update to use the new `pluginId` option of `createFrontendPlugin`. +- 7d445da: Update keyboard focus on when clicking hash links. This fixes the issue where the "skip to content" link rendered by Material MkDocs isn't focused when used. +- 72d019d: Removed various typos +- 2ffd273: Add hover and focus styling to the "copy to clipboard" button within codeblocks in techdocs. Also added an aria-label to the button for accessibility. +- Updated dependencies + - @backstage/frontend-plugin-api@0.10.2 + - @backstage/theme@0.6.6 + - @backstage/integration@1.17.0 + - @backstage/core-components@0.17.2 + - @backstage/catalog-model@1.7.4 + - @backstage/core-compat-api@0.4.2 + - @backstage/plugin-search-react@1.9.0 + - @backstage/plugin-catalog-react@1.18.0 + - @backstage/plugin-techdocs-react@1.2.17 + - @backstage/plugin-auth-react@0.1.15 + - @backstage/core-plugin-api@1.10.7 + - @backstage/catalog-client@1.10.0 + - @backstage/config@1.3.2 + - @backstage/integration-react@1.2.7 + - @backstage/errors@1.2.7 + - @backstage/plugin-search-common@1.2.18 + - @backstage/plugin-techdocs-common@0.1.0 + +## 1.12.6-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.17.0-next.3 + - @backstage/plugin-search-react@1.9.0-next.2 + - @backstage/core-compat-api@0.4.2-next.3 + - @backstage/core-components@0.17.2-next.1 + - @backstage/core-plugin-api@1.10.7-next.0 + - @backstage/plugin-catalog-react@1.18.0-next.3 + - @backstage/catalog-client@1.10.0-next.0 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/frontend-plugin-api@0.10.2-next.1 + - @backstage/integration-react@1.2.7-next.3 + - @backstage/theme@0.6.6-next.0 + - @backstage/plugin-auth-react@0.1.15-next.1 + - @backstage/plugin-search-common@1.2.18-next.0 + - @backstage/plugin-techdocs-common@0.1.0 + - @backstage/plugin-techdocs-react@1.2.17-next.1 + +## 1.12.6-next.2 + +### Patch Changes + +- 7d445da: Update keyboard focus on when clicking hash links. This fixes the issue where the "skip to content" link rendered by Material MkDocs isn't focused when used. +- 2ffd273: Add hover and focus styling to the "copy to clipboard" button within codeblocks in techdocs. Also added an aria-label to the button for accessibility. +- Updated dependencies + - @backstage/integration@1.17.0-next.2 + - @backstage/core-plugin-api@1.10.7-next.0 + - @backstage/config@1.3.2 + - @backstage/core-compat-api@0.4.2-next.2 + - @backstage/core-components@0.17.2-next.1 + - @backstage/frontend-plugin-api@0.10.2-next.1 + - @backstage/integration-react@1.2.7-next.2 + - @backstage/plugin-auth-react@0.1.15-next.1 + - @backstage/plugin-catalog-react@1.18.0-next.2 + - @backstage/plugin-search-react@1.9.0-next.1 + - @backstage/plugin-techdocs-react@1.2.17-next.1 + - @backstage/catalog-client@1.10.0-next.0 + - @backstage/catalog-model@1.7.3 + - @backstage/errors@1.2.7 + - @backstage/theme@0.6.6-next.0 + - @backstage/plugin-search-common@1.2.18-next.0 + - @backstage/plugin-techdocs-common@0.1.0 + +## 1.12.6-next.1 + +### Patch Changes + +- fb58f20: Internal update to use the new `pluginId` option of `createFrontendPlugin`. +- 72d019d: Removed various typos +- Updated dependencies + - @backstage/theme@0.6.6-next.0 + - @backstage/core-components@0.17.2-next.0 + - @backstage/frontend-plugin-api@0.10.2-next.0 + - @backstage/core-compat-api@0.4.2-next.1 + - @backstage/integration@1.16.4-next.1 + - @backstage/plugin-search-react@1.9.0-next.0 + - @backstage/plugin-catalog-react@1.18.0-next.1 + - @backstage/plugin-techdocs-react@1.2.17-next.0 + - @backstage/plugin-auth-react@0.1.15-next.0 + - @backstage/integration-react@1.2.7-next.1 + - @backstage/catalog-client@1.10.0-next.0 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/core-plugin-api@1.10.6 + - @backstage/errors@1.2.7 + - @backstage/plugin-search-common@1.2.18-next.0 + - @backstage/plugin-techdocs-common@0.1.0 + +## 1.12.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.16.4-next.0 + - @backstage/core-compat-api@0.4.2-next.0 + - @backstage/plugin-catalog-react@1.18.0-next.0 + - @backstage/catalog-client@1.10.0-next.0 + - @backstage/integration-react@1.2.7-next.0 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/core-components@0.17.1 + - @backstage/core-plugin-api@1.10.6 + - @backstage/errors@1.2.7 + - @backstage/frontend-plugin-api@0.10.1 + - @backstage/theme@0.6.5 + - @backstage/plugin-auth-react@0.1.14 + - @backstage/plugin-search-common@1.2.17 + - @backstage/plugin-search-react@1.8.8 + - @backstage/plugin-techdocs-common@0.1.0 + - @backstage/plugin-techdocs-react@1.2.16 + +## 1.12.5 + +### Patch Changes + +- a47fd39: Removes instances of default React imports, a necessary update for the upcoming React 19 migration. + + + +- Updated dependencies + - @backstage/plugin-techdocs-react@1.2.16 + - @backstage/plugin-catalog-react@1.17.0 + - @backstage/frontend-plugin-api@0.10.1 + - @backstage/integration-react@1.2.6 + - @backstage/core-compat-api@0.4.1 + - @backstage/core-components@0.17.1 + - @backstage/core-plugin-api@1.10.6 + - @backstage/plugin-search-react@1.8.8 + - @backstage/plugin-auth-react@0.1.14 + - @backstage/theme@0.6.5 + - @backstage/integration@1.16.3 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/plugin-search-common@1.2.17 + - @backstage/plugin-techdocs-common@0.1.0 + +## 1.12.5-next.2 + +### Patch Changes + +- a47fd39: Removes instances of default React imports, a necessary update for the upcoming React 19 migration. + + + +- Updated dependencies + - @backstage/frontend-plugin-api@0.10.1-next.1 + - @backstage/integration-react@1.2.6-next.1 + - @backstage/core-compat-api@0.4.1-next.2 + - @backstage/core-components@0.17.1-next.1 + - @backstage/core-plugin-api@1.10.6-next.0 + - @backstage/plugin-techdocs-react@1.2.16-next.1 + - @backstage/plugin-catalog-react@1.17.0-next.2 + - @backstage/plugin-search-react@1.8.8-next.1 + - @backstage/plugin-auth-react@0.1.14-next.1 + - @backstage/theme@0.6.5-next.0 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.3-next.0 + - @backstage/plugin-search-common@1.2.17 + - @backstage/plugin-techdocs-common@0.1.0 + +## 1.12.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-techdocs-react@1.2.16-next.0 + - @backstage/core-components@0.17.1-next.0 + - @backstage/integration-react@1.2.6-next.0 + - @backstage/integration@1.16.3-next.0 + - @backstage/frontend-plugin-api@0.10.1-next.0 + - @backstage/plugin-auth-react@0.1.14-next.0 + - @backstage/plugin-catalog-react@1.16.1-next.1 + - @backstage/plugin-search-react@1.8.8-next.0 + - @backstage/core-compat-api@0.4.1-next.1 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/core-plugin-api@1.10.5 + - @backstage/errors@1.2.7 + - @backstage/theme@0.6.4 + - @backstage/plugin-search-common@1.2.17 + - @backstage/plugin-techdocs-common@0.1.0 + +## 1.12.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.16.1-next.0 + - @backstage/core-compat-api@0.4.1-next.0 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/core-components@0.17.0 + - @backstage/core-plugin-api@1.10.5 + - @backstage/errors@1.2.7 + - @backstage/frontend-plugin-api@0.10.0 + - @backstage/integration@1.16.2 + - @backstage/integration-react@1.2.5 + - @backstage/theme@0.6.4 + - @backstage/plugin-auth-react@0.1.13 + - @backstage/plugin-search-common@1.2.17 + - @backstage/plugin-search-react@1.8.7 + - @backstage/plugin-techdocs-common@0.1.0 + - @backstage/plugin-techdocs-react@1.2.15 + +## 1.12.4 + +### Patch Changes + +- fffe3c0: Fixed double scrollbar issue that would appear on the Entity TechDocs view page that would stop the page from full scrolling to the top when navigating to a new page +- 065e6b9: Excludes SVG styling from sanitization +- b5a8208: Added `TechDocsAddonsBlueprint` extension to allow adding of techdocs addons. +- ed1cb3e: Adds the page name of techdocs to the document's title. +- fe4abb8: Updates logic to check for SVG sources when inlining them. +- Updated dependencies + - @backstage/core-components@0.17.0 + - @backstage/core-plugin-api@1.10.5 + - @backstage/integration@1.16.2 + - @backstage/plugin-search-react@1.8.7 + - @backstage/frontend-plugin-api@0.10.0 + - @backstage/plugin-catalog-react@1.16.0 + - @backstage/core-compat-api@0.4.0 + - @backstage/plugin-techdocs-react@1.2.15 + - @backstage/integration-react@1.2.5 + - @backstage/plugin-auth-react@0.1.13 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/theme@0.6.4 + - @backstage/plugin-search-common@1.2.17 + - @backstage/plugin-techdocs-common@0.1.0 + +## 1.12.4-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.10.0-next.2 + - @backstage/plugin-catalog-react@1.16.0-next.2 + - @backstage/core-compat-api@0.4.0-next.2 + - @backstage/core-components@0.16.5-next.1 + - @backstage/integration@1.16.2-next.0 + - @backstage/plugin-search-react@1.8.7-next.2 + - @backstage/plugin-techdocs-react@1.2.15-next.2 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/core-plugin-api@1.10.4 + - @backstage/errors@1.2.7 + - @backstage/integration-react@1.2.5-next.0 + - @backstage/theme@0.6.4 + - @backstage/plugin-auth-react@0.1.13-next.1 + - @backstage/plugin-search-common@1.2.17 + - @backstage/plugin-techdocs-common@0.1.0 + +## 1.12.4-next.1 + +### Patch Changes + +- fffe3c0: Fixed double scrollbar issue that would appear on the Entity TechDocs view page that would stop the page from full scrolling to the top when navigating to a new page +- Updated dependencies + - @backstage/core-components@0.16.5-next.0 + - @backstage/plugin-catalog-react@1.16.0-next.1 + - @backstage/core-compat-api@0.3.7-next.1 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/core-plugin-api@1.10.4 + - @backstage/errors@1.2.7 + - @backstage/frontend-plugin-api@0.9.6-next.1 + - @backstage/integration@1.16.1 + - @backstage/integration-react@1.2.4 + - @backstage/theme@0.6.4 + - @backstage/plugin-auth-react@0.1.13-next.0 + - @backstage/plugin-search-common@1.2.17 + - @backstage/plugin-search-react@1.8.7-next.1 + - @backstage/plugin-techdocs-common@0.1.0 + - @backstage/plugin-techdocs-react@1.2.15-next.1 + +## 1.12.4-next.0 + +### Patch Changes + +- 065e6b9: Excludes svg styling from sanitization +- b5a8208: Added `TechDocsAddonsBlueprint` extension to allow adding of techdocs addons. +- ed1cb3e: Adds the page name of techdoc to the document's title. +- fe4abb8: Updates logic to check for SVG sources when inlining svgs. +- Updated dependencies + - @backstage/plugin-search-react@1.8.7-next.0 + - @backstage/plugin-catalog-react@1.16.0-next.0 + - @backstage/plugin-techdocs-react@1.2.15-next.0 + - @backstage/frontend-plugin-api@0.9.6-next.0 + - @backstage/core-compat-api@0.3.7-next.0 + - @backstage/integration-react@1.2.4 + +## 1.12.3 + +### Patch Changes + +- eb3d91a: Use the custom error page if provided for displaying errors instead of the default error page + +- 524f0af: Add missing route ref to the `/alpha` entity content extension. + +- f4be934: Changed the base URL in addLinkClickListener from window.location.origin to app.baseUrl for improved path handling. This fixes an issue where Backstage, when running on a subpath, was unable to handle non-Backstage URLs of the same origin correctly. + +- 1f40e6b: Add optional props to `TechDocCustomHome` to allow for more flexibility: + + ```tsx + import { TechDocsCustomHome } from '@backstage/plugin-techdocs'; + //... + + const options = { emptyRowsWhenPaging: false }; + const linkDestination = (entity: Entity): string | undefined => { + return entity.metadata.annotations?.['external-docs']; + }; + const techDocsTabsConfig = [ + { + label: 'Recommended Documentation', + panels: [ + { + title: 'Golden Path', + description: 'Documentation about standards to follow', + panelType: 'DocsCardGrid', + panelProps: { CustomHeader: () => }, + filterPredicate: entity => + entity?.metadata?.tags?.includes('golden-path') ?? false, + }, + { + title: 'Recommended', + description: 'Useful documentation', + panelType: 'InfoCardGrid', + panelProps: { + CustomHeader: () => + linkDestination: linkDestination, + }, + filterPredicate: entity => + entity?.metadata?.tags?.includes('recommended') ?? false, + }, + ], + }, + { + label: 'Browse All', + panels: [ + { + description: 'Browse all docs', + filterPredicate: filterEntity, + panelType: 'TechDocsIndexPage', + title: 'All', + panelProps: { PageWrapper: React.Fragment, CustomHeader: React.Fragment, options: options }, + }, + ], + }, + ]; + + const AppRoutes = () => { + + ) => ({children})} + /> + } + /> + ; + }; + ``` + + Add new Grid option called `InfoCardGrid` which is a more customizable card option for the Docs grid. + + ```tsx + entity.metadata['external-docs']} + /> + ``` + + Expose existing `CustomDocsPanel` so that it can be used independently if desired. + + ```tsx + const panels: PanelConfig[] = [ + { + description: '', + filterPredicate: entity => {}, + panelType: 'InfoCardGrid', + title: 'Standards', + panelProps: { + CustomHeader: () => + linkDestination: linkDestination, + }, + }, + { + description: '', + filterPredicate: entity => {}, + panelType: 'DocsCardGrid', + title: 'Contribute', + }, + ]; + { + panels.map((config, index) => ( + + )); + } + ``` + +- 58ec9e7: Removed older versions of React packages as a preparatory step for upgrading to React 19. This commit does not introduce any functional changes, but removes dependencies on previous React versions, allowing for a cleaner upgrade path in subsequent commits. + +- Updated dependencies + - @backstage/plugin-search-react@1.8.6 + - @backstage/core-components@0.16.4 + - @backstage/plugin-catalog-react@1.15.2 + - @backstage/frontend-plugin-api@0.9.5 + - @backstage/integration-react@1.2.4 + - @backstage/core-compat-api@0.3.6 + - @backstage/core-plugin-api@1.10.4 + - @backstage/plugin-techdocs-react@1.2.14 + - @backstage/plugin-auth-react@0.1.12 + - @backstage/theme@0.6.4 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.1 + - @backstage/plugin-search-common@1.2.17 + - @backstage/plugin-techdocs-common@0.1.0 + +## 1.12.3-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.9.5-next.3 + - @backstage/core-compat-api@0.3.6-next.3 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/core-components@0.16.4-next.1 + - @backstage/core-plugin-api@1.10.4-next.0 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.1 + - @backstage/integration-react@1.2.4-next.0 + - @backstage/theme@0.6.4-next.0 + - @backstage/plugin-auth-react@0.1.12-next.1 + - @backstage/plugin-catalog-react@1.15.2-next.3 + - @backstage/plugin-search-common@1.2.17 + - @backstage/plugin-search-react@1.8.6-next.3 + - @backstage/plugin-techdocs-common@0.1.0 + - @backstage/plugin-techdocs-react@1.2.14-next.1 + +## 1.12.3-next.2 + +### Patch Changes + +- eb3d91a: Use the custom error page if provided for displaying errors instead of the default error page +- Updated dependencies + - @backstage/core-components@0.16.4-next.1 + - @backstage/plugin-catalog-react@1.15.2-next.2 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/core-compat-api@0.3.6-next.2 + - @backstage/core-plugin-api@1.10.4-next.0 + - @backstage/errors@1.2.7 + - @backstage/frontend-plugin-api@0.9.5-next.2 + - @backstage/integration@1.16.1 + - @backstage/integration-react@1.2.4-next.0 + - @backstage/theme@0.6.4-next.0 + - @backstage/plugin-auth-react@0.1.12-next.1 + - @backstage/plugin-search-common@1.2.17 + - @backstage/plugin-search-react@1.8.6-next.2 + - @backstage/plugin-techdocs-common@0.1.0 + - @backstage/plugin-techdocs-react@1.2.14-next.1 + +## 1.12.2-next.1 + +### Patch Changes + +- 524f0af: Add missing route ref to the `/alpha` entity content extension. +- 58ec9e7: Removed older versions of React packages as a preparatory step for upgrading to React 19. This commit does not introduce any functional changes, but removes dependencies on previous React versions, allowing for a cleaner upgrade path in subsequent commits. +- Updated dependencies + - @backstage/core-components@0.16.4-next.0 + - @backstage/frontend-plugin-api@0.9.5-next.1 + - @backstage/integration-react@1.2.4-next.0 + - @backstage/core-compat-api@0.3.6-next.1 + - @backstage/core-plugin-api@1.10.4-next.0 + - @backstage/plugin-techdocs-react@1.2.14-next.0 + - @backstage/plugin-catalog-react@1.15.2-next.1 + - @backstage/plugin-search-react@1.8.6-next.1 + - @backstage/plugin-auth-react@0.1.12-next.0 + - @backstage/theme@0.6.4-next.0 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.1 + - @backstage/plugin-search-common@1.2.17 + - @backstage/plugin-techdocs-common@0.1.0 + +## 1.12.2-next.0 + +### Patch Changes + +- f4be934: Changed the base URL in addLinkClickListener from window.location.origin to app.baseUrl for improved path handling. This fixes an issue where Backstage, when running on a subpath, was unable to handle non-Backstage URLs of the same origin correctly. + +- 1f40e6b: Add optional props to `TechDocCustomHome` to allow for more flexibility: + + ```tsx + import { TechDocsCustomHome } from '@backstage/plugin-techdocs'; + //... + + const options = { emptyRowsWhenPaging: false }; + const linkDestination = (entity: Entity): string | undefined => { + return entity.metadata.annotations?.['external-docs']; + }; + const techDocsTabsConfig = [ + { + label: 'Recommended Documentation', + panels: [ + { + title: 'Golden Path', + description: 'Documentation about standards to follow', + panelType: 'DocsCardGrid', + panelProps: { CustomHeader: () => }, + filterPredicate: entity => + entity?.metadata?.tags?.includes('golden-path') ?? false, + }, + { + title: 'Recommended', + description: 'Useful documentation', + panelType: 'InfoCardGrid', + panelProps: { + CustomHeader: () => + linkDestination: linkDestination, + }, + filterPredicate: entity => + entity?.metadata?.tags?.includes('recommended') ?? false, + }, + ], + }, + { + label: 'Browse All', + panels: [ + { + description: 'Browse all docs', + filterPredicate: filterEntity, + panelType: 'TechDocsIndexPage', + title: 'All', + panelProps: { PageWrapper: React.Fragment, CustomHeader: React.Fragment, options: options }, + }, + ], + }, + ]; + + const AppRoutes = () => { + + ) => ({children})} + /> + } + /> + ; + }; + ``` + + Add new Grid option called `InfoCardGrid` which is a more customizable card option for the Docs grid. + + ```tsx + entity.metadata['external-docs']} + /> + ``` + + Expose existing `CustomDocsPanel` so that it can be used independently if desired. + + ```tsx + const panels: PanelConfig[] = [ + { + description: '', + filterPredicate: entity => {}, + panelType: 'InfoCardGrid', + title: 'Standards', + panelProps: { + CustomHeader: () => + linkDestination: linkDestination, + }, + }, + { + description: '', + filterPredicate: entity => {}, + panelType: 'DocsCardGrid', + title: 'Contribute', + }, + ]; + { + panels.map((config, index) => ( + + )); + } + ``` + +- Updated dependencies + - @backstage/plugin-search-react@1.8.6-next.0 + - @backstage/frontend-plugin-api@0.9.5-next.0 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/core-compat-api@0.3.6-next.0 + - @backstage/core-components@0.16.3 + - @backstage/core-plugin-api@1.10.3 + - @backstage/errors@1.2.7 + - @backstage/integration@1.16.1 + - @backstage/integration-react@1.2.3 + - @backstage/theme@0.6.3 + - @backstage/plugin-auth-react@0.1.11 + - @backstage/plugin-catalog-react@1.15.2-next.0 + - @backstage/plugin-search-common@1.2.17 + - @backstage/plugin-techdocs-common@0.1.0 + - @backstage/plugin-techdocs-react@1.2.13 + +## 1.12.1 + +### Patch Changes + +- 3710b35: Allow passing down `withSearch` prop to `EntityTechdocsContent` component since it was `true` by default, now user can use the `EntityTechdocsContent` component _without_ showing the search field on top of the content. +- Updated dependencies + - @backstage/plugin-catalog-react@1.15.1 + - @backstage/frontend-plugin-api@0.9.4 + - @backstage/core-plugin-api@1.10.3 + - @backstage/core-components@0.16.3 + - @backstage/integration@1.16.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/core-compat-api@0.3.5 + - @backstage/errors@1.2.7 + - @backstage/integration-react@1.2.3 + - @backstage/theme@0.6.3 + - @backstage/plugin-auth-react@0.1.11 + - @backstage/plugin-search-common@1.2.17 + - @backstage/plugin-search-react@1.8.5 + - @backstage/plugin-techdocs-common@0.1.0 + - @backstage/plugin-techdocs-react@1.2.13 + +## 1.12.1-next.1 + +### Patch Changes + +- 3710b35: Allow passing down `withSearch` prop to `EntityTechdocsContent` component since it was `true` by default, now user can use the `EntityTechdocsContent` component _without_ showing the search field on top of the content. +- Updated dependencies + - @backstage/frontend-plugin-api@0.9.4-next.0 + - @backstage/core-plugin-api@1.10.3-next.0 + - @backstage/core-compat-api@0.3.5-next.0 + - @backstage/plugin-catalog-react@1.15.1-next.1 + - @backstage/plugin-search-react@1.8.5-next.0 + - @backstage/core-components@0.16.3-next.0 + - @backstage/integration-react@1.2.3-next.0 + - @backstage/plugin-auth-react@0.1.11-next.0 + - @backstage/plugin-techdocs-react@1.2.13-next.0 + - @backstage/catalog-model@1.7.3-next.0 + - @backstage/config@1.3.2-next.0 + - @backstage/errors@1.2.7-next.0 + - @backstage/plugin-search-common@1.2.17-next.0 + - @backstage/integration@1.16.1-next.0 + - @backstage/theme@0.6.3 + - @backstage/plugin-techdocs-common@0.1.0 + +## 1.12.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.15.1-next.0 + - @backstage/integration-react@1.2.2 + - @backstage/core-compat-api@0.3.4 + +## 1.12.0 + +### Minor Changes + +- e153ca6: Add pagination support to TechDocs Index Page and make it the default + +### Patch Changes + +- 7d8777d: Added support for the Search bar in docs residing in the entity page tab, and not only the global "/docs" page. +- Updated dependencies + - @backstage/plugin-catalog-react@1.15.0 + - @backstage/integration@1.16.0 + - @backstage/plugin-search-react@1.8.4 + - @backstage/core-compat-api@0.3.4 + - @backstage/frontend-plugin-api@0.9.3 + - @backstage/theme@0.6.3 + - @backstage/core-components@0.16.2 + - @backstage/errors@1.2.6 + - @backstage/catalog-model@1.7.2 + - @backstage/config@1.3.1 + - @backstage/core-plugin-api@1.10.2 + - @backstage/integration-react@1.2.2 + - @backstage/plugin-auth-react@0.1.10 + - @backstage/plugin-search-common@1.2.16 + - @backstage/plugin-techdocs-common@0.1.0 + - @backstage/plugin-techdocs-react@1.2.12 + +## 1.11.3-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/core-compat-api@0.3.4-next.2 + - @backstage/plugin-catalog-react@1.14.3-next.2 + - @backstage/errors@1.2.6-next.0 + - @backstage/catalog-model@1.7.2-next.0 + - @backstage/config@1.3.1-next.0 + - @backstage/core-components@0.16.2-next.2 + - @backstage/core-plugin-api@1.10.2-next.0 + - @backstage/frontend-plugin-api@0.9.3-next.2 + - @backstage/integration@1.16.0-next.1 + - @backstage/integration-react@1.2.2-next.1 + - @backstage/theme@0.6.3-next.0 + - @backstage/plugin-auth-react@0.1.10-next.2 + - @backstage/plugin-search-common@1.2.16-next.0 + - @backstage/plugin-search-react@1.8.4-next.2 + - @backstage/plugin-techdocs-common@0.1.0 + - @backstage/plugin-techdocs-react@1.2.12-next.2 + +## 1.11.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.14.3-next.1 + - @backstage/core-components@0.16.2-next.1 + - @backstage/catalog-model@1.7.1 + - @backstage/config@1.3.0 + - @backstage/core-compat-api@0.3.4-next.1 + - @backstage/core-plugin-api@1.10.1 + - @backstage/errors@1.2.5 + - @backstage/frontend-plugin-api@0.9.3-next.1 + - @backstage/integration@1.16.0-next.0 + - @backstage/integration-react@1.2.2-next.0 + - @backstage/theme@0.6.3-next.0 + - @backstage/plugin-auth-react@0.1.10-next.1 + - @backstage/plugin-search-common@1.2.15 + - @backstage/plugin-search-react@1.8.4-next.1 + - @backstage/plugin-techdocs-common@0.1.0 + - @backstage/plugin-techdocs-react@1.2.12-next.1 + +## 1.11.3-next.0 + +### Patch Changes + +- 7d8777d: Added support for the Search bar in docs residing in the entity page tab, and not only the global "/docs" page. +- Updated dependencies + - @backstage/integration@1.16.0-next.0 + - @backstage/plugin-search-react@1.8.4-next.0 + - @backstage/plugin-catalog-react@1.14.3-next.0 + - @backstage/frontend-plugin-api@0.9.3-next.0 + - @backstage/theme@0.6.3-next.0 + - @backstage/catalog-model@1.7.1 + - @backstage/config@1.3.0 + - @backstage/core-compat-api@0.3.4-next.0 + - @backstage/core-components@0.16.2-next.0 + - @backstage/core-plugin-api@1.10.1 + - @backstage/errors@1.2.5 + - @backstage/integration-react@1.2.2-next.0 + - @backstage/plugin-auth-react@0.1.10-next.0 + - @backstage/plugin-search-common@1.2.15 + - @backstage/plugin-techdocs-common@0.1.0 + - @backstage/plugin-techdocs-react@1.2.12-next.0 + +## 1.11.1 + +### Patch Changes + +- 37a7810: Fixed an issue where `` would re-render infinitely under certain conditions. +- e937ae7: Fix an issue with index page of documentation site being re-rendered. +- 90246a9: Fix techdocs config schema for custom elements sanitizer +- 605bdc0: Avoid page re-rendering when clicking on anchor links in the same documentation page. +- 4f0cb89: Added DomPurify sanitizer configuration for custom elements implementing RFC . + See for how to enable it in the configuration. +- f246178: Removed `canvas` dev dependency. +- 4a2f73a: Fix an issue that caused the current documentation page to be re-rendered when navigating to + another one. +- Updated dependencies + - @backstage/config@1.3.0 + - @backstage/theme@0.6.1 + - @backstage/plugin-catalog-react@1.14.1 + - @backstage/core-components@0.16.0 + - @backstage/plugin-techdocs-react@1.2.10 + - @backstage/catalog-model@1.7.1 + - @backstage/core-compat-api@0.3.2 + - @backstage/core-plugin-api@1.10.1 + - @backstage/errors@1.2.5 + - @backstage/frontend-plugin-api@0.9.1 + - @backstage/integration@1.15.2 + - @backstage/integration-react@1.2.1 + - @backstage/plugin-auth-react@0.1.8 + - @backstage/plugin-search-common@1.2.15 + - @backstage/plugin-search-react@1.8.2 + - @backstage/plugin-techdocs-common@0.1.0 + +## 1.11.1-next.3 + +### Patch Changes + +- e937ae7: Fix an issue with index page of documentation site being re-rendered. +- Updated dependencies + - @backstage/core-components@0.16.0-next.2 + - @backstage/plugin-catalog-react@1.14.1-next.3 + - @backstage/core-compat-api@0.3.2-next.2 + - @backstage/catalog-model@1.7.0 + - @backstage/config@1.2.0 + - @backstage/core-plugin-api@1.10.0 + - @backstage/errors@1.2.4 + - @backstage/frontend-plugin-api@0.9.1-next.2 + - @backstage/integration@1.15.1 + - @backstage/integration-react@1.2.0 + - @backstage/theme@0.6.1-next.0 + - @backstage/plugin-auth-react@0.1.8-next.2 + - @backstage/plugin-search-common@1.2.14 + - @backstage/plugin-search-react@1.8.2-next.2 + - @backstage/plugin-techdocs-common@0.1.0 + - @backstage/plugin-techdocs-react@1.2.10-next.2 + +## 1.11.1-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.14.1-next.2 + - @backstage/catalog-model@1.7.0 + - @backstage/config@1.2.0 + - @backstage/core-compat-api@0.3.2-next.1 + - @backstage/core-components@0.16.0-next.1 + - @backstage/core-plugin-api@1.10.0 + - @backstage/errors@1.2.4 + - @backstage/frontend-plugin-api@0.9.1-next.1 + - @backstage/integration@1.15.1 + - @backstage/integration-react@1.2.0 + - @backstage/theme@0.6.1-next.0 + - @backstage/plugin-auth-react@0.1.8-next.1 + - @backstage/plugin-search-common@1.2.14 + - @backstage/plugin-search-react@1.8.2-next.1 + - @backstage/plugin-techdocs-common@0.1.0 + - @backstage/plugin-techdocs-react@1.2.10-next.1 + +## 1.11.1-next.1 + +### Patch Changes + +- 90246a9: Fix techdocs config schema for custom elements sanitizer +- Updated dependencies + - @backstage/theme@0.6.1-next.0 + - @backstage/catalog-model@1.7.0 + - @backstage/config@1.2.0 + - @backstage/core-compat-api@0.3.2-next.1 + - @backstage/core-components@0.16.0-next.1 + - @backstage/core-plugin-api@1.10.0 + - @backstage/errors@1.2.4 + - @backstage/frontend-plugin-api@0.9.1-next.1 + - @backstage/integration@1.15.1 + - @backstage/integration-react@1.2.0 + - @backstage/plugin-auth-react@0.1.8-next.1 + - @backstage/plugin-catalog-react@1.14.1-next.1 + - @backstage/plugin-search-common@1.2.14 + - @backstage/plugin-search-react@1.8.2-next.1 + - @backstage/plugin-techdocs-common@0.1.0 + - @backstage/plugin-techdocs-react@1.2.10-next.1 + +## 1.11.1-next.0 + +### Patch Changes + +- 605bdc0: Avoid page re-rendering when clicking on anchor links in the same documentation page. +- 4f0cb89: Added DomPurify sanitizer configuration for custom elements implementing RFC . + See for how to enable it in the configuration. +- f246178: Removed `canvas` dev dependency. +- 4a2f73a: Fix an issue that caused the current documentation page to be re-rendered when navigating to + another one. +- Updated dependencies + - @backstage/core-components@0.16.0-next.0 + - @backstage/plugin-techdocs-react@1.2.10-next.0 + - @backstage/catalog-model@1.7.0 + - @backstage/config@1.2.0 + - @backstage/core-compat-api@0.3.2-next.0 + - @backstage/core-plugin-api@1.10.0 + - @backstage/errors@1.2.4 + - @backstage/frontend-plugin-api@0.9.1-next.0 + - @backstage/integration@1.15.1 + - @backstage/integration-react@1.2.0 + - @backstage/theme@0.6.0 + - @backstage/plugin-auth-react@0.1.8-next.0 + - @backstage/plugin-catalog-react@1.14.1-next.0 + - @backstage/plugin-search-common@1.2.14 + - @backstage/plugin-search-react@1.8.2-next.0 + - @backstage/plugin-techdocs-common@0.1.0 + +## 1.11.0 + +### Minor Changes + +- e77ff3d: Adds support for custom background colors in code blocks and inline code within TechDocs. + +### Patch Changes + +- e969dc7: Move `@types/react` to a peer dependency. +- a77cb40: Make `emptyState` input optional on `entity-content:techdocs` extension so that + the default empty state extension works correctly. +- e918061: Add support for mkdocs material palette conditional hashes. +- 720a2f9: Updated dependency `git-url-parse` to `^15.0.0`. +- e8b4966: Use more of the available space for the navigation sidebar. +- Updated dependencies + - @backstage/core-components@0.15.1 + - @backstage/frontend-plugin-api@0.9.0 + - @backstage/integration-react@1.2.0 + - @backstage/core-compat-api@0.3.1 + - @backstage/core-plugin-api@1.10.0 + - @backstage/plugin-techdocs-react@1.2.9 + - @backstage/plugin-catalog-react@1.14.0 + - @backstage/plugin-search-react@1.8.1 + - @backstage/plugin-auth-react@0.1.7 + - @backstage/theme@0.6.0 + - @backstage/integration@1.15.1 + - @backstage/catalog-model@1.7.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/plugin-search-common@1.2.14 + - @backstage/plugin-techdocs-common@0.1.0 + +## 1.11.0-next.2 + +### Minor Changes + +- e77ff3d: Adds support for custom background colors in code blocks and inline code within TechDocs. + +### Patch Changes + +- e918061: Add support for mkdocs material palette conditional hashes. +- 720a2f9: Updated dependency `git-url-parse` to `^15.0.0`. +- e8b4966: Use more of the available space for the navigation sidebar. +- Updated dependencies + - @backstage/plugin-catalog-react@1.14.0-next.2 + - @backstage/integration@1.15.1-next.1 + - @backstage/theme@0.6.0-next.1 + - @backstage/catalog-model@1.7.0 + - @backstage/config@1.2.0 + - @backstage/core-compat-api@0.3.1-next.2 + - @backstage/core-components@0.15.1-next.2 + - @backstage/core-plugin-api@1.10.0-next.1 + - @backstage/errors@1.2.4 + - @backstage/frontend-plugin-api@0.9.0-next.2 + - @backstage/integration-react@1.2.0-next.2 + - @backstage/plugin-auth-react@0.1.7-next.2 + - @backstage/plugin-search-common@1.2.14 + - @backstage/plugin-search-react@1.8.1-next.2 + - @backstage/plugin-techdocs-common@0.1.0 + - @backstage/plugin-techdocs-react@1.2.9-next.2 + +## 1.10.11-next.1 + +### Patch Changes + +- e969dc7: Move `@types/react` to a peer dependency. +- Updated dependencies + - @backstage/core-components@0.15.1-next.1 + - @backstage/frontend-plugin-api@0.9.0-next.1 + - @backstage/integration-react@1.2.0-next.1 + - @backstage/core-compat-api@0.3.1-next.1 + - @backstage/core-plugin-api@1.10.0-next.1 + - @backstage/plugin-techdocs-react@1.2.9-next.1 + - @backstage/plugin-catalog-react@1.14.0-next.1 + - @backstage/plugin-search-react@1.8.1-next.1 + - @backstage/plugin-auth-react@0.1.7-next.1 + - @backstage/theme@0.5.8-next.0 + - @backstage/integration@1.15.1-next.0 + - @backstage/catalog-model@1.7.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/plugin-search-common@1.2.14 + - @backstage/plugin-techdocs-common@0.1.0 + +## 1.10.10-next.0 + +### Patch Changes + +- a77cb40: Make `emptyState` input optional on `entity-content:techdocs` extension so that + the default empty state extension works correctly. +- Updated dependencies + - @backstage/frontend-plugin-api@0.9.0-next.0 + - @backstage/core-compat-api@0.3.1-next.0 + - @backstage/core-components@0.15.1-next.0 + - @backstage/core-plugin-api@1.10.0-next.0 + - @backstage/plugin-catalog-react@1.13.1-next.0 + - @backstage/plugin-search-react@1.8.1-next.0 + - @backstage/catalog-model@1.7.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.15.0 + - @backstage/integration-react@1.1.32-next.0 + - @backstage/theme@0.5.7 + - @backstage/plugin-auth-react@0.1.7-next.0 + - @backstage/plugin-search-common@1.2.14 + - @backstage/plugin-techdocs-common@0.1.0 + - @backstage/plugin-techdocs-react@1.2.9-next.0 + +## 1.10.9 + +### Patch Changes + +- c891b69: Add `FavoriteToggle` in `core-components` to standardise favorite marking + +- fec8b57: Updated exports to use the new type parameters for extensions and extension blueprints. + +- fe94ad8: Fixes left navigation positioning when using mkdocs blog plugin + +- b0206dc: Added support for setting page status with 'new' and 'deprecated' values, allowing visual indication of page status in TechDocs. To use include the following at the top of your markdown file: + + ```markdown + --- + status: new + --- + ``` + +- 836127c: Updated dependency `@testing-library/react` to `^16.0.0`. + +- c7cb4c0: Add `empty-state:techdocs/entity-content` extension to allow overriding the empty state for the entity page techdocs tab. + +- 97db53e: Enhanced the table hover effect with a lighter color and updated the border radius to align with Backstage's theme styling + +- Updated dependencies + - @backstage/core-components@0.15.0 + - @backstage/plugin-catalog-react@1.13.0 + - @backstage/frontend-plugin-api@0.8.0 + - @backstage/plugin-techdocs-react@1.2.8 + - @backstage/core-compat-api@0.3.0 + - @backstage/plugin-search-react@1.8.0 + - @backstage/integration-react@1.1.31 + - @backstage/catalog-model@1.7.0 + - @backstage/integration@1.15.0 + - @backstage/core-plugin-api@1.9.4 + - @backstage/theme@0.5.7 + - @backstage/plugin-auth-react@0.1.6 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/plugin-search-common@1.2.14 + - @backstage/plugin-techdocs-common@0.1.0 + +## 1.10.9-next.2 + +### Patch Changes + +- c891b69: Add `FavoriteToggle` in `core-components` to standardise favorite marking +- 836127c: Updated dependency `@testing-library/react` to `^16.0.0`. +- 97db53e: Enhanced the table hover effect with a lighter color and updated the border radius to align with Backstage's theme styling +- Updated dependencies + - @backstage/core-components@0.14.11-next.1 + - @backstage/plugin-catalog-react@1.13.0-next.2 + - @backstage/integration-react@1.1.31-next.0 + - @backstage/plugin-search-react@1.8.0-next.2 + - @backstage/integration@1.15.0-next.0 + - @backstage/core-compat-api@0.3.0-next.2 + - @backstage/core-plugin-api@1.9.4-next.0 + - @backstage/frontend-plugin-api@0.8.0-next.2 + - @backstage/theme@0.5.7-next.0 + - @backstage/plugin-auth-react@0.1.6-next.1 + - @backstage/plugin-techdocs-react@1.2.8-next.2 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/plugin-search-common@1.2.14 + - @backstage/plugin-techdocs-common@0.1.0 + +## 1.10.9-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.1 + - @backstage/core-compat-api@0.3.0-next.1 + - @backstage/core-components@0.14.11-next.0 + - @backstage/plugin-catalog-react@1.12.4-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/core-plugin-api@1.9.3 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/integration-react@1.1.30 + - @backstage/theme@0.5.6 + - @backstage/plugin-auth-react@0.1.6-next.0 + - @backstage/plugin-search-common@1.2.14 + - @backstage/plugin-search-react@1.8.0-next.1 + - @backstage/plugin-techdocs-common@0.1.0 + - @backstage/plugin-techdocs-react@1.2.8-next.1 + +## 1.10.9-next.0 + +### Patch Changes + +- fec8b57: Updated exports to use the new type parameters for extensions and extension blueprints. +- Updated dependencies + - @backstage/frontend-plugin-api@0.8.0-next.0 + - @backstage/plugin-techdocs-react@1.2.8-next.0 + - @backstage/core-compat-api@0.2.9-next.0 + - @backstage/plugin-catalog-react@1.12.4-next.0 + - @backstage/plugin-search-react@1.8.0-next.0 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/core-components@0.14.10 + - @backstage/core-plugin-api@1.9.3 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0 + - @backstage/integration-react@1.1.30 + - @backstage/theme@0.5.6 + - @backstage/plugin-auth-react@0.1.5 + - @backstage/plugin-search-common@1.2.14 + - @backstage/plugin-techdocs-common@0.1.0 + +## 1.10.8 + +### Patch Changes + +- 69bd940: Use annotation constants from new techdocs-common package. +- c7603e8: Deprecate the old pattern of `create*Extension`, and replace it with the equivalent Blueprint implementation instead +- 27794d1: Allow for more granular control of TechDocsReaderPage styling. Theme overrides can now be provided to TechDocs without affecting the theme in other areas of Backstage. +- 4490d73: Refactor TechDocs' mkdocs-redirects support. +- 8543e72: TechDocs redirect feature now includes a notification to the user before they are redirected. +- 67e76f2: TechDocs now supports the `mkdocs-redirects` plugin. Redirects defined using the `mkdocs-redirect` plugin will be handled automatically in TechDocs. Redirecting to external urls is not supported. In the case that an external redirect url is provided, TechDocs will redirect to the current documentation site home. +- bdc5471: Fixed issue where header styles were incorrectly generated when themes used CSS variables to define font size. +- 6349099: Added config input type to the extensions +- Updated dependencies + - @backstage/frontend-plugin-api@0.7.0 + - @backstage/plugin-catalog-react@1.12.3 + - @backstage/plugin-search-react@1.7.14 + - @backstage/core-components@0.14.10 + - @backstage/core-compat-api@0.2.8 + - @backstage/plugin-search-common@1.2.14 + - @backstage/integration@1.14.0 + - @backstage/plugin-techdocs-common@0.1.0 + - @backstage/plugin-auth-react@0.1.5 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/core-plugin-api@1.9.3 + - @backstage/errors@1.2.4 + - @backstage/integration-react@1.1.30 + - @backstage/theme@0.5.6 + - @backstage/plugin-techdocs-react@1.2.7 + +## 1.10.8-next.3 + +### Patch Changes + +- 27794d1: Allow for more granular control of TechDocsReaderPage styling. Theme overrides can now be provided to TechDocs without affecting the theme in other areas of Backstage. +- 8543e72: TechDocs redirect feature now includes a notification to the user before they are redirected. +- Updated dependencies + - @backstage/frontend-plugin-api@0.7.0-next.3 + - @backstage/catalog-model@1.6.0-next.0 + - @backstage/core-compat-api@0.2.8-next.3 + - @backstage/plugin-catalog-react@1.12.3-next.3 + - @backstage/plugin-search-react@1.7.14-next.3 + - @backstage/config@1.2.0 + - @backstage/core-components@0.14.10-next.0 + - @backstage/core-plugin-api@1.9.3 + - @backstage/errors@1.2.4 + - @backstage/integration@1.14.0-next.0 + - @backstage/integration-react@1.1.30-next.0 + - @backstage/theme@0.5.6 + - @backstage/plugin-auth-react@0.1.5-next.0 + - @backstage/plugin-search-common@1.2.14-next.1 + - @backstage/plugin-techdocs-common@0.1.0-next.0 + - @backstage/plugin-techdocs-react@1.2.7-next.1 + +## 1.10.8-next.2 + +### Patch Changes + +- 67e76f2: TechDocs now supports the `mkdocs-redirects` plugin. Redirects defined using the `mkdocs-redirect` plugin will be handled automatically in TechDocs. Redirecting to external urls is not supported. In the case that an external redirect url is provided, TechDocs will redirect to the current documentation site home. +- bdc5471: Fixed issue where header styles were incorrectly generated when themes used CSS variables to define font size. +- Updated dependencies + - @backstage/frontend-plugin-api@0.7.0-next.2 + - @backstage/core-compat-api@0.2.8-next.2 + - @backstage/plugin-search-common@1.2.14-next.1 + - @backstage/plugin-search-react@1.7.14-next.2 + - @backstage/plugin-catalog-react@1.12.3-next.2 + - @backstage/integration@1.14.0-next.0 + - @backstage/catalog-model@1.5.0 + - @backstage/config@1.2.0 + - @backstage/core-components@0.14.10-next.0 + - @backstage/core-plugin-api@1.9.3 + - @backstage/errors@1.2.4 + - @backstage/integration-react@1.1.30-next.0 + - @backstage/theme@0.5.6 + - @backstage/plugin-auth-react@0.1.5-next.0 + - @backstage/plugin-techdocs-common@0.1.0-next.0 + - @backstage/plugin-techdocs-react@1.2.7-next.0 + +## 1.10.8-next.1 + +### Patch Changes + +- 69bd940: Use annotation constants from new techdocs-common package. +- 6349099: Added config input type to the extensions +- Updated dependencies + - @backstage/plugin-catalog-react@1.12.3-next.1 + - @backstage/plugin-techdocs-common@0.1.0-next.0 + - @backstage/frontend-plugin-api@0.6.8-next.1 + - @backstage/core-compat-api@0.2.8-next.1 + - @backstage/plugin-search-react@1.7.14-next.1 + - @backstage/integration@1.14.0-next.0 + - @backstage/plugin-search-common@1.2.14-next.0 + - @backstage/catalog-model@1.5.0 + - @backstage/config@1.2.0 + - @backstage/core-components@0.14.10-next.0 + - @backstage/core-plugin-api@1.9.3 + - @backstage/errors@1.2.4 + - @backstage/integration-react@1.1.30-next.0 + - @backstage/theme@0.5.6 + - @backstage/plugin-auth-react@0.1.5-next.0 + - @backstage/plugin-techdocs-react@1.2.7-next.0 + +## 1.10.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.6.8-next.0 + - @backstage/plugin-catalog-react@1.12.3-next.0 + - @backstage/plugin-search-react@1.7.14-next.0 + - @backstage/core-components@0.14.10-next.0 + - @backstage/integration@1.14.0-next.0 + - @backstage/plugin-auth-react@0.1.5-next.0 + - @backstage/catalog-model@1.5.0 + - @backstage/config@1.2.0 + - @backstage/core-compat-api@0.2.8-next.0 + - @backstage/core-plugin-api@1.9.3 + - @backstage/errors@1.2.4 + - @backstage/integration-react@1.1.30-next.0 + - @backstage/theme@0.5.6 + - @backstage/plugin-search-common@1.2.13 + - @backstage/plugin-techdocs-react@1.2.7-next.0 + +## 1.10.7 + +### Patch Changes + +- 8fc2622: Fixed an issue that was causing techdocs pages unnecessarily re-render on navigate. +- 6fa652c: Improve default sorting of docs table +- 605b691: Allow for searching TechDocs by entity title +- 60caa92: Fix double scrollbar bug in reader +- Updated dependencies + - @backstage/plugin-techdocs-react@1.2.6 + - @backstage/core-components@0.14.9 + - @backstage/integration@1.13.0 + - @backstage/plugin-catalog-react@1.12.2 + - @backstage/plugin-search-common@1.2.13 + - @backstage/frontend-plugin-api@0.6.7 + - @backstage/integration-react@1.1.29 + - @backstage/plugin-auth-react@0.1.4 + - @backstage/plugin-search-react@1.7.13 + - @backstage/catalog-model@1.5.0 + - @backstage/config@1.2.0 + - @backstage/core-compat-api@0.2.7 + - @backstage/core-plugin-api@1.9.3 + - @backstage/errors@1.2.4 + - @backstage/theme@0.5.6 + +## 1.10.7-next.2 + +### Patch Changes + +- 6fa652c: Improve default sorting of docs table +- Updated dependencies + - @backstage/core-components@0.14.9-next.1 + - @backstage/frontend-plugin-api@0.6.7-next.1 + - @backstage/integration-react@1.1.29-next.0 + - @backstage/plugin-auth-react@0.1.4-next.1 + - @backstage/plugin-catalog-react@1.12.2-next.2 + - @backstage/plugin-search-react@1.7.13-next.1 + - @backstage/plugin-techdocs-react@1.2.6-next.1 + - @backstage/core-compat-api@0.2.7-next.1 + +## 1.10.7-next.1 + +### Patch Changes + +- 60caa92: Fix double scrollbar bug in reader +- Updated dependencies + - @backstage/plugin-catalog-react@1.12.2-next.1 + - @backstage/core-compat-api@0.2.7-next.0 + - @backstage/core-components@0.14.9-next.0 + - @backstage/core-plugin-api@1.9.3 + - @backstage/plugin-search-react@1.7.13-next.0 + - @backstage/catalog-model@1.5.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/frontend-plugin-api@0.6.7-next.0 + - @backstage/integration@1.13.0-next.0 + - @backstage/integration-react@1.1.29-next.0 + - @backstage/theme@0.5.6 + - @backstage/plugin-auth-react@0.1.4-next.0 + - @backstage/plugin-search-common@1.2.12 + - @backstage/plugin-techdocs-react@1.2.6-next.0 + +## 1.10.7-next.0 + +### Patch Changes + +- 8ac9ce5: Fixed a bug with the TechDocsReaderPageProvider not re-rendering when setShadowDom is called, meaning that the useShadowDom hooks were inconsistent. This issue caused the TextSize addon changes not to reapply during navigation. +- Updated dependencies + - @backstage/plugin-techdocs-react@1.2.6-next.0 + - @backstage/core-components@0.14.9-next.0 + - @backstage/integration@1.13.0-next.0 + - @backstage/plugin-catalog-react@1.12.2-next.0 + - @backstage/frontend-plugin-api@0.6.7-next.0 + - @backstage/integration-react@1.1.29-next.0 + - @backstage/plugin-auth-react@0.1.4-next.0 + - @backstage/plugin-search-react@1.7.13-next.0 + - @backstage/core-compat-api@0.2.7-next.0 + - @backstage/core-plugin-api@1.9.3 + - @backstage/catalog-model@1.5.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/theme@0.5.6 + - @backstage/plugin-search-common@1.2.12 + +## 1.10.6 + +### Patch Changes + +- 654af4a: mkdocs-material have updated their CSS variable template, and a few are unset in Backstage. This patch adds the missing variables to ensure coverage. +- cbebad1: Internal updates to allow reusing Backstage's `fetchApi` implementation for event source requests. This allows you to for example, override the `Authorization` header. +- 96cd13e: `TechDocsIndexPage` now accepts an optional `ownerPickerMode` for toggling the behavior of the `EntityOwnerPicker`, exposing a new mode `` particularly suitable for larger catalogs. In this new mode, `EntityOwnerPicker` will display all the users and groups present in the catalog. +- e40bd9a: Fixed bug in `CopyToClipboardButton` component where positioning of the "Copy to clipboard" button in techdocs code snippets was broken in some cases. +- d44a20a: Added additional plugin metadata to `package.json`. +- 1256d88: Fixed an issue preventing the `TechDocsSearchBar` component from opening when clicking on the arrow icon. +- Updated dependencies + - @backstage/core-components@0.14.8 + - @backstage/core-compat-api@0.2.6 + - @backstage/integration@1.12.0 + - @backstage/core-plugin-api@1.9.3 + - @backstage/theme@0.5.6 + - @backstage/plugin-techdocs-react@1.2.5 + - @backstage/plugin-catalog-react@1.12.1 + - @backstage/plugin-search-common@1.2.12 + - @backstage/plugin-search-react@1.7.12 + - @backstage/plugin-auth-react@0.1.3 + - @backstage/integration-react@1.1.28 + - @backstage/frontend-plugin-api@0.6.6 + - @backstage/catalog-model@1.5.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + +## 1.10.6-next.2 + +### Patch Changes + +- d44a20a: Added additional plugin metadata to `package.json`. +- Updated dependencies + - @backstage/core-components@0.14.8-next.2 + - @backstage/integration@1.12.0-next.1 + - @backstage/plugin-techdocs-react@1.2.5-next.2 + - @backstage/plugin-catalog-react@1.12.1-next.2 + - @backstage/plugin-search-common@1.2.12-next.0 + - @backstage/plugin-search-react@1.7.12-next.2 + - @backstage/plugin-auth-react@0.1.3-next.2 + - @backstage/integration-react@1.1.28-next.1 + - @backstage/frontend-plugin-api@0.6.6-next.2 + - @backstage/core-compat-api@0.2.6-next.2 + - @backstage/catalog-model@1.5.0 + - @backstage/config@1.2.0 + - @backstage/core-plugin-api@1.9.3-next.0 + - @backstage/errors@1.2.4 + - @backstage/theme@0.5.6-next.0 + +## 1.10.6-next.1 + +### Patch Changes + +- cbebad1: Internal updates to allow reusing Backstage's `fetchApi` implementation for event source requests. This allows you to for example, override the `Authorization` header. +- Updated dependencies + - @backstage/core-components@0.14.8-next.1 + - @backstage/core-compat-api@0.2.6-next.1 + - @backstage/core-plugin-api@1.9.3-next.0 + - @backstage/integration@1.12.0-next.0 + - @backstage/frontend-plugin-api@0.6.6-next.1 + - @backstage/integration-react@1.1.28-next.0 + - @backstage/plugin-auth-react@0.1.3-next.1 + - @backstage/plugin-catalog-react@1.12.1-next.1 + - @backstage/plugin-search-react@1.7.12-next.1 + - @backstage/plugin-techdocs-react@1.2.5-next.1 + - @backstage/catalog-model@1.5.0 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/theme@0.5.6-next.0 + - @backstage/plugin-search-common@1.2.11 + +## 1.10.6-next.0 + +### Patch Changes + +- 654af4a: mkdocs-material have updated their CSS variable template, and a few are unset in Backstage. This patch adds the missing variables to ensure coverage. +- 96cd13e: `TechDocsIndexPage` now accepts an optional `ownerPickerMode` for toggling the behavior of the `EntityOwnerPicker`, exposing a new mode `` particularly suitable for larger catalogs. In this new mode, `EntityOwnerPicker` will display all the users and groups present in the catalog. +- e40bd9a: Fixed bug in CopyToClipboardButton component where positioning of the "Copy to clipboard" button in techdocs code snippets was broken in some cases +- 1256d88: Fix weird opening behaviour of the component. +- Updated dependencies + - @backstage/theme@0.5.6-next.0 + - @backstage/core-components@0.14.8-next.0 + - @backstage/plugin-search-react@1.7.12-next.0 + - @backstage/plugin-techdocs-react@1.2.5-next.0 + - @backstage/catalog-model@1.5.0 + - @backstage/config@1.2.0 + - @backstage/core-compat-api@0.2.6-next.0 + - @backstage/core-plugin-api@1.9.2 + - @backstage/errors@1.2.4 + - @backstage/frontend-plugin-api@0.6.6-next.0 + - @backstage/integration@1.11.0 + - @backstage/integration-react@1.1.27 + - @backstage/plugin-auth-react@0.1.3-next.0 + - @backstage/plugin-catalog-react@1.12.1-next.0 + - @backstage/plugin-search-common@1.2.11 + +## 1.10.5 + +### Patch Changes + +- d2cc139: Update path in Readme for Plugin Techdocs to show the correct setup information. +- 5863cf7: The `techdocs.builder` config is now optional and it will default to `local`. +- Updated dependencies + - @backstage/core-compat-api@0.2.5 + - @backstage/core-components@0.14.7 + - @backstage/catalog-model@1.5.0 + - @backstage/plugin-auth-react@0.1.2 + - @backstage/plugin-catalog-react@1.12.0 + - @backstage/theme@0.5.4 + - @backstage/integration@1.11.0 + - @backstage/frontend-plugin-api@0.6.5 + - @backstage/integration-react@1.1.27 + - @backstage/plugin-search-react@1.7.11 + - @backstage/plugin-techdocs-react@1.2.4 + +## 1.10.5-next.2 + +### Patch Changes + +- 5863cf7: The `techdocs.builder` config is now optional and it will default to `local`. +- Updated dependencies + - @backstage/plugin-catalog-react@1.12.0-next.2 + - @backstage/core-components@0.14.7-next.2 + - @backstage/integration@1.11.0-next.0 + - @backstage/core-compat-api@0.2.5-next.1 + - @backstage/frontend-plugin-api@0.6.5-next.1 + - @backstage/plugin-search-react@1.7.11-next.1 + - @backstage/integration-react@1.1.27-next.0 + +## 1.10.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.14.6-next.1 + - @backstage/plugin-catalog-react@1.11.4-next.1 + - @backstage/frontend-plugin-api@0.6.5-next.1 + - @backstage/integration-react@1.1.26 + - @backstage/plugin-auth-react@0.1.2-next.1 + - @backstage/plugin-search-react@1.7.11-next.1 + - @backstage/plugin-techdocs-react@1.2.4-next.1 + - @backstage/core-compat-api@0.2.5-next.1 + +## 1.10.5-next.0 + +### Patch Changes + +- d2cc139: Update path in Readme for Plugin Techdocs to show the correct setup information. +- Updated dependencies + - @backstage/core-compat-api@0.2.5-next.0 + - @backstage/catalog-model@1.5.0-next.0 + - @backstage/plugin-auth-react@0.1.1-next.0 + - @backstage/theme@0.5.4-next.0 + - @backstage/core-components@0.14.5-next.0 + - @backstage/plugin-catalog-react@1.11.4-next.0 + - @backstage/plugin-techdocs-react@1.2.4-next.0 + - @backstage/config@1.2.0 + - @backstage/core-plugin-api@1.9.2 + - @backstage/errors@1.2.4 + - @backstage/frontend-plugin-api@0.6.5-next.0 + - @backstage/integration@1.10.0 + - @backstage/integration-react@1.1.26 + - @backstage/plugin-search-common@1.2.11 + - @backstage/plugin-search-react@1.7.11-next.0 + +## 1.10.4 + +### Patch Changes + +- abfbcfc: Updated dependency `@testing-library/react` to `^15.0.0`. +- cb1e3b0: Updated dependency `@testing-library/dom` to `^10.0.0`. +- Updated dependencies + - @backstage/plugin-techdocs-react@1.2.3 + - @backstage/plugin-search-react@1.7.10 + - @backstage/plugin-auth-react@0.1.0 + - @backstage/plugin-catalog-react@1.11.3 + - @backstage/core-compat-api@0.2.4 + - @backstage/core-components@0.14.4 + - @backstage/core-plugin-api@1.9.2 + - @backstage/frontend-plugin-api@0.6.4 + - @backstage/theme@0.5.3 + - @backstage/integration-react@1.1.26 + - @backstage/integration@1.10.0 + - @backstage/catalog-model@1.4.5 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/plugin-search-common@1.2.11 + +## 1.10.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-react@0.1.0-next.1 + - @backstage/frontend-plugin-api@0.6.4-next.1 + - @backstage/core-compat-api@0.2.4-next.1 + - @backstage/catalog-model@1.4.5 + - @backstage/config@1.2.0 + - @backstage/core-components@0.14.4-next.0 + - @backstage/core-plugin-api@1.9.1 + - @backstage/errors@1.2.4 + - @backstage/integration@1.10.0-next.0 + - @backstage/integration-react@1.1.26-next.0 + - @backstage/theme@0.5.2 + - @backstage/plugin-catalog-react@1.11.3-next.1 + - @backstage/plugin-search-common@1.2.11 + - @backstage/plugin-search-react@1.7.10-next.1 + - @backstage/plugin-techdocs-react@1.2.3-next.0 + +## 1.10.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.10.0-next.0 + - @backstage/core-components@0.14.4-next.0 + - @backstage/catalog-model@1.4.5 + - @backstage/config@1.2.0 + - @backstage/core-compat-api@0.2.4-next.0 + - @backstage/core-plugin-api@1.9.1 + - @backstage/errors@1.2.4 + - @backstage/frontend-plugin-api@0.6.4-next.0 + - @backstage/integration-react@1.1.26-next.0 + - @backstage/theme@0.5.2 + - @backstage/plugin-auth-react@0.0.4-next.0 + - @backstage/plugin-catalog-react@1.11.3-next.0 + - @backstage/plugin-search-common@1.2.11 + - @backstage/plugin-search-react@1.7.10-next.0 + - @backstage/plugin-techdocs-react@1.2.3-next.0 + +## 1.10.3 + +### Patch Changes + +- e8f026a: Use ESM exports of react-use library +- Updated dependencies + - @backstage/core-components@0.14.3 + - @backstage/plugin-techdocs-react@1.2.2 + - @backstage/plugin-catalog-react@1.11.2 + - @backstage/plugin-search-react@1.7.9 + - @backstage/frontend-plugin-api@0.6.3 + - @backstage/integration-react@1.1.25 + - @backstage/plugin-auth-react@0.0.3 + - @backstage/core-compat-api@0.2.3 + - @backstage/core-plugin-api@1.9.1 + - @backstage/catalog-model@1.4.5 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.9.1 + - @backstage/theme@0.5.2 + - @backstage/plugin-search-common@1.2.11 + +## 1.10.2 + +### Patch Changes + +- e8f026a: Use ESM exports of react-use library +- Updated dependencies + - @backstage/core-components@0.14.2 + - @backstage/plugin-techdocs-react@1.2.1 + - @backstage/plugin-catalog-react@1.11.1 + - @backstage/plugin-search-react@1.7.8 + - @backstage/frontend-plugin-api@0.6.2 + - @backstage/integration-react@1.1.25 + - @backstage/plugin-auth-react@0.0.2 + - @backstage/core-compat-api@0.2.2 + - @backstage/core-plugin-api@1.9.1 + - @backstage/catalog-model@1.4.5 + - @backstage/config@1.2.0 + - @backstage/errors@1.2.4 + - @backstage/integration@1.9.1 + - @backstage/theme@0.5.2 + - @backstage/plugin-search-common@1.2.11 + +## 1.10.1 + +### Patch Changes + +- 7c2d022: Fixed bug in TechDocs sidebar render that prevented scrollbar from being displayed +- 3f14e9f: Implement a client cookie refresh mechanism. +- 62bcaf8: Use the new generic refresh user cookie provider. +- 28f27f0: Added ESLint rule `no-top-level-material-ui-4-imports` to aid with the migration to Material UI v5. +- Updated dependencies + - @backstage/integration@1.9.1 + - @backstage/config@1.2.0 + - @backstage/core-components@0.14.1 + - @backstage/errors@1.2.4 + - @backstage/plugin-auth-react@0.0.1 + - @backstage/theme@0.5.2 + - @backstage/integration-react@1.1.25 + - @backstage/plugin-techdocs-react@1.2.0 + - @backstage/plugin-catalog-react@1.11.0 + - @backstage/plugin-search-common@1.2.11 + - @backstage/catalog-model@1.4.5 + - @backstage/core-compat-api@0.2.1 + - @backstage/core-plugin-api@1.9.1 + - @backstage/frontend-plugin-api@0.6.1 + - @backstage/plugin-search-react@1.7.7 + +## 1.10.1-next.2 + +### Patch Changes + +- 7c2d022: Fixed bug in TechDocs sidebar render that prevented scrollbar from being displayed +- 3f14e9f: Implement a client cookie refresh mechanism. +- Updated dependencies + - @backstage/integration@1.9.1-next.2 + - @backstage/plugin-techdocs-react@1.2.0-next.2 + - @backstage/core-components@0.14.1-next.2 + - @backstage/plugin-catalog-react@1.11.0-next.2 + - @backstage/integration-react@1.1.25-next.2 + - @backstage/frontend-plugin-api@0.6.1-next.2 + - @backstage/plugin-search-react@1.7.7-next.2 + - @backstage/core-compat-api@0.2.1-next.2 + - @backstage/catalog-model@1.4.5-next.0 + - @backstage/config@1.2.0-next.1 + - @backstage/core-plugin-api@1.9.1-next.1 + - @backstage/errors@1.2.4-next.0 + - @backstage/theme@0.5.2-next.0 + - @backstage/plugin-search-common@1.2.11-next.1 + +## 1.10.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.2.0-next.1 + - @backstage/core-components@0.14.1-next.1 + - @backstage/plugin-catalog-react@1.10.1-next.1 + - @backstage/core-plugin-api@1.9.1-next.1 + - @backstage/integration@1.9.1-next.1 + - @backstage/integration-react@1.1.25-next.1 + - @backstage/plugin-techdocs-react@1.1.17-next.1 + - @backstage/frontend-plugin-api@0.6.1-next.1 + - @backstage/plugin-search-react@1.7.7-next.1 + - @backstage/catalog-model@1.4.5-next.0 + - @backstage/core-compat-api@0.2.1-next.1 + - @backstage/errors@1.2.4-next.0 + - @backstage/theme@0.5.2-next.0 + - @backstage/plugin-search-common@1.2.11-next.1 + +## 1.10.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/errors@1.2.4-next.0 + - @backstage/theme@0.5.2-next.0 + - @backstage/core-components@0.14.1-next.0 + - @backstage/integration-react@1.1.25-next.0 + - @backstage/plugin-catalog-react@1.10.1-next.0 + - @backstage/plugin-search-common@1.2.11-next.0 + - @backstage/catalog-model@1.4.5-next.0 + - @backstage/config@1.1.2-next.0 + - @backstage/core-plugin-api@1.9.1-next.0 + - @backstage/integration@1.9.1-next.0 + - @backstage/plugin-search-react@1.7.7-next.0 + - @backstage/plugin-techdocs-react@1.1.17-next.0 + - @backstage/frontend-plugin-api@0.6.1-next.0 + - @backstage/core-compat-api@0.2.1-next.0 + +## 1.10.0 + +### Minor Changes + +- af4d147: Updated the styling for `` tags to avoid word break. + +### Patch Changes + +- 912ca7b: Use `convertLegacyRouteRefs` to define routes in `/alpha` export plugin. +- 8fe56a8: Widen `@types/react` dependency range to include version 18. +- 3631fb4: Updated dependency `dompurify` to `^3.0.0`. + Updated dependency `@types/dompurify` to `^3.0.0`. +- 1cae748: Updated dependency `git-url-parse` to `^14.0.0`. +- Updated dependencies + - @backstage/frontend-plugin-api@0.6.0 + - @backstage/core-compat-api@0.2.0 + - @backstage/plugin-catalog-react@1.10.0 + - @backstage/core-components@0.14.0 + - @backstage/plugin-techdocs-react@1.1.16 + - @backstage/catalog-model@1.4.4 + - @backstage/theme@0.5.1 + - @backstage/integration@1.9.0 + - @backstage/core-plugin-api@1.9.0 + - @backstage/plugin-search-react@1.7.6 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/integration-react@1.1.24 + - @backstage/plugin-search-common@1.2.10 + +## 1.10.0-next.3 + +### Patch Changes + +- 3631fb4: Updated dependency `dompurify` to `^3.0.0`. + Updated dependency `@types/dompurify` to `^3.0.0`. +- 1cae748: Updated dependency `git-url-parse` to `^14.0.0`. +- Updated dependencies + - @backstage/theme@0.5.1-next.1 + - @backstage/integration@1.9.0-next.1 + - @backstage/core-components@0.14.0-next.2 + - @backstage/plugin-catalog-react@1.10.0-next.3 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/config@1.1.1 + - @backstage/core-compat-api@0.2.0-next.3 + - @backstage/core-plugin-api@1.9.0-next.1 + - @backstage/errors@1.2.3 + - @backstage/frontend-plugin-api@0.6.0-next.3 + - @backstage/integration-react@1.1.24-next.2 + - @backstage/plugin-search-common@1.2.10 + - @backstage/plugin-search-react@1.7.6-next.3 + - @backstage/plugin-techdocs-react@1.1.16-next.2 + +## 1.10.0-next.2 + +### Patch Changes + +- 8fe56a8: Widen `@types/react` dependency range to include version 18. +- Updated dependencies + - @backstage/core-components@0.14.0-next.1 + - @backstage/plugin-techdocs-react@1.1.16-next.1 + - @backstage/core-plugin-api@1.9.0-next.1 + - @backstage/frontend-plugin-api@0.6.0-next.2 + - @backstage/plugin-catalog-react@1.10.0-next.2 + - @backstage/plugin-search-react@1.7.6-next.2 + - @backstage/theme@0.5.1-next.0 + - @backstage/integration-react@1.1.24-next.1 + - @backstage/core-compat-api@0.2.0-next.2 + - @backstage/config@1.1.1 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/errors@1.2.3 + - @backstage/integration@1.9.0-next.0 + - @backstage/plugin-search-common@1.2.10 + +## 1.10.0-next.1 + +### Minor Changes + +- af4d147: Updated the styling for `` tags to avoid word break. + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.6.0-next.1 + - @backstage/core-compat-api@0.2.0-next.1 + - @backstage/core-components@0.14.0-next.0 + - @backstage/catalog-model@1.4.4-next.0 + - @backstage/core-plugin-api@1.8.3-next.0 + - @backstage/integration@1.9.0-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.1 + - @backstage/plugin-search-react@1.7.6-next.1 + - @backstage/integration-react@1.1.24-next.0 + - @backstage/plugin-techdocs-react@1.1.16-next.0 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0 + - @backstage/plugin-search-common@1.2.10 + +## 1.9.4-next.0 + +### Patch Changes + +- 912ca7b: Use `convertLegacyRouteRefs` to define routes in `/alpha` export plugin. +- Updated dependencies + - @backstage/core-compat-api@0.1.2-next.0 + - @backstage/plugin-catalog-react@1.9.4-next.0 + - @backstage/frontend-plugin-api@0.5.1-next.0 + - @backstage/core-components@0.13.10 + - @backstage/plugin-search-react@1.7.6-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/core-plugin-api@1.8.2 + - @backstage/errors@1.2.3 + - @backstage/integration@1.8.0 + - @backstage/integration-react@1.1.23 + - @backstage/theme@0.5.0 + - @backstage/plugin-search-common@1.2.10 + - @backstage/plugin-techdocs-react@1.1.15 + +## 1.9.3 + +### Patch Changes + +- 4016f21: Remove some unused dependencies +- Updated dependencies + - @backstage/core-compat-api@0.1.1 + - @backstage/frontend-plugin-api@0.5.0 + - @backstage/core-components@0.13.10 + - @backstage/core-plugin-api@1.8.2 + - @backstage/plugin-techdocs-react@1.1.15 + - @backstage/plugin-catalog-react@1.9.3 + - @backstage/plugin-search-react@1.7.5 + - @backstage/integration-react@1.1.23 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/integration@1.8.0 + - @backstage/theme@0.5.0 + - @backstage/plugin-search-common@1.2.10 + +## 1.9.3-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/core-compat-api@0.1.1-next.2 + - @backstage/frontend-plugin-api@0.4.1-next.2 + - @backstage/plugin-catalog-react@1.9.3-next.2 + - @backstage/plugin-search-react@1.7.5-next.2 + - @backstage/integration-react@1.1.23-next.0 + +## 1.9.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-plugin-api@1.8.2-next.0 + - @backstage/core-components@0.13.10-next.1 + - @backstage/core-compat-api@0.1.1-next.1 + - @backstage/frontend-plugin-api@0.4.1-next.1 + - @backstage/integration-react@1.1.23-next.0 + - @backstage/plugin-catalog-react@1.9.3-next.1 + - @backstage/plugin-search-react@1.7.5-next.1 + - @backstage/plugin-techdocs-react@1.1.15-next.1 + - @backstage/integration@1.8.0 + - @backstage/config@1.1.1 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0 + - @backstage/plugin-search-common@1.2.9 + +## 1.9.3-next.0 + +### Patch Changes + +- 4016f21: Remove some unused dependencies +- Updated dependencies + - @backstage/core-components@0.13.10-next.0 + - @backstage/frontend-plugin-api@0.4.1-next.0 + - @backstage/plugin-techdocs-react@1.1.15-next.0 + - @backstage/plugin-catalog-react@1.9.3-next.0 + - @backstage/integration-react@1.1.22 + - @backstage/plugin-search-react@1.7.5-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/core-compat-api@0.1.1-next.0 + - @backstage/core-plugin-api@1.8.1 + - @backstage/errors@1.2.3 + - @backstage/integration@1.8.0 + - @backstage/theme@0.5.0 + - @backstage/plugin-search-common@1.2.9 + +## 1.9.2 + +### Patch Changes + +- 03d0b6d: The `convertLegacyRouteRef` utility used by the alpha exports is now imported from `@backstage/core-compat-api`. +- a1227cc: Wrap `/alpha` export extension elements in backwards compatibility wrapper. +- 5814122: Updated `/alpha` exports to fit new naming patterns. +- 36c94b8: Refactor of the alpha exports due to API change in how extension IDs are constructed. +- Updated dependencies + - @backstage/core-compat-api@0.1.0 + - @backstage/core-plugin-api@1.8.1 + - @backstage/frontend-plugin-api@0.4.0 + - @backstage/plugin-catalog-react@1.9.2 + - @backstage/core-components@0.13.9 + - @backstage/theme@0.5.0 + - @backstage/plugin-search-react@1.7.4 + - @backstage/integration@1.8.0 + - @backstage/integration-react@1.1.22 + - @backstage/plugin-techdocs-react@1.1.14 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-search-common@1.2.9 + +## 1.9.2-next.4 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.9-next.3 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/core-compat-api@0.1.0-next.3 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/errors@1.2.3 + - @backstage/frontend-plugin-api@0.4.0-next.3 + - @backstage/integration@1.8.0-next.1 + - @backstage/integration-react@1.1.22-next.1 + - @backstage/theme@0.5.0-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.3 + - @backstage/plugin-search-common@1.2.8 + - @backstage/plugin-search-react@1.7.4-next.3 + - @backstage/plugin-techdocs-react@1.1.14-next.3 + +## 1.9.2-next.3 + +### Patch Changes + +- a1227cc: Wrap `/alpha` export extension elements in backwards compatibility wrapper. +- 36c94b8: Refactor of the alpha exports due to API change in how extension IDs are constructed. +- Updated dependencies + - @backstage/frontend-plugin-api@0.4.0-next.2 + - @backstage/theme@0.5.0-next.1 + - @backstage/core-compat-api@0.1.0-next.2 + - @backstage/plugin-catalog-react@1.9.2-next.2 + - @backstage/plugin-search-react@1.7.4-next.2 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/core-components@0.13.9-next.2 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/errors@1.2.3 + - @backstage/integration@1.8.0-next.1 + - @backstage/integration-react@1.1.22-next.1 + - @backstage/plugin-search-common@1.2.8 + - @backstage/plugin-techdocs-react@1.1.14-next.2 + +## 1.9.2-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.4.0-next.1 + - @backstage/core-components@0.13.9-next.1 + - @backstage/core-plugin-api@1.8.1-next.1 + - @backstage/plugin-catalog-react@1.9.2-next.1 + - @backstage/plugin-search-react@1.7.4-next.1 + - @backstage/integration@1.8.0-next.1 + - @backstage/core-compat-api@0.0.1-next.1 + - @backstage/integration-react@1.1.22-next.1 + - @backstage/plugin-techdocs-react@1.1.14-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/theme@0.5.0-next.0 + - @backstage/plugin-search-common@1.2.8 + +## 1.9.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-compat-api@0.0.1-next.0 + +## 1.9.2-next.0 + +### Patch Changes + +- 03d0b6dcdc: The `convertLegacyRouteRef` utility used by the alpha exports is now imported from `@backstage/core-compat-api`. +- Updated dependencies + - @backstage/core-compat-api@0.0.2-next.0 + - @backstage/core-plugin-api@1.8.1-next.0 + - @backstage/plugin-catalog-react@1.9.2-next.0 + - @backstage/core-components@0.13.9-next.0 + - @backstage/plugin-search-react@1.7.4-next.0 + - @backstage/integration@1.8.0-next.0 + - @backstage/theme@0.5.0-next.0 + - @backstage/frontend-plugin-api@0.3.1-next.0 + - @backstage/integration-react@1.1.22-next.0 + - @backstage/plugin-techdocs-react@1.1.14-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-search-common@1.2.8 + +## 1.9.0 + +### Minor Changes + +- 17f93d5589: A new analytics event `not-found` will be published when a user visits a documentation site that does not exist + +### Patch Changes + +- 4728b3960d: Fixed navigation bug that caused users to not be scrolled to the top of a new page. Fixed navigation bug where using backwards and forwards browser navigation did not scroll users to the correct place on the TechDoc page. +- a3add7a682: Export alpha routes and nav item extension, only available for applications that uses the new Frontend system. +- 71c97e7d73: The \`spec.lifecycle' field in entities will now always be rendered as a string. +- 68fc9dc60e: Updated alpha exports according to routing changes in `@backstage/frontend-plugin-api`. +- 6c2b872153: Add official support for React 18. +- 0bf6ebda88: Added entity page content for the new plugin exported via `/alpha`. +- 67cc85bb14: Switched the conditional `react-dom/client` import to use `import(...)` rather than `require(...)`. +- 4aa43f62aa: Updated dependency `cross-fetch` to `^4.0.0`. +- 38cda52746: Added support for React 18. The new `createRoot` API from `react-dom/client` will now be used if present. +- fdb5e23602: Import `MissingAnnotationEmptyState` from `@backstage/plugin-catalog-react` to remove the cyclical dependency +- Updated dependencies + - @backstage/plugin-catalog-react@1.9.0 + - @backstage/core-components@0.13.8 + - @backstage/frontend-plugin-api@0.3.0 + - @backstage/integration@1.7.2 + - @backstage/integration-react@1.1.21 + - @backstage/core-plugin-api@1.8.0 + - @backstage/plugin-techdocs-react@1.1.13 + - @backstage/plugin-search-react@1.7.2 + - @backstage/theme@0.4.4 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-search-common@1.2.8 + +## 1.9.0-next.2 + +### Minor Changes + +- [#20851](https://github.com/backstage/backstage/pull/20851) [`17f93d5589`](https://github.com/backstage/backstage/commit/17f93d5589812df3dea53d956212e184b080fbac) Thanks [@agentbellnorm](https://github.com/agentbellnorm)! - A new analytics event `not-found` will be published when a user visits a documentation site that does not exist + +### Patch Changes + +- [#20842](https://github.com/backstage/backstage/pull/20842) [`fdb5e23602`](https://github.com/backstage/backstage/commit/fdb5e2360299c5faa30f4d4236fc548b94d37446) Thanks [@benjdlambert](https://github.com/benjdlambert)! - Import `MissingAnnotationEmptyState` from `@backstage/plugin-catalog-react` to remove the cyclical dependency + +- Updated dependencies + - @backstage/core-components@0.13.8-next.2 + - @backstage/frontend-plugin-api@0.3.0-next.2 + - @backstage/plugin-catalog-react@1.9.0-next.2 + - @backstage/integration-react@1.1.21-next.1 + - @backstage/plugin-search-react@1.7.2-next.2 + - @backstage/plugin-techdocs-react@1.1.13-next.2 + +## 1.8.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.7.2-next.0 + - @backstage/frontend-plugin-api@0.3.0-next.1 + - @backstage/plugin-catalog-react@1.9.0-next.1 + - @backstage/plugin-search-react@1.7.2-next.1 + - @backstage/integration-react@1.1.21-next.1 + - @backstage/core-components@0.13.8-next.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/errors@1.2.3 + - @backstage/theme@0.4.4-next.0 + - @backstage/plugin-search-common@1.2.7 + - @backstage/plugin-techdocs-react@1.1.13-next.1 + +## 1.8.1-next.0 + +### Patch Changes + +- 4728b3960d: Fixed navigation bug that caused users to not be scrolled to the top of a new page. Fixed navigation bug where using backwards and forwards browser navigation did not scroll users to the correct place on the TechDoc page. +- a3add7a682: Export alpha routes and nav item extension, only available for applications that uses the new Frontend system. +- 71c97e7d73: The \`spec.lifecycle' field in entities will now always be rendered as a string. +- 68fc9dc60e: Updated alpha exports according to routing changes in `@backstage/frontend-plugin-api`. +- 6c2b872153: Add official support for React 18. +- 0bf6ebda88: Added entity page content for the new plugin exported via `/alpha`. +- 67cc85bb14: Switched the conditional `react-dom/client` import to use `import(...)` rather than `require(...)`. +- 38cda52746: Added support for React 18. The new `createRoot` API from `react-dom/client` will now be used if present. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/frontend-plugin-api@0.3.0-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/integration-react@1.1.21-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/plugin-techdocs-react@1.1.13-next.0 + - @backstage/plugin-search-react@1.7.2-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/integration@1.7.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-search-common@1.2.7 + +## 1.8.0 + +### Minor Changes + +- 27740caa2d: Added experimental support for declarative integration via the `/alpha` subpath. + +### Patch Changes + +- 4918f65ab2: Create an experimental `TechDocsSearchResultItemExtension` for declarative integration with Backstage; it can be accessed via the `/alpha` import. +- 3605370af6: Improved `DocsTable` to display pagination controls dynamically, appearing only when needed. +- 0296f272b4: The \`spec.lifecycle' field in entities will now always be rendered as a string. +- 9a1fce352e: Updated dependency `@testing-library/jest-dom` to `^6.0.0`. +- f95af4e540: Updated dependency `@testing-library/dom` to `^9.0.0`. +- 9468a67b92: Added support for React 18. The new `createRoot` API from `react-dom/client` will now be used if present. +- df449a7a31: Add kind column by default to TechDocsTable +- Updated dependencies + - @backstage/integration@1.7.1 + - @backstage/plugin-catalog-react@1.8.5 + - @backstage/frontend-plugin-api@0.2.0 + - @backstage/core-plugin-api@1.7.0 + - @backstage/core-components@0.13.6 + - @backstage/integration-react@1.1.20 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/plugin-search-react@1.7.1 + - @backstage/plugin-techdocs-react@1.1.12 + - @backstage/theme@0.4.3 + - @backstage/config@1.1.1 + - @backstage/plugin-search-common@1.2.7 + +## 1.7.1-next.2 + +### Patch Changes + +- 3605370af6: Improved `DocsTable` to display pagination controls dynamically, appearing only when needed. +- Updated dependencies + - @backstage/frontend-plugin-api@0.2.0-next.2 + - @backstage/integration-react@1.1.20-next.2 + - @backstage/core-components@0.13.6-next.2 + - @backstage/core-plugin-api@1.7.0-next.1 + - @backstage/catalog-model@1.4.3-next.0 + - @backstage/plugin-catalog-react@1.8.5-next.2 + - @backstage/integration@1.7.1-next.1 + - @backstage/errors@1.2.3-next.0 + - @backstage/plugin-search-react@1.7.1-next.2 + - @backstage/theme@0.4.3-next.0 + - @backstage/config@1.1.1-next.0 + - @backstage/plugin-search-common@1.2.7-next.0 + - @backstage/plugin-techdocs-react@1.1.12-next.2 + +## 1.7.1-next.1 + +### Patch Changes + +- 4918f65ab2: Create an experimental `TechDocsSearchResultItemExtension` for declarative integration with Backstage; it can be accessed via the `/alpha` import. +- df449a7a31: Add kind column by default to TechDocsTable +- Updated dependencies + - @backstage/frontend-plugin-api@0.1.1-next.1 + - @backstage/core-components@0.13.6-next.1 + - @backstage/plugin-search-react@1.7.1-next.1 + - @backstage/integration-react@1.1.20-next.1 + - @backstage/plugin-catalog-react@1.8.5-next.1 + - @backstage/plugin-techdocs-react@1.1.12-next.1 + - @backstage/core-plugin-api@1.7.0-next.0 + - @backstage/config@1.1.0 + - @backstage/catalog-model@1.4.2 + - @backstage/errors@1.2.2 + - @backstage/integration@1.7.1-next.0 + - @backstage/theme@0.4.2 + - @backstage/plugin-search-common@1.2.6 + +## 1.7.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.7.1-next.0 + - @backstage/plugin-catalog-react@1.8.5-next.0 + - @backstage/core-plugin-api@1.7.0-next.0 + - @backstage/core-components@0.13.6-next.0 + - @backstage/integration-react@1.1.20-next.0 + - @backstage/config@1.1.0 + - @backstage/plugin-search-react@1.7.1-next.0 + - @backstage/plugin-techdocs-react@1.1.12-next.0 + - @backstage/catalog-model@1.4.2 + - @backstage/errors@1.2.2 + - @backstage/theme@0.4.2 + - @backstage/plugin-search-common@1.2.6 + +## 1.7.0 + +### Minor Changes + +- e44f45ac4515: This change allows a new annotation of `backstage.io/techdocs-entity` this ref allows you to reference another entity for its TechDocs. This allows you have a single TechDoc for all items in a system, for example you might have a frontend and a backend in the same repo. This would allow you to have TechDocs build under a `System` entity while referencing the system e.g.: `backstage.io/techdocs-entity: system:default/example` that will show the systems docs in both the TechDocs button and the TechDocs tab without needing to do duplicate builds and filling the TechDocs page with garbage. + +### Patch Changes + +- 88c9525a36f3: Fixed bug in styles that caused next and previous links in footer to overlap page content. +- 406b786a2a2c: Mark package as being free of side effects, allowing more optimized Webpack builds. +- 8cec7664e146: Removed `@types/node` dependency +- Updated dependencies + - @backstage/integration-react@1.1.19 + - @backstage/plugin-catalog-react@1.8.4 + - @backstage/core-components@0.13.5 + - @backstage/config@1.1.0 + - @backstage/catalog-model@1.4.2 + - @backstage/core-plugin-api@1.6.0 + - @backstage/errors@1.2.2 + - @backstage/integration@1.7.0 + - @backstage/plugin-search-common@1.2.6 + - @backstage/plugin-search-react@1.7.0 + - @backstage/plugin-techdocs-react@1.1.10 + - @backstage/theme@0.4.2 + +## 1.7.0-next.3 + +### Patch Changes + +- 406b786a2a2c: Mark package as being free of side effects, allowing more optimized Webpack builds. +- Updated dependencies + - @backstage/catalog-model@1.4.2-next.2 + - @backstage/config@1.1.0-next.2 + - @backstage/core-components@0.13.5-next.3 + - @backstage/core-plugin-api@1.6.0-next.3 + - @backstage/errors@1.2.2-next.0 + - @backstage/integration@1.7.0-next.3 + - @backstage/integration-react@1.1.19-next.3 + - @backstage/plugin-catalog-react@1.8.4-next.3 + - @backstage/plugin-search-common@1.2.6-next.2 + - @backstage/plugin-search-react@1.7.0-next.3 + - @backstage/plugin-techdocs-react@1.1.10-next.3 + - @backstage/theme@0.4.2-next.0 + +## 1.7.0-next.2 + +### Minor Changes + +- e44f45ac4515: This change allows a new annotation of `backstage.io/techdocs-entity` this ref allows you to reference another entity for its TechDocs. This allows you have a single TechDoc for all items in a system, for example you might have a frontend and a backend in the same repo. This would allow you to have TechDocs build under a `System` entity while referencing the system e.g.: `backstage.io/techdocs-entity: system:default/example` that will show the systems docs in both the TechDocs button and the TechDocs tab without needing to do duplicate builds and filling the TechDocs page with garbage. + +### Patch Changes + +- 8cec7664e146: Removed `@types/node` dependency +- Updated dependencies + - @backstage/integration-react@1.1.19-next.2 + - @backstage/core-components@0.13.5-next.2 + - @backstage/core-plugin-api@1.6.0-next.2 + - @backstage/config@1.1.0-next.1 + - @backstage/plugin-catalog-react@1.8.4-next.2 + - @backstage/plugin-search-react@1.7.0-next.2 + - @backstage/plugin-techdocs-react@1.1.10-next.2 + - @backstage/integration@1.7.0-next.2 + - @backstage/catalog-model@1.4.2-next.1 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + - @backstage/plugin-search-common@1.2.6-next.1 + +## 1.6.9-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.4-next.1 + - @backstage/core-components@0.13.5-next.1 + - @backstage/config@1.1.0-next.0 + - @backstage/integration@1.7.0-next.1 + - @backstage/plugin-search-react@1.7.0-next.1 + - @backstage/integration-react@1.1.19-next.1 + - @backstage/plugin-techdocs-react@1.1.10-next.1 + - @backstage/catalog-model@1.4.2-next.0 + - @backstage/core-plugin-api@1.6.0-next.1 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + - @backstage/plugin-search-common@1.2.6-next.0 + +## 1.6.8-next.0 + +### Patch Changes + +- 88c9525a36f3: Fixed bug in styles that caused next and previous links in footer to overlap page content. +- Updated dependencies + - @backstage/integration-react@1.1.18-next.0 + - @backstage/integration@1.7.0-next.0 + - @backstage/core-plugin-api@1.6.0-next.0 + - @backstage/core-components@0.13.5-next.0 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + - @backstage/plugin-catalog-react@1.8.3-next.0 + - @backstage/plugin-search-common@1.2.5 + - @backstage/plugin-search-react@1.6.5-next.0 + - @backstage/plugin-techdocs-react@1.1.10-next.0 + +## 1.6.6 + +### Patch Changes + +- Updated dependencies + - @backstage/integration-react@1.1.16 + - @backstage/integration@1.6.0 + - @backstage/core-components@0.13.4 + - @backstage/plugin-catalog-react@1.8.1 + - @backstage/core-plugin-api@1.5.3 + - @backstage/plugin-search-react@1.6.4 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + - @backstage/plugin-search-common@1.2.5 + - @backstage/plugin-techdocs-react@1.1.9 + +## 1.6.6-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.1-next.1 + - @backstage/integration-react@1.1.16-next.1 + +## 1.6.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/integration-react@1.1.16-next.1 + - @backstage/integration@1.5.1 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/errors@1.2.1 + - @backstage/theme@0.4.1 + - @backstage/plugin-catalog-react@1.8.1-next.0 + - @backstage/plugin-search-common@1.2.5 + - @backstage/plugin-search-react@1.6.4-next.0 + - @backstage/plugin-techdocs-react@1.1.9-next.0 + +## 1.6.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.4-next.0 + - @backstage/core-plugin-api@1.5.3 + - @backstage/plugin-catalog-react@1.8.1-next.0 + - @backstage/plugin-search-react@1.6.4-next.0 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1 + - @backstage/integration@1.5.1 + - @backstage/integration-react@1.1.16-next.0 + - @backstage/theme@0.4.1 + - @backstage/plugin-search-common@1.2.5 + - @backstage/plugin-techdocs-react@1.1.9-next.0 + +## 1.6.5 + +### Patch Changes + +- Updated dependencies + - @backstage/theme@0.4.1 + - @backstage/errors@1.2.1 + - @backstage/plugin-catalog-react@1.8.0 + - @backstage/core-components@0.13.3 + - @backstage/core-plugin-api@1.5.3 + - @backstage/catalog-model@1.4.1 + - @backstage/config@1.0.8 + - @backstage/integration@1.5.1 + - @backstage/integration-react@1.1.15 + - @backstage/plugin-search-common@1.2.5 + - @backstage/plugin-search-react@1.6.3 + - @backstage/plugin-techdocs-react@1.1.8 + +## 1.6.5-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.8.0-next.2 + - @backstage/theme@0.4.1-next.1 + - @backstage/core-plugin-api@1.5.3-next.1 + - @backstage/core-components@0.13.3-next.2 + - @backstage/catalog-model@1.4.1-next.0 + - @backstage/config@1.0.8 + - @backstage/errors@1.2.1-next.0 + - @backstage/integration@1.5.1-next.0 + - @backstage/integration-react@1.1.15-next.2 + - @backstage/plugin-search-common@1.2.5-next.0 + - @backstage/plugin-search-react@1.6.3-next.2 + - @backstage/plugin-techdocs-react@1.1.8-next.2 + +## 1.6.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/theme@0.4.1-next.0 + - @backstage/core-components@0.13.3-next.1 + - @backstage/core-plugin-api@1.5.3-next.0 + - @backstage/integration-react@1.1.15-next.1 + - @backstage/plugin-catalog-react@1.7.1-next.1 + - @backstage/plugin-search-react@1.6.3-next.1 + - @backstage/plugin-techdocs-react@1.1.8-next.1 + - @backstage/config@1.0.8 + +## 1.6.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/errors@1.2.1-next.0 + - @backstage/core-components@0.13.3-next.0 + - @backstage/catalog-model@1.4.1-next.0 + - @backstage/config@1.0.8 + - @backstage/core-plugin-api@1.5.2 + - @backstage/integration@1.5.1-next.0 + - @backstage/integration-react@1.1.15-next.0 + - @backstage/theme@0.4.0 + - @backstage/plugin-catalog-react@1.7.1-next.0 + - @backstage/plugin-search-common@1.2.5-next.0 + - @backstage/plugin-search-react@1.6.3-next.0 + - @backstage/plugin-techdocs-react@1.1.8-next.0 + +## 1.6.4 + +### Patch Changes + +- 2f660eb573cc: Fix SearchBar styles & update StoryBook stories for custom styles for `notchedOutline` class. +- 956d09e8ea68: Change deprecated local references to import from shared `plugin-techdocs-react` plugin +- e33beb1f2a8e: Make the documentation pages printable (also handy for exporting to PDF) +- Updated dependencies + - @backstage/core-plugin-api@1.5.2 + - @backstage/plugin-search-react@1.6.2 + - @backstage/core-components@0.13.2 + - @backstage/theme@0.4.0 + - @backstage/integration@1.5.0 + - @backstage/plugin-catalog-react@1.7.0 + - @backstage/catalog-model@1.4.0 + - @backstage/errors@1.2.0 + - @backstage/plugin-techdocs-react@1.1.7 + - @backstage/integration-react@1.1.14 + - @backstage/config@1.0.8 + - @backstage/plugin-search-common@1.2.4 + +## 1.6.4-next.3 + +### Patch Changes + +- e33beb1f2a8e: Make the documentation pages printable (also handy for exporting to PDF) +- Updated dependencies + - @backstage/plugin-search-react@1.6.2-next.3 + - @backstage/core-components@0.13.2-next.3 + - @backstage/catalog-model@1.4.0-next.1 + - @backstage/config@1.0.7 + - @backstage/core-plugin-api@1.5.2-next.0 + - @backstage/errors@1.2.0-next.0 + - @backstage/integration@1.5.0-next.0 + - @backstage/integration-react@1.1.14-next.3 + - @backstage/theme@0.4.0-next.1 + - @backstage/plugin-catalog-react@1.7.0-next.3 + - @backstage/plugin-search-common@1.2.4-next.0 + - @backstage/plugin-techdocs-react@1.1.7-next.3 + +## 1.6.3-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/theme@0.4.0-next.1 + - @backstage/plugin-catalog-react@1.7.0-next.2 + - @backstage/core-components@0.13.2-next.2 + - @backstage/integration-react@1.1.14-next.2 + - @backstage/plugin-search-react@1.6.1-next.2 + - @backstage/plugin-techdocs-react@1.1.7-next.2 + - @backstage/config@1.0.7 + - @backstage/core-plugin-api@1.5.2-next.0 + +## 1.6.3-next.1 + +### Patch Changes + +- 2f660eb573cc: Fix SearchBar styles & update StoryBook stories for custom styles for `notchedOutline` class. +- Updated dependencies + - @backstage/integration@1.5.0-next.0 + - @backstage/errors@1.2.0-next.0 + - @backstage/plugin-search-react@1.6.1-next.1 + - @backstage/core-components@0.13.2-next.1 + - @backstage/plugin-catalog-react@1.7.0-next.1 + - @backstage/catalog-model@1.4.0-next.0 + - @backstage/core-plugin-api@1.5.2-next.0 + - @backstage/integration-react@1.1.14-next.1 + - @backstage/plugin-techdocs-react@1.1.7-next.1 + - @backstage/config@1.0.7 + - @backstage/theme@0.4.0-next.0 + - @backstage/plugin-search-common@1.2.4-next.0 + +## 1.6.3-next.0 + +### Patch Changes + +- 956d09e8ea68: Change deprecated local references to import from shared `plugin-techdocs-react` plugin +- Updated dependencies + - @backstage/plugin-catalog-react@1.7.0-next.0 + - @backstage/theme@0.4.0-next.0 + - @backstage/plugin-techdocs-react@1.1.7-next.0 + - @backstage/integration@1.4.5 + - @backstage/config@1.0.7 + - @backstage/core-components@0.13.2-next.0 + - @backstage/core-plugin-api@1.5.1 + - @backstage/integration-react@1.1.14-next.0 + - @backstage/plugin-search-react@1.6.1-next.0 + - @backstage/catalog-model@1.3.0 + - @backstage/errors@1.1.5 + - @backstage/plugin-search-common@1.2.3 + +## 1.6.2 + +### Patch Changes + +- 863beb49498: Re-add the possibility to have trailing slashes in Techdocs navigation. +- Updated dependencies + - @backstage/theme@0.3.0 + - @backstage/plugin-catalog-react@1.6.0 + - @backstage/integration@1.4.5 + - @backstage/plugin-search-react@1.6.0 + - @backstage/core-components@0.13.1 + - @backstage/integration-react@1.1.13 + - @backstage/plugin-techdocs-react@1.1.6 + - @backstage/catalog-model@1.3.0 + - @backstage/config@1.0.7 + - @backstage/core-plugin-api@1.5.1 + - @backstage/errors@1.1.5 + - @backstage/plugin-search-common@1.2.3 + +## 1.6.2-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/theme@0.3.0-next.0 + - @backstage/core-components@0.13.1-next.1 + - @backstage/plugin-search-react@1.6.0-next.2 + - @backstage/integration-react@1.1.13-next.2 + - @backstage/plugin-catalog-react@1.6.0-next.2 + - @backstage/plugin-techdocs-react@1.1.6-next.1 + - @backstage/config@1.0.7 + - @backstage/core-plugin-api@1.5.1 + +## 1.6.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.1-next.0 + - @backstage/core-plugin-api@1.5.1 + - @backstage/plugin-catalog-react@1.6.0-next.1 + - @backstage/plugin-search-react@1.6.0-next.1 + - @backstage/integration-react@1.1.13-next.1 + - @backstage/plugin-techdocs-react@1.1.6-next.0 + - @backstage/config@1.0.7 + +## 1.6.2-next.0 + +### Patch Changes + +- 863beb49498: Re-add the possibility to have trailing slashes in Techdocs navigation. +- Updated dependencies + - @backstage/plugin-catalog-react@1.6.0-next.0 + - @backstage/integration@1.4.5-next.0 + - @backstage/plugin-search-react@1.6.0-next.0 + - @backstage/integration-react@1.1.13-next.0 + - @backstage/core-components@0.13.0 + - @backstage/core-plugin-api@1.5.1 + - @backstage/catalog-model@1.3.0 + - @backstage/config@1.0.7 + - @backstage/errors@1.1.5 + - @backstage/theme@0.2.19 + - @backstage/plugin-search-common@1.2.3 + - @backstage/plugin-techdocs-react@1.1.5 + +## 1.6.1 + +### Patch Changes + +- 6c809d1a41c: Minor visual tweaks to adapt to changes in mkdocs-material v9 + +- b2e182cdfa4: Fixes a UI bug in search result item which rendered the item text with incorrect font size and color + +- 847a1eee3da: Change anchor links color in Techdocs content + + With the color (mkdocs supplied) used for anchor links the background and foreground colors do not have a sufficient contrast ratio. Using the link color from theme palette. + +- 8e00acb28db: Small tweaks to remove warnings in the console during development (mainly focusing on techdocs) + +- 2e493480626: Fix a bug in sub-path navigation due to double addition of a sub-path if one was set up in `app.baseUrl`. + +- e0c6e8b9c3c: Update peer dependencies + +- Updated dependencies + - @backstage/core-components@0.13.0 + - @backstage/plugin-catalog-react@1.5.0 + - @backstage/plugin-search-react@1.5.2 + - @backstage/plugin-techdocs-react@1.1.5 + - @backstage/integration-react@1.1.12 + - @backstage/theme@0.2.19 + - @backstage/core-plugin-api@1.5.1 + - @backstage/catalog-model@1.3.0 + - @backstage/integration@1.4.4 + - @backstage/config@1.0.7 + - @backstage/errors@1.1.5 + - @backstage/plugin-search-common@1.2.3 + +## 1.6.1-next.3 + +### Patch Changes + +- 2e493480626: Fix a bug in sub-path navigation due to double addition of a sub-path if one was set up in `app.baseUrl`. +- Updated dependencies + - @backstage/plugin-catalog-react@1.5.0-next.3 + - @backstage/catalog-model@1.3.0-next.0 + - @backstage/core-components@0.13.0-next.3 + - @backstage/config@1.0.7 + - @backstage/core-plugin-api@1.5.1-next.1 + - @backstage/errors@1.1.5 + - @backstage/integration@1.4.4-next.0 + - @backstage/integration-react@1.1.12-next.3 + - @backstage/theme@0.2.19-next.0 + - @backstage/plugin-search-common@1.2.3-next.0 + - @backstage/plugin-search-react@1.5.2-next.3 + - @backstage/plugin-techdocs-react@1.1.5-next.3 + +## 1.6.1-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.12.6-next.2 + - @backstage/plugin-catalog-react@1.4.1-next.2 + - @backstage/core-plugin-api@1.5.1-next.1 + - @backstage/catalog-model@1.2.1 + - @backstage/config@1.0.7 + - @backstage/errors@1.1.5 + - @backstage/integration@1.4.4-next.0 + - @backstage/integration-react@1.1.12-next.2 + - @backstage/theme@0.2.19-next.0 + - @backstage/plugin-search-common@1.2.3-next.0 + - @backstage/plugin-search-react@1.5.2-next.2 + - @backstage/plugin-techdocs-react@1.1.5-next.2 + +## 1.6.1-next.1 + +### Patch Changes + +- 6c809d1a41c: Minor visual tweaks to adapt to changes in mkdocs-material v9 + +- 847a1eee3da: Change anchor links color in Techdocs content + + With the color (mkdocs supplied) used for anchor links the background and foreground colors do not have a sufficient contrast ratio. Using the link color from theme palette. + +- e0c6e8b9c3c: Update peer dependencies + +- Updated dependencies + - @backstage/core-components@0.12.6-next.1 + - @backstage/integration-react@1.1.12-next.1 + - @backstage/core-plugin-api@1.5.1-next.0 + - @backstage/plugin-techdocs-react@1.1.5-next.1 + - @backstage/plugin-catalog-react@1.4.1-next.1 + - @backstage/integration@1.4.4-next.0 + - @backstage/plugin-search-react@1.5.2-next.1 + - @backstage/theme@0.2.19-next.0 + - @backstage/catalog-model@1.2.1 + - @backstage/config@1.0.7 + - @backstage/errors@1.1.5 + - @backstage/plugin-search-common@1.2.3-next.0 + +## 1.6.1-next.0 + +### Patch Changes + +- b2e182cdfa4: Fixes a UI bug in search result item which rendered the item text with incorrect font size and color +- 8e00acb28db: Small tweaks to remove warnings in the console during development (mainly focusing on techdocs) +- Updated dependencies + - @backstage/core-components@0.12.6-next.0 + - @backstage/plugin-search-react@1.5.2-next.0 + - @backstage/plugin-techdocs-react@1.1.5-next.0 + - @backstage/plugin-catalog-react@1.4.1-next.0 + - @backstage/integration-react@1.1.12-next.0 + - @backstage/core-plugin-api@1.5.0 + - @backstage/config@1.0.7 + - @backstage/integration@1.4.3 + - @backstage/catalog-model@1.2.1 + - @backstage/errors@1.1.5 + - @backstage/theme@0.2.18 + - @backstage/plugin-search-common@1.2.2 + +## 1.6.0 + +### Minor Changes + +- 3f75b7607ca: Add ability to pass icon as function to have ability to customize it by search item + +### Patch Changes + +- 65454876fb2: Minor API report tweaks +- 54a1e133b56: Fix bug that caused next and previous links not to work with certain versions of mkdocs-material +- f320c299c67: The HTML tag attributes in the documentation content inserted to shadow DOM is preserved to improve accessibility +- cb8ec97cdeb: Change black & white colors to be theme aware +- c10384a9235: Switch to using `LinkButton` instead of the deprecated `Button` +- 8adfda60ae1: Updated dependency `jss` to `~10.10.0`. +- 52b0022dab7: Updated dependency `msw` to `^1.0.0`. +- 238cf657c09: Copy to clipboard now works in a not secure context. +- Updated dependencies + - @backstage/core-components@0.12.5 + - @backstage/plugin-techdocs-react@1.1.4 + - @backstage/plugin-catalog-react@1.4.0 + - @backstage/plugin-search-react@1.5.1 + - @backstage/errors@1.1.5 + - @backstage/core-plugin-api@1.5.0 + - @backstage/catalog-model@1.2.1 + - @backstage/integration-react@1.1.11 + - @backstage/integration@1.4.3 + - @backstage/config@1.0.7 + - @backstage/theme@0.2.18 + - @backstage/plugin-search-common@1.2.2 + +## 1.6.0-next.2 + +### Patch Changes + +- 65454876fb2: Minor API report tweaks +- Updated dependencies + - @backstage/core-components@0.12.5-next.2 + - @backstage/plugin-techdocs-react@1.1.4-next.2 + - @backstage/plugin-catalog-react@1.4.0-next.2 + - @backstage/plugin-search-react@1.5.1-next.2 + - @backstage/core-plugin-api@1.5.0-next.2 + - @backstage/integration-react@1.1.11-next.2 + - @backstage/config@1.0.7-next.0 + - @backstage/integration@1.4.3-next.0 + +## 1.6.0-next.1 + +### Patch Changes + +- 54a1e133b56: Fix bug that caused next and previous links not to work with certain versions of mkdocs-material +- cb8ec97cdeb: Change black & white colors to be theme aware +- c10384a9235: Switch to using `LinkButton` instead of the deprecated `Button` +- 8adfda60ae1: Updated dependency `jss` to `~10.10.0`. +- 52b0022dab7: Updated dependency `msw` to `^1.0.0`. +- 238cf657c09: Copy to clipboard now works in a not secure context. +- Updated dependencies + - @backstage/core-components@0.12.5-next.1 + - @backstage/errors@1.1.5-next.0 + - @backstage/plugin-techdocs-react@1.1.4-next.1 + - @backstage/core-plugin-api@1.4.1-next.1 + - @backstage/integration-react@1.1.11-next.1 + - @backstage/integration@1.4.3-next.0 + - @backstage/config@1.0.7-next.0 + - @backstage/theme@0.2.18-next.0 + - @backstage/plugin-catalog-react@1.4.0-next.1 + - @backstage/catalog-model@1.2.1-next.1 + - @backstage/plugin-search-common@1.2.2-next.0 + - @backstage/plugin-search-react@1.5.1-next.1 + +## 1.6.0-next.0 + +### Minor Changes + +- 3f75b7607c: Add ability to pass icon as function to have ability to customize it by search item + +### Patch Changes + +- f320c299c6: The HTML tag attributes in the documentation content inserted to shadow DOM is preserved to improve accessibility +- Updated dependencies + - @backstage/plugin-catalog-react@1.4.0-next.0 + - @backstage/core-plugin-api@1.4.1-next.0 + - @backstage/catalog-model@1.2.1-next.0 + - @backstage/plugin-techdocs-react@1.1.4-next.0 + - @backstage/config@1.0.6 + - @backstage/core-components@0.12.5-next.0 + - @backstage/errors@1.1.4 + - @backstage/integration@1.4.2 + - @backstage/integration-react@1.1.11-next.0 + - @backstage/theme@0.2.17 + - @backstage/plugin-search-common@1.2.1 + - @backstage/plugin-search-react@1.5.1-next.0 + +## 1.5.0 + +### Minor Changes + +- 20840b36b4: Update DocsTable and EntityListDocsTable to accept overrides for Material Table options. +- 0eaa579f89: The `TechDocsSearchResultListItem` component is now a search result extension. This means that when rendered as a child of components that render search extensions, the `result`, `rank`, and `highlight` properties are optional. See the [documentation](https://backstage.io/docs/features/search/how-to-guides#how-to-render-search-results-using-extensions) for more details. + +### Patch Changes + +- c8e09cc383: Fixed bug in Techdocs reader where a techdocs page with a hash in the URL did not always jump to the document anchor. + +- cad5607411: Improve view: remove footer overlay on large screen + +- 66e2aab4c4: `ListItem` wrapper component moved to `SearchResultListItemExtension` for all `*SearchResultListItems` that are exported as extensions. This is to make sure the list only contains list elements. + + Note: If you have implemented a custom result list item, we recommend you to remove the list item wrapper to avoid nested `
  • ` elements. + +- 4660b63947: Create a TechDocs `` addon that allows users to open images in a light-box on documentation pages, they can navigate between images if there are several on one page. + + Here's an example on how to use it in a Backstage app: + + ```diff + import { + DefaultTechDocsHome, + TechDocsIndexPage, + TechDocsReaderPage, + } from '@backstage/plugin-techdocs'; + import { TechDocsAddons } from '@backstage/plugin-techdocs-react/alpha'; + +import { LightBox } from '@backstage/plugin-techdocs-module-addons-contrib'; + + const AppRoutes = () => { + + // other plugin routes + }> + + + } + > + + + + + + ; + }; + ``` + +- Updated dependencies + - @backstage/core-components@0.12.4 + - @backstage/catalog-model@1.2.0 + - @backstage/theme@0.2.17 + - @backstage/core-plugin-api@1.4.0 + - @backstage/plugin-catalog-react@1.3.0 + - @backstage/plugin-search-react@1.5.0 + - @backstage/config@1.0.6 + - @backstage/errors@1.1.4 + - @backstage/integration@1.4.2 + - @backstage/integration-react@1.1.10 + - @backstage/plugin-search-common@1.2.1 + - @backstage/plugin-techdocs-react@1.1.3 + +## 1.5.0-next.2 + +### Patch Changes + +- 66e2aab4c4: `ListItem` wrapper component moved to `SearchResultListItemExtension` for all `*SearchResultListItems` that are exported as extensions. This is to make sure the list only contains list elements. + + Note: If you have implemented a custom result list item, we recommend you to remove the list item wrapper to avoid nested `
  • ` elements. + +- Updated dependencies + - @backstage/catalog-model@1.2.0-next.1 + - @backstage/plugin-search-react@1.5.0-next.1 + - @backstage/core-components@0.12.4-next.1 + - @backstage/config@1.0.6 + - @backstage/core-plugin-api@1.3.0 + - @backstage/errors@1.1.4 + - @backstage/integration@1.4.2 + - @backstage/integration-react@1.1.10-next.1 + - @backstage/theme@0.2.16 + - @backstage/plugin-catalog-react@1.3.0-next.2 + - @backstage/plugin-search-common@1.2.1 + - @backstage/plugin-techdocs-react@1.1.3-next.2 + +## 1.5.0-next.1 + +### Minor Changes + +- 20840b36b4: Update DocsTable and EntityListDocsTable to accept overrides for Material Table options. +- 0eaa579f89: The `TechDocsSearchResultListItem` component is now a search result extension. This means that when rendered as a child of components that render search extensions, the `result`, `rank`, and `highlight` properties are optional. See the [documentation](https://backstage.io/docs/features/search/how-to-guides#how-to-render-search-results-using-extensions) for more details. + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.12.4-next.0 + - @backstage/plugin-search-react@1.5.0-next.0 + - @backstage/plugin-catalog-react@1.3.0-next.1 + - @backstage/catalog-model@1.1.6-next.0 + - @backstage/config@1.0.6 + - @backstage/core-plugin-api@1.3.0 + - @backstage/errors@1.1.4 + - @backstage/integration@1.4.2 + - @backstage/integration-react@1.1.10-next.0 + - @backstage/theme@0.2.16 + - @backstage/plugin-search-common@1.2.1 + - @backstage/plugin-techdocs-react@1.1.3-next.1 + +## 1.4.4-next.0 + +### Patch Changes + +- c8e09cc383: Fixed bug in Techdocs reader where a techdocs page with a hash in the URL did not always jump to the document anchor. +- cad5607411: Improve view: remove footer overlay on large screen +- Updated dependencies + - @backstage/plugin-catalog-react@1.3.0-next.0 + - @backstage/catalog-model@1.1.6-next.0 + - @backstage/plugin-techdocs-react@1.1.3-next.0 + - @backstage/integration-react@1.1.9 + +## 1.4.3 + +### Patch Changes + +- a74dd61534: Fix sizing of build log component to render all lines +- 80ce4e8c29: Small updates to some components to ensure theme typography properties are inherited correctly. +- 7115c7389b: Updated dependency `jss` to `~10.9.0`. +- Updated dependencies + - @backstage/catalog-model@1.1.5 + - @backstage/plugin-catalog-react@1.2.4 + - @backstage/core-components@0.12.3 + - @backstage/plugin-search-react@1.4.0 + - @backstage/core-plugin-api@1.3.0 + - @backstage/plugin-techdocs-react@1.1.2 + - @backstage/config@1.0.6 + - @backstage/errors@1.1.4 + - @backstage/integration@1.4.2 + - @backstage/integration-react@1.1.9 + - @backstage/theme@0.2.16 + - @backstage/plugin-search-common@1.2.1 + +## 1.4.3-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-react@1.4.0-next.2 + - @backstage/core-plugin-api@1.3.0-next.1 + - @backstage/plugin-catalog-react@1.2.4-next.2 + - @backstage/plugin-techdocs-react@1.1.2-next.2 + - @backstage/catalog-model@1.1.5-next.1 + - @backstage/config@1.0.6-next.0 + - @backstage/core-components@0.12.3-next.2 + - @backstage/errors@1.1.4 + - @backstage/integration@1.4.2-next.0 + - @backstage/integration-react@1.1.9-next.2 + - @backstage/theme@0.2.16 + - @backstage/plugin-search-common@1.2.1-next.0 + +## 1.4.3-next.1 + +### Patch Changes + +- a74dd61534: Fix sizing of build log component to render all lines +- Updated dependencies + - @backstage/config@1.0.6-next.0 + - @backstage/catalog-model@1.1.5-next.1 + - @backstage/core-components@0.12.3-next.1 + - @backstage/core-plugin-api@1.2.1-next.0 + - @backstage/errors@1.1.4 + - @backstage/integration@1.4.2-next.0 + - @backstage/integration-react@1.1.9-next.1 + - @backstage/theme@0.2.16 + - @backstage/plugin-catalog-react@1.2.4-next.1 + - @backstage/plugin-search-common@1.2.1-next.0 + - @backstage/plugin-search-react@1.3.2-next.1 + - @backstage/plugin-techdocs-react@1.1.2-next.1 + +## 1.4.3-next.0 + +### Patch Changes + +- 7115c7389b: Updated dependency `jss` to `~10.9.0`. +- Updated dependencies + - @backstage/catalog-model@1.1.5-next.0 + - @backstage/plugin-catalog-react@1.2.4-next.0 + - @backstage/core-components@0.12.3-next.0 + - @backstage/plugin-techdocs-react@1.1.2-next.0 + - @backstage/config@1.0.5 + - @backstage/core-plugin-api@1.2.0 + - @backstage/errors@1.1.4 + - @backstage/integration@1.4.1 + - @backstage/integration-react@1.1.9-next.0 + - @backstage/theme@0.2.16 + - @backstage/plugin-search-common@1.2.0 + - @backstage/plugin-search-react@1.3.2-next.0 + +## 1.4.2 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.12.2 + - @backstage/integration-react@1.1.8 + - @backstage/plugin-catalog-react@1.2.3 + - @backstage/plugin-search-react@1.3.1 + - @backstage/plugin-techdocs-react@1.1.1 + +## 1.4.1 + +### Patch Changes + +- d3fea4ae0a: Internal fixes to avoid implicit usage of globals +- 2e701b3796: Internal refactor to use `react-router-dom` rather than `react-router`. +- a19cffbeed: Update search links to only have header as linkable text +- 5d3058355d: Add `react/forbid-elements` linter rule for button, suggest Material UI `Button` +- 3280711113: Updated dependency `msw` to `^0.49.0`. +- 786f1b1419: Support older versions of react-router +- Updated dependencies + - @backstage/plugin-techdocs-react@1.1.0 + - @backstage/core-plugin-api@1.2.0 + - @backstage/plugin-search-react@1.3.0 + - @backstage/core-components@0.12.1 + - @backstage/errors@1.1.4 + - @backstage/plugin-catalog-react@1.2.2 + - @backstage/integration-react@1.1.7 + - @backstage/integration@1.4.1 + - @backstage/plugin-search-common@1.2.0 + - @backstage/catalog-model@1.1.4 + - @backstage/config@1.0.5 + - @backstage/theme@0.2.16 + +## 1.4.1-next.4 + +### Patch Changes + +- 2e701b3796: Internal refactor to use `react-router-dom` rather than `react-router`. +- Updated dependencies + - @backstage/core-components@0.12.1-next.4 + - @backstage/plugin-catalog-react@1.2.2-next.4 + - @backstage/plugin-search-react@1.3.0-next.4 + - @backstage/catalog-model@1.1.4-next.1 + - @backstage/config@1.0.5-next.1 + - @backstage/core-plugin-api@1.2.0-next.2 + - @backstage/errors@1.1.4-next.1 + - @backstage/integration@1.4.1-next.1 + - @backstage/integration-react@1.1.7-next.4 + - @backstage/theme@0.2.16 + - @backstage/plugin-search-common@1.2.0-next.3 + - @backstage/plugin-techdocs-react@1.0.7-next.4 + +## 1.4.1-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.12.1-next.3 + - @backstage/catalog-model@1.1.4-next.1 + - @backstage/config@1.0.5-next.1 + - @backstage/core-plugin-api@1.2.0-next.2 + - @backstage/errors@1.1.4-next.1 + - @backstage/integration@1.4.1-next.1 + - @backstage/integration-react@1.1.7-next.3 + - @backstage/theme@0.2.16 + - @backstage/plugin-catalog-react@1.2.2-next.3 + - @backstage/plugin-search-common@1.2.0-next.2 + - @backstage/plugin-search-react@1.3.0-next.3 + - @backstage/plugin-techdocs-react@1.0.7-next.3 + +## 1.4.1-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/core-plugin-api@1.2.0-next.2 + - @backstage/plugin-search-react@1.3.0-next.2 + - @backstage/core-components@0.12.1-next.2 + - @backstage/plugin-catalog-react@1.2.2-next.2 + - @backstage/plugin-search-common@1.2.0-next.2 + - @backstage/integration-react@1.1.7-next.2 + - @backstage/plugin-techdocs-react@1.0.7-next.2 + - @backstage/catalog-model@1.1.4-next.1 + - @backstage/config@1.0.5-next.1 + - @backstage/errors@1.1.4-next.1 + - @backstage/integration@1.4.1-next.1 + - @backstage/theme@0.2.16 + +## 1.4.1-next.1 + +### Patch Changes + +- d3fea4ae0a: Internal fixes to avoid implicit usage of globals +- a19cffbeed: Update search links to only have header as linkable text +- Updated dependencies + - @backstage/core-components@0.12.1-next.1 + - @backstage/plugin-search-react@1.2.2-next.1 + - @backstage/core-plugin-api@1.1.1-next.1 + - @backstage/plugin-catalog-react@1.2.2-next.1 + - @backstage/integration-react@1.1.7-next.1 + - @backstage/plugin-techdocs-react@1.0.7-next.1 + - @backstage/config@1.0.5-next.1 + - @backstage/integration@1.4.1-next.1 + - @backstage/catalog-model@1.1.4-next.1 + - @backstage/errors@1.1.4-next.1 + - @backstage/theme@0.2.16 + - @backstage/plugin-search-common@1.1.2-next.1 + +## 1.4.1-next.0 + +### Patch Changes + +- 3280711113: Updated dependency `msw` to `^0.49.0`. +- Updated dependencies + - @backstage/plugin-techdocs-react@1.0.7-next.0 + - @backstage/core-components@0.12.1-next.0 + - @backstage/core-plugin-api@1.1.1-next.0 + - @backstage/integration-react@1.1.7-next.0 + - @backstage/integration@1.4.1-next.0 + - @backstage/plugin-catalog-react@1.2.2-next.0 + - @backstage/catalog-model@1.1.4-next.0 + - @backstage/config@1.0.5-next.0 + - @backstage/errors@1.1.4-next.0 + - @backstage/theme@0.2.16 + - @backstage/plugin-search-common@1.1.2-next.0 + - @backstage/plugin-search-react@1.2.2-next.0 + +## 1.4.0 + +### Minor Changes + +- 5691baea69: Add ability to configure filters when using EntityListDocsGrid + + The following example will render two sections of cards grid: + + - One section for documentations tagged as `recommended` + - One section for documentations tagged as `runbook` + + ```js + + entity?.metadata?.tags?.includes('recommended') ?? false, + }, + { + title: "RunBooks Documentation", + filterPredicate: entity => + entity?.metadata?.tags?.includes('runbook') ?? false, + } + ]}} /> + ``` + +- 63705e73d9: Hide document description if not provided + +- 847fc588a6: Updated TechDocs header to include label for source code icon and updated label to reflect Kind name + +### Patch Changes + +- 9e4d8e6198: Fix logic bug that broke techdocs-cli-embedded-app +- e92aa15f01: Bumped `canvas` dependency to the latest version, which has better Node.js v18 support. +- cbe11d1e23: Tweak README +- 7573b65232: Internal refactor of imports to avoid circular dependencies +- c1784a4980: Replaces in-code uses of `GitHub` with `Github` and deprecates old versions. +- 3a1a999b7b: Include query parameters when navigating to relative links in documents +- bd2aab4726: An analytics event matching the semantics of the `click` action is now captured when users click links within a TechDocs document. +- Updated dependencies + - @backstage/plugin-catalog-react@1.2.1 + - @backstage/core-components@0.12.0 + - @backstage/core-plugin-api@1.1.0 + - @backstage/integration@1.4.0 + - @backstage/catalog-model@1.1.3 + - @backstage/plugin-techdocs-react@1.0.6 + - @backstage/integration-react@1.1.6 + - @backstage/plugin-search-react@1.2.1 + - @backstage/config@1.0.4 + - @backstage/errors@1.1.3 + - @backstage/theme@0.2.16 + - @backstage/plugin-search-common@1.1.1 + +## 1.4.0-next.2 + +### Patch Changes + +- e92aa15f01: Bumped `canvas` dependency to the latest version, which has better Node.js v18 support. +- Updated dependencies + - @backstage/core-components@0.12.0-next.1 + - @backstage/catalog-model@1.1.3-next.0 + - @backstage/config@1.0.4-next.0 + - @backstage/core-plugin-api@1.1.0-next.0 + - @backstage/errors@1.1.3-next.0 + - @backstage/integration@1.4.0-next.0 + - @backstage/integration-react@1.1.6-next.1 + - @backstage/theme@0.2.16 + - @backstage/plugin-catalog-react@1.2.1-next.1 + - @backstage/plugin-search-common@1.1.1-next.0 + - @backstage/plugin-search-react@1.2.1-next.1 + - @backstage/plugin-techdocs-react@1.0.6-next.1 + +## 1.4.0-next.1 + +### Patch Changes + +- 9e4d8e6198: Fix logic bug that broke techdocs-cli-embedded-app + +## 1.4.0-next.0 + +### Minor Changes + +- 5691baea69: Add ability to configure filters when using EntityListDocsGrid + + The following example will render two sections of cards grid: + + - One section for documentations tagged as `recommended` + - One section for documentations tagged as `runbook` + + ```js + + entity?.metadata?.tags?.includes('recommended') ?? false, + }, + { + title: "RunBooks Documentation", + filterPredicate: entity => + entity?.metadata?.tags?.includes('runbook') ?? false, + } + ]}} /> + ``` + +### Patch Changes + +- cbe11d1e23: Tweak README +- 7573b65232: Internal refactor of imports to avoid circular dependencies +- c1784a4980: Replaces in-code uses of `GitHub` with `Github` and deprecates old versions. +- 3a1a999b7b: Include query parameters when navigating to relative links in documents +- Updated dependencies + - @backstage/plugin-catalog-react@1.2.1-next.0 + - @backstage/core-components@0.12.0-next.0 + - @backstage/core-plugin-api@1.1.0-next.0 + - @backstage/integration@1.4.0-next.0 + - @backstage/catalog-model@1.1.3-next.0 + - @backstage/integration-react@1.1.6-next.0 + - @backstage/plugin-search-react@1.2.1-next.0 + - @backstage/plugin-techdocs-react@1.0.6-next.0 + - @backstage/config@1.0.4-next.0 + - @backstage/errors@1.1.3-next.0 + - @backstage/theme@0.2.16 + - @backstage/plugin-search-common@1.1.1-next.0 + +## 1.3.3 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2 + - @backstage/plugin-catalog-react@1.2.0 + - @backstage/core-components@0.11.2 + - @backstage/plugin-search-react@1.2.0 + - @backstage/plugin-search-common@1.1.0 + - @backstage/plugin-techdocs-react@1.0.5 + - @backstage/integration-react@1.1.5 + - @backstage/core-plugin-api@1.0.7 + - @backstage/config@1.0.3 + - @backstage/errors@1.1.2 + - @backstage/integration@1.3.2 + - @backstage/theme@0.2.16 + +## 1.3.3-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.2.0-next.2 + - @backstage/plugin-search-common@1.1.0-next.2 + - @backstage/catalog-model@1.1.2-next.2 + - @backstage/config@1.0.3-next.2 + - @backstage/core-components@0.11.2-next.2 + - @backstage/core-plugin-api@1.0.7-next.2 + - @backstage/errors@1.1.2-next.2 + - @backstage/integration@1.3.2-next.2 + - @backstage/integration-react@1.1.5-next.2 + - @backstage/theme@0.2.16 + - @backstage/plugin-search-react@1.2.0-next.2 + - @backstage/plugin-techdocs-react@1.0.5-next.2 + +## 1.3.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.2.0-next.1 + - @backstage/plugin-search-react@1.2.0-next.1 + - @backstage/plugin-search-common@1.1.0-next.1 + - @backstage/core-components@0.11.2-next.1 + - @backstage/core-plugin-api@1.0.7-next.1 + - @backstage/catalog-model@1.1.2-next.1 + - @backstage/config@1.0.3-next.1 + - @backstage/errors@1.1.2-next.1 + - @backstage/integration@1.3.2-next.1 + - @backstage/integration-react@1.1.5-next.1 + - @backstage/theme@0.2.16 + - @backstage/plugin-techdocs-react@1.0.5-next.1 + +## 1.3.3-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.1.2-next.0 + - @backstage/core-components@0.11.2-next.0 + - @backstage/plugin-catalog-react@1.1.5-next.0 + - @backstage/plugin-techdocs-react@1.0.5-next.0 + - @backstage/integration-react@1.1.5-next.0 + - @backstage/plugin-search-react@1.1.1-next.0 + - @backstage/config@1.0.3-next.0 + - @backstage/core-plugin-api@1.0.7-next.0 + - @backstage/errors@1.1.2-next.0 + - @backstage/integration@1.3.2-next.0 + - @backstage/theme@0.2.16 + - @backstage/plugin-search-common@1.0.2-next.0 + +## 1.3.2 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- eadf56bbbf: Bump `git-url-parse` version to `^13.0.0` +- 3f739be9d9: Minor API signatures cleanup +- 763fb81e82: Internal refactor to use more type safe code when dealing with route parameters. +- 7d47def9c4: Removed dependency on `@types/jest`. +- 817f3196f6: Updated the `TechDocsReaderPage` to be compatible with React Router v6 stable. +- 7a95c705fa: Fixed a bug where addons wouldn't render on sub pages when using React Route v6 stable. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- ca8d5a6eae: Use the new `SearchAutocomplete` component in the `TechDocsSearch` component to maintain consistency across search experiences and avoid code duplication. +- 829f14a9b0: Always update the title and sub-title when the location changes on a `TechDocs` reader page. +- e97d616f08: Fixed a bug where scrolling for anchors where the id starts with number didn't work for the current page. +- ef9ab322de: Minor API signatures cleanup +- Updated dependencies + - @backstage/core-components@0.11.1 + - @backstage/core-plugin-api@1.0.6 + - @backstage/plugin-catalog-react@1.1.4 + - @backstage/plugin-search-react@1.1.0 + - @backstage/plugin-techdocs-react@1.0.4 + - @backstage/integration@1.3.1 + - @backstage/catalog-model@1.1.1 + - @backstage/config@1.0.2 + - @backstage/errors@1.1.1 + - @backstage/integration-react@1.1.4 + - @backstage/plugin-search-common@1.0.1 + +## 1.3.2-next.3 + +### Patch Changes + +- 7d47def9c4: Removed dependency on `@types/jest`. +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.4-next.2 + - @backstage/catalog-model@1.1.1-next.0 + - @backstage/config@1.0.2-next.0 + - @backstage/core-components@0.11.1-next.3 + - @backstage/core-plugin-api@1.0.6-next.3 + - @backstage/errors@1.1.1-next.0 + - @backstage/integration@1.3.1-next.2 + - @backstage/integration-react@1.1.4-next.2 + - @backstage/plugin-techdocs-react@1.0.4-next.2 + +## 1.3.2-next.2 + +### Patch Changes + +- eadf56bbbf: Bump `git-url-parse` version to `^13.0.0` +- 7a95c705fa: Fixed a bug where addons wouldn't render on sub pages when using React Route v6 stable. +- 667d917488: Updated dependency `msw` to `^0.47.0`. +- 87ec2ba4d6: Updated dependency `msw` to `^0.46.0`. +- ca8d5a6eae: Use the new `SearchAutocomplete` component in the `TechDocsSearch` component to maintain consistency across search experiences and avoid code duplication. +- e97d616f08: Fixed a bug where scrolling for anchors where the id starts with number didn't work for the current page. +- Updated dependencies + - @backstage/integration@1.3.1-next.1 + - @backstage/core-components@0.11.1-next.2 + - @backstage/core-plugin-api@1.0.6-next.2 + - @backstage/integration-react@1.1.4-next.1 + - @backstage/plugin-search-react@1.1.0-next.2 + +## 1.3.2-next.1 + +### Patch Changes + +- 817f3196f6: Updated React Router dependencies to be peer dependencies. +- 763fb81e82: Internal refactor to use more type safe code when dealing with route parameters. +- 817f3196f6: Updated the `TechDocsReaderPage` to be compatible with React Router v6 stable. +- Updated dependencies + - @backstage/core-components@0.11.1-next.1 + - @backstage/core-plugin-api@1.0.6-next.1 + - @backstage/plugin-catalog-react@1.1.4-next.1 + - @backstage/plugin-search-react@1.0.2-next.1 + - @backstage/plugin-techdocs-react@1.0.4-next.1 + +## 1.3.2-next.0 + +### Patch Changes + +- 3f739be9d9: Minor API signatures cleanup +- bf5e9030eb: Updated dependency `msw` to `^0.45.0`. +- 829f14a9b0: Always update the title and sub-title when the location changes on a `TechDocs` reader page. +- ef9ab322de: Minor API signatures cleanup +- Updated dependencies + - @backstage/core-plugin-api@1.0.6-next.0 + - @backstage/core-components@0.11.1-next.0 + - @backstage/integration-react@1.1.4-next.0 + - @backstage/integration@1.3.1-next.0 + - @backstage/plugin-catalog-react@1.1.4-next.0 + - @backstage/plugin-search-react@1.0.2-next.0 + - @backstage/plugin-techdocs-react@1.0.4-next.0 + - @backstage/plugin-search-common@1.0.1-next.0 + +## 1.3.1 + +### Patch Changes + +- e924d2d013: Added back reduction in size, this fixes the extremely large TeachDocs headings +- b86ed4d990: Add highlight to active navigation item and navigation parents. +- 7a98c73dc8: Fixed techdocs sidebar layout bug for medium devices. +- 8acb22205c: Scroll techdocs navigation into focus and expand any nested navigation items. +- Updated dependencies + - @backstage/integration@1.3.0 + - @backstage/core-components@0.11.0 + - @backstage/core-plugin-api@1.0.5 + - @backstage/plugin-catalog-react@1.1.3 + - @backstage/plugin-techdocs-react@1.0.3 + - @backstage/integration-react@1.1.3 + - @backstage/plugin-search-react@1.0.1 + +## 1.3.1-next.2 + +### Patch Changes + +- 8acb22205c: Scroll techdocs navigation into focus and expand any nested navigation items. +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.3-next.2 + - @backstage/core-components@0.11.0-next.2 + - @backstage/integration-react@1.1.3-next.1 + - @backstage/plugin-search-react@1.0.1-next.1 + - @backstage/plugin-techdocs-react@1.0.3-next.2 + +## 1.3.1-next.1 + +### Patch Changes + +- b86ed4d990: Add highlight to active navigation item and navigation parents. +- Updated dependencies + - @backstage/core-components@0.10.1-next.1 + - @backstage/integration@1.3.0-next.1 + - @backstage/plugin-techdocs-react@1.0.3-next.1 + - @backstage/plugin-catalog-react@1.1.3-next.1 + +## 1.3.1-next.0 + +### Patch Changes + +- 7a98c73dc8: Fixed techdocs sidebar layout bug for medium devices. +- Updated dependencies + - @backstage/integration@1.3.0-next.0 + - @backstage/core-plugin-api@1.0.5-next.0 + - @backstage/integration-react@1.1.3-next.0 + - @backstage/plugin-catalog-react@1.1.3-next.0 + - @backstage/core-components@0.10.1-next.0 + - @backstage/plugin-search-react@1.0.1-next.0 + - @backstage/plugin-techdocs-react@1.0.3-next.0 + +## 1.3.0 + +### Minor Changes + +- ebf3eb1641: Use the same initial filter `owned` for the `TechDocsIndexPage` as for the `CatalogPage`. + + If you prefer to keep the previous behavior, you can change the default for the initial filter + to `all` (or `starred` if you rather prefer that). + + + + In general, with this change you will be able to set props at `TechDocsIndexPage`. + +### Patch Changes + +- a70869e775: Updated dependency `msw` to `^0.43.0`. + +- 8006d0f9bf: Updated dependency `msw` to `^0.44.0`. + +- e2d7b76f43: Upgrade git-url-parse to 12.0.0. + + Motivation for upgrade is transitively upgrading parse-url which is vulnerable + to several CVEs detected by Snyk. + + - SNYK-JS-PARSEURL-2935944 + - SNYK-JS-PARSEURL-2935947 + - SNYK-JS-PARSEURL-2936249 + +- 3cbebf710e: Reorder browser tab title in Techdocs pages to have the site name first. + +- 726577958f: Remove the 60% factor from the font size calculation of headers to use the exact size defined in BackstageTheme. + +- 7739141ab2: Fix: When docs are shown in an entity page under the docs tab the sidebars start overlapping with the header and tabs in the page when you scroll the documentation content. + +- Updated dependencies + - @backstage/core-components@0.10.0 + - @backstage/catalog-model@1.1.0 + - @backstage/plugin-techdocs-react@1.0.2 + - @backstage/plugin-search-react@1.0.0 + - @backstage/plugin-search-common@1.0.0 + - @backstage/core-plugin-api@1.0.4 + - @backstage/integration@1.2.2 + - @backstage/integration-react@1.1.2 + - @backstage/plugin-catalog-react@1.1.2 + - @backstage/theme@0.2.16 + - @backstage/errors@1.1.0 + +## 1.2.1-next.3 + +### Patch Changes + +- a70869e775: Updated dependency `msw` to `^0.43.0`. +- Updated dependencies + - @backstage/core-plugin-api@1.0.4-next.0 + - @backstage/core-components@0.10.0-next.3 + - @backstage/integration-react@1.1.2-next.3 + - @backstage/integration@1.2.2-next.3 + - @backstage/catalog-model@1.1.0-next.3 + - @backstage/plugin-catalog-react@1.1.2-next.3 + - @backstage/plugin-search-react@0.2.2-next.3 + - @backstage/plugin-techdocs-react@1.0.2-next.2 + +## 1.2.1-next.2 + +### Patch Changes + +- e2d7b76f43: Upgrade git-url-parse to 12.0.0. + + Motivation for upgrade is transitively upgrading parse-url which is vulnerable + to several CVEs detected by Snyk. + + - SNYK-JS-PARSEURL-2935944 + - SNYK-JS-PARSEURL-2935947 + - SNYK-JS-PARSEURL-2936249 + +- 7739141ab2: Fix: When docs are shown in an entity page under the docs tab the sidebars start overlapping with the header and tabs in the page when you scroll the documentation content. + +- Updated dependencies + - @backstage/core-components@0.10.0-next.2 + - @backstage/catalog-model@1.1.0-next.2 + - @backstage/plugin-search-react@0.2.2-next.2 + - @backstage/theme@0.2.16-next.1 + - @backstage/integration@1.2.2-next.2 + - @backstage/plugin-catalog-react@1.1.2-next.2 + - @backstage/integration-react@1.1.2-next.2 + - @backstage/plugin-techdocs-react@1.0.2-next.1 + +## 1.2.1-next.1 + +### Patch Changes + +- 726577958f: Remove the 60% factor from the font size calculation of headers to use the exact size defined in BackstageTheme. +- Updated dependencies + - @backstage/core-components@0.9.6-next.1 + - @backstage/catalog-model@1.1.0-next.1 + - @backstage/errors@1.1.0-next.0 + - @backstage/theme@0.2.16-next.0 + - @backstage/integration@1.2.2-next.1 + - @backstage/integration-react@1.1.2-next.1 + - @backstage/plugin-catalog-react@1.1.2-next.1 + - @backstage/plugin-search-common@0.3.6-next.0 + - @backstage/plugin-search-react@0.2.2-next.1 + +## 1.2.1-next.0 + +### Patch Changes + +- 3cbebf710e: Reorder browser tab title in Techdocs pages to have the site name first. +- Updated dependencies + - @backstage/catalog-model@1.1.0-next.0 + - @backstage/core-components@0.9.6-next.0 + - @backstage/plugin-techdocs-react@1.0.2-next.0 + - @backstage/integration@1.2.2-next.0 + - @backstage/plugin-catalog-react@1.1.2-next.0 + - @backstage/integration-react@1.1.2-next.0 + - @backstage/plugin-search-react@0.2.2-next.0 + +## 1.2.0 + +### Minor Changes + +- fe7614ea54: Add an optional icon to the Catalog and TechDocs search results + +### Patch Changes + +- d047d81295: Use entity title as label in `TechDocsReaderPageHeader` if available + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. + +- bff65e6958: Updated sidebar-related logic to use `` + `useSidebarPinState()` and/or `` + `useSidebarOpenState()` from `@backstage/core-components`. + +- 915700f64f: In order to simplify analytics on top of the search experience in Backstage, the provided `<*ResultListItem />` component now captures a `discover` analytics event instead of a `click` event. This event includes the result rank as its `value` and, like a click, the URL/path clicked to as its `to` attribute. + +- 881fbd7e8d: Fix `EntityTechdocsContent` component to use objects instead of `` elements, otherwise "outlet" will be null on sub-pages and add-ons won't render. + +- 17c059dfd0: Restructures reader style transformations to improve code readability: + + - Extracts the style rules to separate files; + - Creates a hook that processes each rule; + - And creates another hook that returns a transformer responsible for injecting them into the head tag of a given element. + +- 3b45ad701f: Packages a set of tweaks to the TechDocs addons rendering process: + + - Prevents displaying sidebars until page styles are loaded and the sidebar position is updated; + - Prevents new sidebar locations from being created every time the reader page is rendered if these locations already exist; + - Centers the styles loaded event to avoid having multiple locations setting the opacity style in Shadow Dom causing the screen to flash multiple times. + +- 9b94ade898: Use entity title in `TechDocsSearch` placeholder if available. + +- 816f7475ec: Convert `sanitizeDOM` transformer to hook as part of code readability improvements in dom file. + +- 50ff56a80f: Change the `EntityDocsPage` path to be more specific and also add integration tests for `sub-routes` on this page. + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1 + - @backstage/plugin-search-common@0.3.5 + - @backstage/plugin-search-react@0.2.1 + - @backstage/core-components@0.9.5 + - @backstage/integration@1.2.1 + - @backstage/core-plugin-api@1.0.3 + - @backstage/integration-react@1.1.1 + - @backstage/catalog-model@1.0.3 + - @backstage/plugin-techdocs-react@1.0.1 + +## 1.1.2-next.1 + +### Patch Changes + +- 8f7b1835df: Updated dependency `msw` to `^0.41.0`. +- bff65e6958: Updated sidebar-related logic to use `` + `useSidebarPinState()` and/or `` + `useSidebarOpenState()` from `@backstage/core-components`. +- Updated dependencies + - @backstage/core-components@0.9.5-next.1 + - @backstage/core-plugin-api@1.0.3-next.0 + - @backstage/integration-react@1.1.1-next.1 + - @backstage/integration@1.2.1-next.1 + - @backstage/catalog-model@1.0.3-next.0 + - @backstage/plugin-catalog-react@1.1.1-next.1 + - @backstage/plugin-search-react@0.2.1-next.0 + - @backstage/plugin-techdocs-react@1.0.1-next.1 + - @backstage/plugin-search-common@0.3.5-next.0 + +## 1.1.2-next.0 + +### Patch Changes + +- 881fbd7e8d: Fix `EntityTechdocsContent` component to use objects instead of `` elements, otherwise "outlet" will be null on sub-pages and add-ons won't render. + +- 17c059dfd0: Restructures reader style transformations to improve code readability: + + - Extracts the style rules to separate files; + - Creates a hook that processes each rule; + - And creates another hook that returns a transformer responsible for injecting them into the head tag of a given element. + +- 3b45ad701f: Packages a set of tweaks to the TechDocs addons rendering process: + + - Prevents displaying sidebars until page styles are loaded and the sidebar position is updated; + - Prevents new sidebar locations from being created every time the reader page is rendered if these locations already exist; + - Centers the styles loaded event to avoid having multiple locations setting the opacity style in Shadow Dom causing the screen to flash multiple times. + +- 816f7475ec: Convert `sanitizeDOM` transformer to hook as part of code readability improvements in dom file. + +- 50ff56a80f: Change the `EntityDocsPage` path to be more specific and also add integration tests for `sub-routes` on this page. + +- Updated dependencies + - @backstage/plugin-catalog-react@1.1.1-next.0 + - @backstage/core-components@0.9.5-next.0 + - @backstage/integration@1.2.1-next.0 + - @backstage/plugin-techdocs-react@1.0.1-next.0 + - @backstage/integration-react@1.1.1-next.0 + +## 1.1.1 + +### Patch Changes + +- 52419be116: Create a menu in the sub header of documentation pages, it is responsible for rendering TechDocs addons that allow users to customize their reading experience. +- a307a14be0: Removed dependency on `@backstage/core-app-api`. +- bed0d64ce9: Fixed bugs that prevented a 404 error from being shown when it should have been. +- 2efee91251: Add a `sub-route` path on the EntityDocs page to fix the blank screen error when navigating using sidebar links. +- 2dcb2c9678: Loading SVGs correctly with `bota` with extended characters +- 52fddad92d: The `TechDocsStorageApi` and its associated ref are now exported by `@backstage/plugin-techdocs-react`. The API interface, ref, and types are now deprecated in `@backstage/plugin-techdocs` and will be removed in a future release. +- 0ad901569f: Hidden exports related to experimental TechDocs reader functionality have been removed and can no longer be imported. In the unlikely event you were using these exports, you can now take advantage of the officially supported and generally available TechDocs Addon framework instead. +- 3a74e203a8: Updated search result components to support rendering content with highlighted matched terms +- Updated dependencies + - @backstage/core-components@0.9.4 + - @backstage/integration@1.2.0 + - @backstage/core-plugin-api@1.0.2 + - @backstage/plugin-catalog-react@1.1.0 + - @backstage/integration-react@1.1.0 + - @backstage/plugin-techdocs-react@1.0.0 + - @backstage/config@1.0.1 + - @backstage/plugin-search-react@0.2.0 + - @backstage/plugin-search-common@0.3.4 + - @backstage/catalog-model@1.0.2 + +## 1.1.1-next.3 + +### Patch Changes + +- cc8ddd0979: revert dependency `event-source-polyfill` to `1.0.25` +- Updated dependencies + - @backstage/core-components@0.9.4-next.2 + +## 1.1.1-next.2 + +### Patch Changes + +- 52419be116: Create a menu in the sub header of documentation pages, it is responsible for rendering TechDocs addons that allow users to customize their reading experience. +- 1af133f779: Updated dependency `event-source-polyfill` to `1.0.26`. +- 2dcb2c9678: Loading SVGs correctly with `bota` with extended characters +- 3a74e203a8: Updated search result components to support rendering content with highlighted matched terms +- Updated dependencies + - @backstage/core-components@0.9.4-next.1 + - @backstage/plugin-techdocs-react@0.1.1-next.2 + - @backstage/config@1.0.1-next.0 + - @backstage/plugin-search-react@0.2.0-next.2 + - @backstage/plugin-search-common@0.3.4-next.0 + - @backstage/plugin-catalog-react@1.1.0-next.2 + - @backstage/catalog-model@1.0.2-next.0 + - @backstage/core-plugin-api@1.0.2-next.1 + - @backstage/integration@1.2.0-next.1 + - @backstage/integration-react@1.1.0-next.2 + +## 1.1.1-next.1 + +### Patch Changes + +- 52fddad92d: The `TechDocsStorageApi` and its associated ref are now exported by `@backstage/plugin-techdocs-react`. The API interface, ref, and types are now deprecated in `@backstage/plugin-techdocs` and will be removed in a future release. +- Updated dependencies + - @backstage/core-components@0.9.4-next.0 + - @backstage/core-plugin-api@1.0.2-next.0 + - @backstage/plugin-catalog-react@1.1.0-next.1 + - @backstage/plugin-search-react@0.2.0-next.1 + - @backstage/plugin-techdocs-react@0.1.1-next.1 + - @backstage/integration-react@1.1.0-next.1 + +## 1.1.1-next.0 + +### Patch Changes + +- a307a14be0: Removed dependency on `@backstage/core-app-api`. +- bed0d64ce9: Fixed bugs that prevented a 404 error from being shown when it should have been. +- Updated dependencies + - @backstage/integration@1.2.0-next.0 + - @backstage/plugin-catalog-react@1.1.0-next.0 + - @backstage/integration-react@1.1.0-next.0 + - @backstage/plugin-search-react@0.1.1-next.0 + - @backstage/plugin-techdocs-react@0.1.1-next.0 + +## 1.1.0 + +### Minor Changes + +- ace749b785: TechDocs supports a new, experimental method of customization: addons! + + To customize the standalone TechDocs reader page experience, update your `/packages/app/src/App.tsx` in the following way: + + ```diff + import { TechDocsIndexPage, TechDocsReaderPage } from '@backstage/plugin-techdocs'; + + import { TechDocsAddons } from '@backstage/plugin-techdocs-react'; + + import { SomeAddon } from '@backstage/plugin-some-plugin'; + + // ... + + } /> + } + > + + + + + + + + + // ... + ``` + + To customize the TechDocs reader experience on the Catalog entity page, update your `packages/app/src/components/catalog/EntityPage.tsx` in the following way: + + ```diff + import { EntityTechdocsContent } from '@backstage/plugin-techdocs'; + + import { TechDocsAddons } from '@backstage/plugin-techdocs-react'; + + import { SomeAddon } from '@backstage/plugin-some-plugin'; + + // ... + + + + {overviewContent} + + + + - + + + + + + + + + + + + + + // ... + ``` + + If you do not wish to customize your TechDocs reader experience in this way at this time, no changes are necessary! + +### Patch Changes + +- ab230a433f: imports from `@backstage/plugin-search-react` instead of `@backstage/plugin-search` + +- 7c7919777e: build(deps-dev): bump `@testing-library/react-hooks` from 7.0.2 to 8.0.0 + +- 24254fd433: build(deps): bump `@testing-library/user-event` from 13.5.0 to 14.0.0 + +- 230ad0826f: Bump to using `@types/node` v16 + +- f0fb9153b7: Fix broken query selectors on techdocs + +- 9975ff9852: Applied the fix from version 1.0.1 of this package, which is part of the v1.0.2 release of Backstage. + +- 3ba256c389: Fixed a bug preventing custom TechDocs reader page implementations from rendering without being double-wrapped in the `` component. + +- fe53fe97d7: Fix permalink scrolling for anchors where the id starts with a number. + +- 0152c0de22: Some documentation layout tweaks: + + - drawer toggle margins + - code block margins + - sidebar drawer width + - inner content width + - footer link width + - sidebar table of contents scroll + +- 3ba256c389: Fixed a bug that caused addons in the `Subheader` location to break the default TechDocs reader page layout. + +- Updated dependencies + - @backstage/integration@1.1.0 + - @backstage/plugin-catalog-react@1.0.1 + - @backstage/catalog-model@1.0.1 + - @backstage/core-app-api@1.0.1 + - @backstage/core-components@0.9.3 + - @backstage/core-plugin-api@1.0.1 + - @backstage/plugin-search-react@0.1.0 + - @backstage/plugin-techdocs-react@0.1.0 + - @backstage/integration-react@1.0.1 + +## 1.1.0-next.3 + +### Minor Changes + +- ace749b785: TechDocs supports a new, experimental method of customization: addons! + + To customize the standalone TechDocs reader page experience, update your `/packages/app/src/App.tsx` in the following way: + + ```diff + import { TechDocsIndexPage, TechDocsReaderPage } from '@backstage/plugin-techdocs'; + + import { TechDocsAddons } from '@backstage/plugin-techdocs-react'; + + import { SomeAddon } from '@backstage/plugin-some-plugin'; + + // ... + + } /> + } + > + + + + + + + + + // ... + ``` + + To customize the TechDocs reader experience on the Catalog entity page, update your `packages/app/src/components/catalog/EntityPage.tsx` in the following way: + + ```diff + import { EntityTechdocsContent } from '@backstage/plugin-techdocs'; + + import { TechDocsAddons } from '@backstage/plugin-techdocs-react'; + + import { SomeAddon } from '@backstage/plugin-some-plugin'; + + // ... + + + + {overviewContent} + + + + - + + + + + + + + + + + + + + // ... + ``` + + If you do not wish to customize your TechDocs reader experience in this way at this time, no changes are necessary! + +### Patch Changes + +- ab230a433f: imports from `@backstage/plugin-search-react` instead of `@backstage/plugin-search` +- 24254fd433: build(deps): bump `@testing-library/user-event` from 13.5.0 to 14.0.0 +- 230ad0826f: Bump to using `@types/node` v16 +- Updated dependencies + - @backstage/core-app-api@1.0.1-next.1 + - @backstage/core-components@0.9.3-next.2 + - @backstage/core-plugin-api@1.0.1-next.0 + - @backstage/integration-react@1.0.1-next.2 + - @backstage/plugin-catalog-react@1.0.1-next.3 + - @backstage/plugin-search-react@0.1.0-next.0 + - @backstage/integration@1.1.0-next.2 + - @backstage/plugin-techdocs-react@0.1.0-next.0 + +## 1.0.1-next.2 + +### Patch Changes + +- f0fb9153b7: Fix broken query selectors on techdocs +- 9975ff9852: Applied the fix from version 1.0.1 of this package, which is part of the v1.0.2 release of Backstage. +- Updated dependencies + - @backstage/core-components@0.9.3-next.1 + - @backstage/plugin-catalog-react@1.0.1-next.2 + - @backstage/catalog-model@1.0.1-next.1 + +## 1.0.1 + +### Patch Changes + +- Pin the `event-source-polyfill` dependency to version 1.0.25 + +## 1.0.1-next.1 + +### Patch Changes + +- 0152c0de22: Some documentation layout tweaks: + + - drawer toggle margins + - code block margins + - sidebar drawer width + - inner content width + - footer link width + - sidebar table of contents scroll + +- Updated dependencies + - @backstage/integration@1.1.0-next.1 + - @backstage/plugin-catalog-react@1.0.1-next.1 + - @backstage/integration-react@1.0.1-next.1 + +## 1.0.1-next.0 + +### Patch Changes + +- fe53fe97d7: Fix permalink scrolling for anchors where the id starts with a number. +- Updated dependencies + - @backstage/catalog-model@1.0.1-next.0 + - @backstage/plugin-search@0.7.5-next.0 + - @backstage/integration@1.0.1-next.0 + - @backstage/plugin-catalog-react@1.0.1-next.0 + - @backstage/core-components@0.9.3-next.0 + - @backstage/integration-react@1.0.1-next.0 + +## 1.0.0 + +### Major Changes + +- b58c70c223: This package has been promoted to v1.0! To understand how this change affects the package, please check out our [versioning policy](https://backstage.io/docs/overview/versioning-policy). + +### Minor Changes + +- 700d93ff41: Removed deprecated exports, including: + + - deprecated `DocsResultListItem` is now deleted and fully replaced with `TechDocsSearchResultListItem` + - deprecated `TechDocsPage` is now deleted and fully replaced with `TechDocsReaderPage` + - deprecated `TechDocsPageHeader` is now deleted and fully replaced with `TechDocsReaderPageHeader` + - deprecated `TechDocsPageHeaderProps` is now deleted and fully replaced with `TechDocsReaderPageHeaderProps` + - deprecated `TechDocsPageRenderFunction` is now deleted and fully replaced with `TechDocsReaderPageRenderFunction` + - deprecated config `techdocs.requestUrl` is now deleted and fully replaced with the discoveryApi + +### Patch Changes + +- a422d7ce5e: chore(deps): bump `@testing-library/react` from 11.2.6 to 12.1.3 +- c689d7a94c: Switched to using `CatalogFilterLayout` from `@backstage/plugin-catalog-react`. +- f24ef7864e: Minor typo fixes +- 06af9e8d17: Long sidebars will no longer overflow the footer and will properly show a scrollbar when needed. +- Updated dependencies + - @backstage/core-components@0.9.2 + - @backstage/core-plugin-api@1.0.0 + - @backstage/integration-react@1.0.0 + - @backstage/plugin-catalog-react@1.0.0 + - @backstage/plugin-search@0.7.4 + - @backstage/catalog-model@1.0.0 + - @backstage/integration@1.0.0 + - @backstage/config@1.0.0 + - @backstage/errors@1.0.0 + +## 0.15.1 + +### Patch Changes + +- 7a1dbe6ce9: The panels of `TechDocsCustomHome` now use the `useEntityOwnership` hook to resolve ownership when the `'ownedByUser'` filter predicate is used. +- Updated dependencies + - @backstage/plugin-catalog@0.10.0 + - @backstage/plugin-catalog-react@0.9.0 + - @backstage/core-components@0.9.1 + - @backstage/catalog-model@0.13.0 + - @backstage/plugin-search@0.7.3 + - @backstage/integration-react@0.1.25 + +## 0.15.1-next.0 + +### Patch Changes + +- 7a1dbe6ce9: The panels of `TechDocsCustomHome` now use the `useEntityOwnership` hook to resolve ownership when the `'ownedByUser'` filter predicate is used. +- Updated dependencies + - @backstage/plugin-catalog@0.10.0-next.0 + - @backstage/plugin-catalog-react@0.9.0-next.0 + - @backstage/core-components@0.9.1-next.0 + - @backstage/catalog-model@0.13.0-next.0 + - @backstage/plugin-search@0.7.3-next.0 + - @backstage/integration-react@0.1.25-next.0 + +## 0.15.0 + +### Minor Changes + +- ee3d6c6f10: **BREAKING:** + Table column utilities `createNameColumn`, `createOwnerColumn`, `createTypeColumn` as well as actions utilities `createCopyDocsUrlAction` and `createStarEntityAction` are no longer directly exported. Instead accessible through DocsTable and EntityListDocsTable. + + Use as following: + + ```tsx + DocsTable.columns.createNameColumn(); + DocsTable.columns.createOwnerColumn(); + DocsTable.columns.createTypeColumn(); + + DocsTable.actions.createCopyDocsUrlAction(); + DocsTable.actions.createStarEntityAction(); + ``` + + - Renamed `DocsResultListItem` to `TechDocsSearchResultListItem`, leaving the old name in place as a deprecations. + + - Renamed `TechDocsPage` to `TechDocsReaderPage`, leaving the old name in place as a deprecations. + + - Renamed `TechDocsPageRenderFunction` to `TechDocsPageRenderFunction`, leaving the old name in place as a deprecations. + + - Renamed `TechDocsPageHeader` to `TechDocsReaderPageHeader`, leaving the old name in place as a deprecations. + + - `LegacyTechDocsHome` marked as deprecated and will be deleted in next release, use `TechDocsCustomHome` instead. + + - `LegacyTechDocsPage` marked as deprecated and will be deleted in next release, use `TechDocsReaderPage` instead. + +### Patch Changes + +- 64b430f80d: chore(deps): bump `react-text-truncate` from 0.17.0 to 0.18.0 +- 899f196af5: Use `getEntityByRef` instead of `getEntityByName` in the catalog client +- f41a293231: - **DEPRECATION**: Deprecated `formatEntityRefTitle` in favor of the new `humanizeEntityRef` method instead. Please migrate to using the new method instead. +- c5fda066b1: Collapse techdocs sidebar on small devices +- f590d1681b: Removed usage of deprecated favorite utility methods. +- 5b0f9a75fa: Remove copyright from old footer in documentation generated with previous version of `mkdocs-techdocs-plugin` (`v0.2.2`). +- 0c3ba547a6: Show feedback when copying code snippet to clipboard. +- 0ca964ee0e: Fixed a bug that could cause searches in the in-context TechDocs search bar to show results from a different TechDocs site. +- 36aa63022b: Use `CompoundEntityRef` instead of `EntityName`, and `getCompoundEntityRef` instead of `getEntityName`, from `@backstage/catalog-model`. +- Updated dependencies + - @backstage/catalog-model@0.12.0 + - @backstage/core-components@0.9.0 + - @backstage/plugin-search@0.7.2 + - @backstage/plugin-catalog@0.9.1 + - @backstage/plugin-catalog-react@0.8.0 + - @backstage/integration@0.8.0 + - @backstage/core-plugin-api@0.8.0 + - @backstage/integration-react@0.1.24 + +## 0.14.0 + +### Minor Changes + +- 2262fe19c9: **BREAKING**: Removed support for passing in an explicit `entity` prop to entity page extensions, which has been deprecated for a long time. This is only a breaking change at the TypeScript level, as this property was already ignored. +- 4faae902eb: Adjust the Tech Docs page theme as a side effect of the `mkdocs-material` theme update. + + If you use the `spofify/techdocs` image to build your documentation, make sure you use version `spotify/techdocs:v0.3.7`. + + **Breaking**: The `PyMdown` extensions have also been updated and some syntax may have changed, so it is recommended that you check the extension's documentation if something stops working. + For example, the syntax of tags below was deprecated in `PyMdown` extensions `v.7.0` and in `v.8.0.0` it has been removed. This means that the old syntax specified below no longer works. + + ````markdown + ```markdown tab="tab" + This is some markdown + ``` + + ```markdown tab="tab 2" + This is some markdown in tab 2 + ``` + ```` + +### Patch Changes + +- 3bbb4d98c6: Changed to use from createApp +- ed09ad8093: Updated usage of the `LocationSpec` type from `@backstage/catalog-model`, which is deprecated. +- b776ce5aab: Replaced use of deprecated `useEntityListProvider` hook with `useEntityList`. +- d4f67fa728: Removed import of deprecated hook. +- 45e1706328: Continuation of [#9569](https://github.com/backstage/backstage/pull/9569), fix Tech Docs Reader search position to be the same width as content. +- 919cf2f836: Minor updates to match the new `targetRef` field of relations, and to stop consuming the `target` field +- Updated dependencies + - @backstage/plugin-catalog@0.9.0 + - @backstage/core-components@0.8.10 + - @backstage/plugin-catalog-react@0.7.0 + - @backstage/catalog-model@0.11.0 + - @backstage/core-plugin-api@0.7.0 + - @backstage/integration@0.7.5 + - @backstage/plugin-search@0.7.1 + - @backstage/integration-react@0.1.23 + +## 0.13.4 + +### Patch Changes + +- 1ed305728b: Bump `node-fetch` to version 2.6.7 and `cross-fetch` to version 3.1.5 +- c77c5c7eb6: Added `backstage.role` to `package.json` +- 6553985cd4: Match text size of admonitions to main content text size. +- 9df7b43e1a: Improve overall appearance of highlighted code in docs. +- Updated dependencies + - @backstage/core-components@0.8.9 + - @backstage/core-plugin-api@0.6.1 + - @backstage/errors@0.2.1 + - @backstage/integration@0.7.3 + - @backstage/integration-react@0.1.22 + - @backstage/plugin-catalog@0.8.0 + - @backstage/plugin-catalog-react@0.6.15 + - @backstage/plugin-search@0.7.0 + - @backstage/catalog-model@0.10.0 + - @backstage/config@0.1.14 + - @backstage/theme@0.2.15 + +## 0.13.3 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.8.8 + - @backstage/plugin-search@0.6.2 + - @backstage/plugin-catalog-react@0.6.14 + - @backstage/plugin-catalog@0.7.12 + - @backstage/integration-react@0.1.21 + +## 0.13.3-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.8.8-next.0 + - @backstage/plugin-search@0.6.2-next.0 + - @backstage/plugin-catalog-react@0.6.14-next.0 + - @backstage/integration-react@0.1.21-next.0 + - @backstage/plugin-catalog@0.7.12-next.0 + +## 0.13.2 + +### Patch Changes + +- 742434a6ba: Fixed a bug where links to files within a TechDocs site that use the `download` attribute would result in a 404 in cases where the TechDocs backend and Backstage frontend application are on the same host. +- 359c31e31d: Added support for documentation using the raw `` tag to point to relative resources like audio or video files. +- 18317a08db: Fixed a bug where copy-to-clipboard buttons were appended to unintended elements. +- Updated dependencies + - @backstage/core-components@0.8.7 + - @backstage/plugin-catalog-react@0.6.13 + - @backstage/integration-react@0.1.20 + - @backstage/plugin-catalog@0.7.11 + - @backstage/plugin-search@0.6.1 + +## 0.13.2-next.1 + +### Patch Changes + +- 742434a6ba: Fixed a bug where links to files within a TechDocs site that use the `download` attribute would result in a 404 in cases where the TechDocs backend and Backstage frontend application are on the same host. +- Updated dependencies + - @backstage/core-components@0.8.7-next.1 + - @backstage/plugin-catalog-react@0.6.13-next.1 + - @backstage/plugin-catalog@0.7.11-next.1 + +## 0.13.2-next.0 + +### Patch Changes + +- 359c31e31d: Added support for documentation using the raw `` tag to point to relative resources like audio or video files. +- Updated dependencies + - @backstage/core-components@0.8.7-next.0 + - @backstage/integration-react@0.1.20-next.0 + - @backstage/plugin-catalog@0.7.11-next.0 + - @backstage/plugin-catalog-react@0.6.13-next.0 + - @backstage/plugin-search@0.6.1-next.0 + +## 0.13.1 + +### Patch Changes + +- bdc53553eb: chore(deps): bump `react-text-truncate` from 0.16.0 to 0.17.0 +- a64f99f734: Code snippets now include a "copy to clipboard" button. +- Updated dependencies + - @backstage/core-components@0.8.6 + - @backstage/plugin-search@0.6.0 + - @backstage/plugin-catalog@0.7.10 + +## 0.13.0 + +### Minor Changes + +- aecfe4f403: Make `TechDocsClient` and `TechDocsStorageClient` use the `FetchApi`. You now + need to pass in an instance of that API when constructing the client, if you + create a custom instance in your app. + + If you are replacing the factory: + + ```diff + +import { fetchApiRef } from '@backstage/core-plugin-api'; + + createApiFactory({ + api: techdocsStorageApiRef, + deps: { + configApi: configApiRef, + discoveryApi: discoveryApiRef, + identityApi: identityApiRef, + + fetchApi: fetchApiRef, + }, + factory: ({ + configApi, + discoveryApi, + identityApi, + + fetchApi, + }) => + new TechDocsStorageClient({ + configApi, + discoveryApi, + identityApi, + + fetchApi, + }), + }), + createApiFactory({ + api: techdocsApiRef, + deps: { + configApi: configApiRef, + discoveryApi: discoveryApiRef, + - identityApi: identityApiRef, + + fetchApi: fetchApiRef, + }, + factory: ({ + configApi, + discoveryApi, + - identityApi, + + fetchApi, + }) => + new TechDocsClient({ + configApi, + discoveryApi, + - identityApi, + + fetchApi, + }), + }), + ``` + + If instantiating directly: + + ```diff + +import { fetchApiRef } from '@backstage/core-plugin-api'; + + +const fetchApi = useApi(fetchApiRef); + const storageClient = new TechDocsStorageClient({ + configApi, + discoveryApi, + identityApi, + + fetchApi, + }); + const techdocsClient = new TechDocsClient({ + configApi, + discoveryApi, + - identityApi, + + fetchApi, + }), + ``` + +### Patch Changes + +- 51fbedc445: Migrated usage of deprecated `IdentityApi` methods. +- 29710c91c2: use lighter color for block quotes and horizontal rulers +- Updated dependencies + - @backstage/core-components@0.8.5 + - @backstage/integration@0.7.2 + - @backstage/plugin-search@0.5.6 + - @backstage/core-plugin-api@0.6.0 + - @backstage/plugin-catalog@0.7.9 + - @backstage/plugin-catalog-react@0.6.12 + - @backstage/config@0.1.13 + - @backstage/catalog-model@0.9.10 + - @backstage/integration-react@0.1.19 + +## 0.12.15-next.0 + +### Patch Changes + +- 51fbedc445: Migrated usage of deprecated `IdentityApi` methods. +- 29710c91c2: use lighter color for block quotes and horizontal rulers +- Updated dependencies + - @backstage/core-components@0.8.5-next.0 + - @backstage/core-plugin-api@0.6.0-next.0 + - @backstage/plugin-catalog@0.7.9-next.0 + - @backstage/config@0.1.13-next.0 + - @backstage/plugin-catalog-react@0.6.12-next.0 + - @backstage/plugin-search@0.5.6-next.0 + - @backstage/catalog-model@0.9.10-next.0 + - @backstage/integration-react@0.1.19-next.0 + - @backstage/integration@0.7.2-next.0 + +## 0.12.14 + +### Patch Changes + +- 5333451def: Cleaned up API exports +- 1628ca3f49: Fix an issue where the TechDocs sidebar is hidden when the Backstage sidebar is pinned at smaller screen sizes +- Updated dependencies + - @backstage/config@0.1.12 + - @backstage/integration@0.7.1 + - @backstage/core-components@0.8.4 + - @backstage/core-plugin-api@0.5.0 + - @backstage/plugin-catalog-react@0.6.11 + - @backstage/errors@0.2.0 + - @backstage/catalog-model@0.9.9 + - @backstage/integration-react@0.1.18 + - @backstage/plugin-catalog@0.7.8 + - @backstage/plugin-search@0.5.5 + +## 0.12.13 + +### Patch Changes + +- fe9de6c25b: Adds support for opening internal Techdocs links in a new tab with CTRL+Click or CMD+Click +- 4ce51ab0f1: Internal refactor of the `react-use` imports to use `react-use/lib/*` instead. +- e0271456d8: Updated Techdocs footer navigation to dynamically resize to the width of the dom, resolving an issue where a pinned sidebar causes navigation to go off of the screen +- Updated dependencies + - @backstage/plugin-search@0.5.4 + - @backstage/core-plugin-api@0.4.1 + - @backstage/plugin-catalog-react@0.6.10 + - @backstage/core-components@0.8.3 + - @backstage/plugin-catalog@0.7.7 + +## 0.12.12 + +### Patch Changes + +- aa8f764a3e: Add the techdocs.sanitizer.allowedIframeHosts config. + This config allows all iframes which have the host of the attribute src in the 'allowedIframehosts' list to be displayed in the documentation. +- Updated dependencies + - @backstage/plugin-search@0.5.3 + - @backstage/plugin-catalog@0.7.6 + - @backstage/plugin-catalog-react@0.6.9 + - @backstage/integration@0.7.0 + - @backstage/integration-react@0.1.17 + +## 0.12.11 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search@0.5.2 + - @backstage/core-plugin-api@0.4.0 + - @backstage/plugin-catalog-react@0.6.8 + - @backstage/core-components@0.8.2 + - @backstage/plugin-catalog@0.7.5 + - @backstage/integration-react@0.1.16 + +## 0.12.10 + +### Patch Changes + +- e7cce2b603: Fix issue where assets weren't being fetched from the correct URL path for doc URLs without trailing slashes +- Updated dependencies + - @backstage/core-plugin-api@0.3.1 + - @backstage/core-components@0.8.1 + - @backstage/catalog-model@0.9.8 + - @backstage/plugin-catalog-react@0.6.7 + +## 0.12.9 + +### Patch Changes + +- cd450844f6: Moved React dependencies to `peerDependencies` and allow both React v16 and v17 to be used. +- d90dad84b0: Switch to using `LogViewer` component from `@backstage/core-components` to display build logs. +- 3421826ca8: The problem of lowercase entity triplets which causes docs to not load on entity page is fixed. +- Updated dependencies + - @backstage/core-components@0.8.0 + - @backstage/core-plugin-api@0.3.0 + - @backstage/plugin-catalog@0.7.4 + - @backstage/integration-react@0.1.15 + - @backstage/plugin-catalog-react@0.6.5 + - @backstage/plugin-search@0.5.1 + +## 0.12.8 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@0.6.10 + - @backstage/core-components@0.7.6 + - @backstage/theme@0.2.14 + - @backstage/core-plugin-api@0.2.2 + - @backstage/plugin-search@0.5.0 + +## 0.12.7 + +### Patch Changes + +- bab752e2b3: Change default port of backend from 7000 to 7007. + + This is due to the AirPlay Receiver process occupying port 7000 and preventing local Backstage instances on MacOS to start. + + You can change the port back to 7000 or any other value by providing an `app-config.yaml` with the following values: + + backend: + listen: 0.0.0.0:7123 + baseUrl: http://localhost:7123 + + More information can be found here: + +- Updated dependencies + - @backstage/errors@0.1.5 + - @backstage/core-plugin-api@0.2.1 + - @backstage/core-components@0.7.5 + +## 0.12.6 + +### Patch Changes + +- a125278b81: Refactor out the deprecated path and icon from RouteRefs +- c1858c4cf9: Fixed entity triplet case handling for certain locales. +- f7703981a9: Use a better checkbox rendering in a task list. +- e266687580: Updates reader component used to display techdocs documentation. A previous change made this component not usable out of a page which don't have entityRef in url parameters. Reader component EntityRef parameter is now used instead of url parameters. Techdocs documentation component can now be used in our custom pages. +- Updated dependencies + - @backstage/plugin-catalog@0.7.3 + - @backstage/catalog-model@0.9.7 + - @backstage/plugin-catalog-react@0.6.4 + - @backstage/plugin-search@0.4.18 + - @backstage/core-components@0.7.4 + - @backstage/core-plugin-api@0.2.0 + - @backstage/integration-react@0.1.14 + +## 0.12.5 + +### Patch Changes + +- fe5738fe1c: Lazy load `LazyLog` as it is rarely used. +- 53c9ad7e04: Update font weight for headings in TechDocs +- Updated dependencies + - @backstage/core-components@0.7.3 + - @backstage/theme@0.2.13 + - @backstage/plugin-search@0.4.17 + - @backstage/core-plugin-api@0.1.13 + - @backstage/plugin-catalog-react@0.6.3 + +## 0.12.4 + +### Patch Changes + +- a9a8c6f7c5: Reader will now scroll to the top of the page when navigating between pages +- 106a5dc3ad: Restore original casing for `kind`, `namespace` and `name` in `DefaultTechDocsCollator`. +- Updated dependencies + - @backstage/config@0.1.11 + - @backstage/theme@0.2.12 + - @backstage/errors@0.1.4 + - @backstage/integration@0.6.9 + - @backstage/core-components@0.7.2 + - @backstage/integration-react@0.1.13 + - @backstage/plugin-catalog-react@0.6.2 + - @backstage/catalog-model@0.9.6 + - @backstage/plugin-search@0.4.16 + - @backstage/core-plugin-api@0.1.12 + +## 0.12.3 + +### Patch Changes + +- ba5b75ed2f: Add `` as an alternative to `` that + shows a grid of card instead of table. + + Extend `` to display the entity title of the entity instead of the + name if available. + +- 177401b571: Display entity title (if defined) in titles of TechDocs search results + +- cdf8ca6111: Only replace the shadow dom if the content is changed to avoid a flickering UI. + +- Updated dependencies + - @backstage/core-components@0.7.1 + - @backstage/errors@0.1.3 + - @backstage/core-plugin-api@0.1.11 + - @backstage/plugin-catalog@0.7.2 + - @backstage/plugin-catalog-react@0.6.1 + - @backstage/catalog-model@0.9.5 + +## 0.12.2 + +### Patch Changes + +- 76fef740fe: Refactored `` component internals to support future extensibility. +- Updated dependencies + - @backstage/plugin-catalog-react@0.6.0 + - @backstage/plugin-catalog@0.7.1 + - @backstage/integration@0.6.8 + - @backstage/core-components@0.7.0 + - @backstage/theme@0.2.11 + - @backstage/plugin-search@0.4.15 + - @backstage/integration-react@0.1.12 + +## 0.12.1 + +### Patch Changes + +- 81a41ec249: Added a `name` key to all extensions in order to improve Analytics API metadata. +- Updated dependencies + - @backstage/core-components@0.6.1 + - @backstage/core-plugin-api@0.1.10 + - @backstage/plugin-catalog@0.7.0 + - @backstage/plugin-catalog-react@0.5.2 + - @backstage/catalog-model@0.9.4 + - @backstage/integration@0.6.7 + - @backstage/plugin-search@0.4.14 + +## 0.12.0 + +### Minor Changes + +- 82bb0842a3: Adds support for being able to customize and compose your TechDocs reader page in the App. + + You can likely upgrade to this version without issue. If, however, you have + imported the `` component in your custom code, the name of a property + has changed. You will need to make the following change anywhere you use it: + + ```diff + - + + + ``` + +### Patch Changes + +- 79ebee7a6b: Add "data-testid" for e2e tests and fix techdocs entity not found error. +- 3df2e8532b: Fixed the URL for the "Click to copy documentation link to clipboard" action +- 0a8bec0877: Added a check for the TechDocs annotation on the entity +- Updated dependencies + - @backstage/integration@0.6.6 + - @backstage/core-plugin-api@0.1.9 + - @backstage/core-components@0.6.0 + - @backstage/integration-react@0.1.11 + - @backstage/plugin-catalog@0.6.17 + - @backstage/plugin-catalog-react@0.5.1 + - @backstage/plugin-search@0.4.13 + +## 0.11.3 + +### Patch Changes + +- be13dfe61a: Make techdocs context search bar width adjust on smaller screens. +- Updated dependencies + - @backstage/core-components@0.5.0 + - @backstage/integration@0.6.5 + - @backstage/plugin-catalog@0.6.16 + - @backstage/plugin-catalog-react@0.5.0 + - @backstage/catalog-model@0.9.3 + - @backstage/config@0.1.10 + - @backstage/integration-react@0.1.10 + - @backstage/plugin-search@0.4.12 + +## 0.11.2 + +### Patch Changes + +- 1d346ba903: Modify TechDocsCollator to be aware of new TechDocs URL pattern. Modify tech docs in context search to use correct casing when creating initial filter. +- 9f1362dcc1: Upgrade `@material-ui/lab` to `4.0.0-alpha.57`. +- 96fef17a18: Upgrade git-parse-url to v11.6.0 +- Updated dependencies + - @backstage/core-components@0.4.2 + - @backstage/integration@0.6.4 + - @backstage/integration-react@0.1.9 + - @backstage/plugin-catalog@0.6.15 + - @backstage/plugin-catalog-react@0.4.6 + - @backstage/plugin-search@0.4.11 + - @backstage/core-plugin-api@0.1.8 + +## 0.11.1 + +### Patch Changes + +- 30ed662a3: Adding in-context search to TechDocs Reader component. Using existing search-backend to query for indexed search results scoped into a specific entity's techdocs. Needs TechDocsCollator enabled on the backend to work. + + Adding extra information to indexed tech docs documents for search. + +- 434dfc5d4: Display [metadata.title](https://backstage.io/docs/features/software-catalog/descriptor-format#title-optional) for components on the TechDocs homepage, if defined; otherwise fall back to `metadata.name` as displayed before. + +- Updated dependencies + - @backstage/plugin-catalog-react@0.4.5 + - @backstage/integration@0.6.3 + - @backstage/core-components@0.4.0 + - @backstage/plugin-catalog@0.6.14 + - @backstage/plugin-search@0.4.9 + - @backstage/catalog-model@0.9.1 + - @backstage/integration-react@0.1.8 + +## 0.11.0 + +### Minor Changes + +- c772d9a84: TechDocs sites can now be accessed using paths containing entity triplets of + any case (e.g. `/docs/namespace/KIND/name` or `/docs/namespace/kind/name`). + + If you do not use an external storage provider for serving TechDocs, this is a + transparent change and no action is required from you. + + If you _do_ use an external storage provider for serving TechDocs (one of\* GCS, + AWS S3, or Azure Blob Storage), you must run a migration command against your + storage provider before updating. + + [A migration guide is available here](https://backstage.io/docs/features/techdocs/how-to-guides#how-to-migrate-from-techdocs-alpha-to-beta). + + - (\*) We're seeking help from the community to bring OpenStack Swift support + [to feature parity](https://github.com/backstage/backstage/issues/6763) with the above. + +- 787bc0826: The TechDocs plugin has completed the migration to the Composability API. In + order to update to this version, please ensure you've made all necessary + changes to your `App.tsx` file as outlined in the [create-app changelog][cacl]. + + [cacl]: https://github.com/backstage/backstage/blob/master/packages/create-app/CHANGELOG.md + +### Patch Changes + +- 90c68a2ca: Fix Techdocs feedback icon link for GitHub URLs +- Updated dependencies + - @backstage/plugin-catalog@0.6.13 + - @backstage/plugin-catalog-react@0.4.4 + - @backstage/core-components@0.3.3 + - @backstage/integration@0.6.2 + - @backstage/config@0.1.8 + +## 0.10.4 + +### Patch Changes + +- a440d3b38: Expose a new composable `TechDocsIndexPage` and a `DefaultTechDocsHome` with support for starring docs and filtering on owned, starred, owner, and tags. + + You can migrate to the new UI view by making the following changes in your `App.tsx`: + + ```diff + - } /> + + }> + + + + + + } + + /> + ``` + +- 56c773909: Switched `@types/react` dependency to request `*` rather than a specific version. + +- 8a3e46591: Switch `EventSource` implementation with header support from a Node.js API-based one to an XHR-based one. + +- Updated dependencies + - @backstage/integration@0.6.0 + - @backstage/core-components@0.3.1 + - @backstage/core-plugin-api@0.1.6 + - @backstage/plugin-catalog@0.6.11 + - @backstage/plugin-catalog-react@0.4.2 + - @backstage/integration-react@0.1.7 + +## 0.10.3 + +### Patch Changes + +- 260c053b9: Fix All Material UI Warnings +- db58cf06c: Avoid sanitize safe links in the header of document pages. +- 1d65bd490: Fix Techdocs feedback icon link for GitLab URLs with subgroup(s) in path +- Updated dependencies + - @backstage/core-components@0.3.0 + - @backstage/config@0.1.6 + - @backstage/core-plugin-api@0.1.5 + - @backstage/integration@0.5.9 + - @backstage/integration-react@0.1.6 + - @backstage/plugin-catalog-react@0.4.1 + +## 0.10.2 + +### Patch Changes + +- 9d40fcb1e: - Bumping `material-ui/core` version to at least `4.12.2` as they made some breaking changes in later versions which broke `Pagination` of the `Table`. + - Switching out `material-table` to `@material-table/core` for support for the later versions of `material-ui/core` + - This causes a minor API change to `@backstage/core-components` as the interface for `Table` re-exports the `prop` from the underlying `Table` components. + - `onChangeRowsPerPage` has been renamed to `onRowsPerPageChange` + - `onChangePage` has been renamed to `onPageChange` + - Migration guide is here: +- 11c370af2: Optimize load times by only fetching entities with the `backstage.io/techdocs-ref` annotation +- 2b1ac002d: TechDocs now uses a "safe by default" sanitization library, rather than relying on its own, hard-coded list of allowable tags and attributes. +- Updated dependencies + - @backstage/core-components@0.2.0 + - @backstage/plugin-catalog-react@0.4.0 + - @backstage/core-plugin-api@0.1.4 + - @backstage/integration-react@0.1.5 + - @backstage/theme@0.2.9 + +## 0.10.1 + +### Patch Changes + +- 9266b80ab: Add search list item to display tech docs search results + +- 03bf17e9b: Improve the responsiveness of the EntityPage UI. With this the Header component should scale with the screen size & wrapping should not cause overflowing/blocking of links. Additionally enforce the Pages using the Grid Layout to use it across all screen sizes & to wrap as intended. + + To benefit from the improved responsive layout, the `EntityPage` in existing Backstage applications should be updated to set the `xs` column size on each grid item in the page, as this does not default. For example: + + ```diff + - + + + ``` + +- 378cc6a54: Only update the `path` when the content is updated. + If content and path are updated independently, the frontend rendering is triggered twice on each navigation: Once for the `path` change (with the old content) and once for the new content. + This might result in a flickering rendering that is caused by the async frontend preprocessing, and the fact that replacing the shadow dom content is expensive. + +- 214e7c52d: Refactor the techdocs transformers to return `Promise`s and await all transformations. + +- e35b13afa: Handle error responses in `getTechDocsMetadata` and `getEntityMetadata` such that `` doesn't throw errors. + +- Updated dependencies + - @backstage/core-components@0.1.6 + - @backstage/plugin-catalog-react@0.3.1 + +## 0.10.0 + +### Minor Changes + +- 94a54dd47: Added a `migrateDocsCase()` method to TechDocs publishers, along with + implementations for AWS, Azure, and GCS. + + This change is in support of a future update to TechDocs that will allow for + case-insensitive entity triplet URL access to documentation pages which will + require a migration of existing documentation objects in external storage + solutions. + + See [#4367](https://github.com/backstage/backstage/issues/4367) for details. + +### Patch Changes + +- 537c37b0f: Fix displaying owned documents list by fetching associated entity relations +- 136a91974: Show a "Refresh" button to if the content is stale. + This removes the need to do a full page-reload to display more recent TechDocs content. +- f1200f44c: Rewrite the `/sync/:namespace/:kind/:name` endpoint to support an event-stream as response. + This change allows the sync process to take longer than a normal HTTP timeout. + The stream also emits log events, so the caller can follow the build process in the frontend. +- 3af126cdd: Provide a Drawer component to follow a running build. + This can be used to debug the rendering and get build logs in case an error occurs. +- 2a4a3b32d: Techdocs: fix sidebars not adjusting position automatically +- Updated dependencies + - @backstage/plugin-catalog-react@0.3.0 + +## 0.9.9 + +### Patch Changes + +- 0172d3424: Fixed bug preventing scroll bar from showing up on code blocks in a TechDocs site. +- Updated dependencies + - @backstage/integration@0.5.8 + - @backstage/core-components@0.1.5 + - @backstage/catalog-model@0.9.0 + - @backstage/plugin-catalog-react@0.2.6 + +## 0.9.8 + +### Patch Changes + +- 99a2873c7: Include cookies when making fetch requests for SVG from techdocs plugin +- a444c7431: Filter fetched entity fields to optimize loading techdocs list +- Updated dependencies + - @backstage/plugin-catalog-react@0.2.5 + - @backstage/core-components@0.1.4 + - @backstage/integration@0.5.7 + +## 0.9.7 + +### Patch Changes + +- aefd54da6: Fix the overlapping between the sidebar and the tabs navigation when enabled in mkdocs (features: navigation.tabs) +- 48c9fcd33: Migrated to use the new `@backstage/core-*` packages rather than `@backstage/core`. +- 1dfec7a2a: Refactor the implicit logic from `` into an explicit state machine. This resolves some state synchronization issues when content is refreshed or rebuilt in the backend. +- Updated dependencies + - @backstage/core-plugin-api@0.1.3 + - @backstage/catalog-model@0.8.4 + - @backstage/integration-react@0.1.4 + - @backstage/plugin-catalog-react@0.2.4 + +## 0.9.6 + +### Patch Changes + +- 938aee2fb: Fix the link to the documentation page when no owned documents are displayed +- 2e1fbe203: Do not add trailing slash for .html pages during doc links rewriting +- 9b57fda8b: Fixes a bug that could prevent some externally hosted images (like icons or + build badges) from rendering within TechDocs documentation. +- 667656c8b: Adding support for user owned document filter for TechDocs custom Homepage +- Updated dependencies + - @backstage/plugin-catalog-react@0.2.3 + - @backstage/catalog-model@0.8.3 + - @backstage/core@0.7.13 + +## 0.9.5 + +### Patch Changes + +- aad98c544: Fixes multiple XSS and sanitization bypass vulnerabilities in TechDocs. +- Updated dependencies [e7c5e4b30] +- Updated dependencies [ebe802bc4] +- Updated dependencies [49d7ec169] +- Updated dependencies [1cf1d351f] +- Updated dependencies [deaba2e13] +- Updated dependencies [8e919a6f8] + - @backstage/theme@0.2.8 + - @backstage/catalog-model@0.8.1 + - @backstage/integration@0.5.5 + - @backstage/core@0.7.12 + - @backstage/plugin-catalog-react@0.2.1 + +## 0.9.4 + +### Patch Changes + +- 4ea9df9d3: Set admonition font size to 1rem in TechDocs to align with the rest of the document's font sizes. + Fixes #5448 and #5541. +- bf805b467: Fixes #5529, a bug that prevented TechDocs from rendering pages containing malformed links. +- 203ce6f6f: TechDocs now respects the `download` attribute on anchor tags in generated + markup, allowing documentation authors to bundle downloadable files with their + documentation. +- Updated dependencies [0fd4ea443] +- Updated dependencies [add62a455] +- Updated dependencies [cc592248b] +- Updated dependencies [17c497b81] +- Updated dependencies [704875e26] + - @backstage/integration@0.5.4 + - @backstage/catalog-model@0.8.0 + - @backstage/core@0.7.11 + - @backstage/plugin-catalog-react@0.2.0 + +## 0.9.3 + +### Patch Changes + +- 65e6c4541: Remove circular dependencies +- a62cfe068: Bug fix on sidebar position when Tab-Bar is enabled +- 35e091604: Handle URLs with a `#hash` correctly when rewriting link URLs. +- Updated dependencies [f7f7783a3] +- Updated dependencies [65e6c4541] +- Updated dependencies [68fdbf014] +- Updated dependencies [5da6a561d] + - @backstage/catalog-model@0.7.10 + - @backstage/core@0.7.10 + - @backstage/integration@0.5.3 + +## 0.9.2 + +### Patch Changes + +- 062bbf90f: chore: bump `@testing-library/user-event` from 12.8.3 to 13.1.8 +- 675a569a9: chore: bump `react-use` dependency in all packages +- Updated dependencies [062bbf90f] +- Updated dependencies [10c008a3a] +- Updated dependencies [889d89b6e] +- Updated dependencies [16be1d093] +- Updated dependencies [3f988cb63] +- Updated dependencies [675a569a9] + - @backstage/core@0.7.9 + - @backstage/integration-react@0.1.2 + - @backstage/plugin-catalog-react@0.1.6 + - @backstage/catalog-model@0.7.9 + +## 0.9.1 + +### Patch Changes + +- 2e05277e0: Fix navigation in a page using the table of contents. +- 4075c6367: Make git config optional for techdocs feedback links +- Updated dependencies [38ca05168] +- Updated dependencies [f65adcde7] +- Updated dependencies [81c54d1f2] +- Updated dependencies [80888659b] +- Updated dependencies [7b8272fb7] +- Updated dependencies [d8b81fd28] + - @backstage/integration@0.5.2 + - @backstage/core@0.7.8 + - @backstage/plugin-catalog-react@0.1.5 + - @backstage/theme@0.2.7 + - @backstage/catalog-model@0.7.8 + - @backstage/config@0.1.5 + +## 0.9.0 + +### Minor Changes + +- 21fddf452: Make `techdocsStorageApiRef` and `techdocsApiRef` use interfaces instead of the + actual implementation classes. + + This renames the classes `TechDocsApi` to `TechDocsClient` and `TechDocsStorageApi` + to `TechDocsStorageClient` and renames the interfaces `TechDocs` to `TechDocsApi` + and `TechDocsStorage` to `TechDocsStorageApi` to comply the pattern elsewhere in + the project. This also fixes the types returned by some methods on those + interfaces. + +### Patch Changes + +- 6fbd7beca: Use `EntityRefLink` in header and use relations to reference the owner of the + document. +- 15cbe6815: Fix TechDocs landing page table wrong copied link +- 39bdaa004: Add customization and exportable components for TechDocs landing page +- cb8c848a3: Disable color transitions on links to avoid issues in dark mode. +- 17915e29b: Rework state management to avoid rendering multiple while navigating between pages. +- Updated dependencies [9afcac5af] +- Updated dependencies [e0c9ed759] +- Updated dependencies [6eaecbd81] + - @backstage/core@0.7.7 + +## 0.8.0 + +### Minor Changes + +- ac6025f63: Add feedback link icon in Techdocs Reader that directs to GitLab or GitHub repo issue page with pre-filled title and source link. + For link to appear, requires `repo_url` and `edit_uri` to be filled in mkdocs.yml, as per . An `edit_uri` will need to be specified for self-hosted GitLab/GitHub instances with a different host name. + To identify issue URL format as GitHub or GitLab, the host name of source in `repo_url` is checked if it contains `gitlab` or `github`. Alternately this is determined by matching to `host` values from `integrations` in app-config.yaml. + +### Patch Changes + +- e292e393f: Add a test id to the shadow root element of the Reader to access it easily in e2e tests +- Updated dependencies [94da20976] +- Updated dependencies [d8cc7e67a] +- Updated dependencies [99fbef232] +- Updated dependencies [ab07d77f6] +- Updated dependencies [931b21a12] +- Updated dependencies [937ed39ce] +- Updated dependencies [9a9e7a42f] +- Updated dependencies [50ce875a0] + - @backstage/core@0.7.6 + - @backstage/theme@0.2.6 + +## 0.7.2 + +### Patch Changes + +- fef852ecd: Reworked the TechDocs plugin to support using the configured company name instead of + 'Backstage' in the page title. +- 18f7345a6: Add borders to TechDocs tables and increase font size. Fixes #5264 and #5276. +- Updated dependencies [bb5055aee] +- Updated dependencies [d0d1c2f7b] +- Updated dependencies [5d0740563] +- Updated dependencies [5cafcf452] +- Updated dependencies [86a95ba67] +- Updated dependencies [e27cb6c45] + - @backstage/catalog-model@0.7.7 + - @backstage/core@0.7.5 + +## 0.7.1 + +### Patch Changes + +- bebd1c4fe: Remove the `@backstage/techdocs-common` dependency to not pull in backend config schemas in the frontend. +- Updated dependencies [9f48b548c] +- Updated dependencies [8488a1a96] + - @backstage/plugin-catalog-react@0.1.4 + - @backstage/catalog-model@0.7.5 + +## 0.7.0 + +### Minor Changes + +- aaeb7ecf3: When newer documentation available but not built, show older documentation while async building newer + TechDocs backend: /sync endpoint added to support above, returns immediate success if docs don't need a build, returns delayed success after build if needed + TechDocs backend: /docs endpoint removed as frontend can directly request to techdocs.storageUrl or /static/docs +- 3139f83af: Add sticky sidebars and footer navigation links to TechDocs Reader + +### Patch Changes + +- ea9d977e7: Introduce workaround for admonition icons of MkDocs. +- 2aab54319: TechDocs: links at sidebar and bottom reset scroll position to top +- Updated dependencies [01ccef4c7] +- Updated dependencies [fcc3ada24] +- Updated dependencies [4618774ff] +- Updated dependencies [df59930b3] + - @backstage/plugin-catalog-react@0.1.3 + - @backstage/core@0.7.3 + - @backstage/theme@0.2.5 + +## 0.6.2 + +### Patch Changes + +- 83bfc98a3: On TechDocs page header, change the breadcrumbs link to be static and point to TechDocs homepage. +- e7baa0d2e: Separate techdocs-backend and frontend config schema declarations +- c8b54c370: Extended TechDocs HomePage with owned documents +- Updated dependencies [0434853a5] +- Updated dependencies [8686eb38c] +- Updated dependencies [9ca0e4009] +- Updated dependencies [34ff49b0f] +- Updated dependencies [8686eb38c] +- Updated dependencies [424742dc1] +- Updated dependencies [4e0b5055a] + - @backstage/config@0.1.4 + - @backstage/core@0.7.2 + - @backstage/plugin-catalog-react@0.1.2 + - @backstage/techdocs-common@0.4.5 + - @backstage/test-utils@0.1.9 + +## 0.6.1 + +### Patch Changes + +- aa095e469: OpenStack Swift publisher added for tech-docs. +- 2089de76b: Make use of the new core `ItemCardGrid` and `ItemCardHeader` instead of the deprecated `ItemCard`. +- 868e4cdf2: - Adds a link to the owner entity + - Corrects the link to the component which includes the namespace +- ca4a904f6: Add an optional configuration option for setting the url endpoint for AWS S3 publisher: `techdocs.publisher.awsS3.endpoint` +- Updated dependencies [d7245b733] +- Updated dependencies [0b42fff22] +- Updated dependencies [0b42fff22] +- Updated dependencies [2ef5bc7ea] +- Updated dependencies [ff4d666ab] +- Updated dependencies [aa095e469] +- Updated dependencies [2089de76b] +- Updated dependencies [dc1fc92c8] +- Updated dependencies [bc46435f5] +- Updated dependencies [a501128db] +- Updated dependencies [ca4a904f6] + - @backstage/techdocs-common@0.4.4 + - @backstage/catalog-model@0.7.4 + - @backstage/core@0.7.1 + - @backstage/theme@0.2.4 + +## 0.6.0 + +### Minor Changes + +- 813c6a4f2: Add authorization header on techdocs api requests. Breaking change as clients now needs the Identity API. + +### Patch Changes + +- Updated dependencies [12d8f27a6] +- Updated dependencies [f43192207] +- Updated dependencies [40c0fdbaa] +- Updated dependencies [2a271d89e] +- Updated dependencies [bece09057] +- Updated dependencies [169f48deb] +- Updated dependencies [8a1566719] +- Updated dependencies [9d455f69a] +- Updated dependencies [4c049a1a1] +- Updated dependencies [02816ecd7] +- Updated dependencies [61299519f] + - @backstage/catalog-model@0.7.3 + - @backstage/techdocs-common@0.4.3 + - @backstage/core@0.7.0 + - @backstage/plugin-catalog-react@0.1.1 + +## 0.5.8 + +### Patch Changes + +- f37992797: Got rid of some `attr` and cleaned up a bit in the TechDocs config schema. +- 2499f6cde: Add support for assuming role in AWS integrations +- Updated dependencies [3a58084b6] +- Updated dependencies [e799e74d4] +- Updated dependencies [dc12852c9] +- Updated dependencies [d0760ecdf] +- Updated dependencies [1407b34c6] +- Updated dependencies [88f1f1b60] +- Updated dependencies [bad21a085] +- Updated dependencies [9615e68fb] +- Updated dependencies [49f9b7346] +- Updated dependencies [5c2e2863f] +- Updated dependencies [3a58084b6] +- Updated dependencies [2499f6cde] +- Updated dependencies [a1f5e6545] +- Updated dependencies [1e4ddd71d] +- Updated dependencies [2c1f2a7c2] + - @backstage/core@0.6.3 + - @backstage/test-utils@0.1.8 + - @backstage/plugin-catalog-react@0.1.0 + - @backstage/catalog-model@0.7.2 + - @backstage/techdocs-common@0.4.2 + - @backstage/config@0.1.3 + +## 0.5.7 + +### Patch Changes + +- Updated dependencies [fd3f2a8c0] +- Updated dependencies [fb28da212] +- Updated dependencies [d34d26125] +- Updated dependencies [0af242b6d] +- Updated dependencies [f4c2bcf54] +- Updated dependencies [10a0124e0] +- Updated dependencies [07e226872] +- Updated dependencies [26e143e60] +- Updated dependencies [c6655413d] +- Updated dependencies [44414239f] +- Updated dependencies [b0a41c707] +- Updated dependencies [f62e7abe5] +- Updated dependencies [96f378d10] +- Updated dependencies [688b73110] + - @backstage/core@0.6.2 + - @backstage/techdocs-common@0.4.1 + - @backstage/plugin-catalog-react@0.0.4 + +## 0.5.6 + +### Patch Changes + +- f5e564cd6: Improve display of error messages +- 41af18227: Migrated to new composability API, exporting the plugin instance as `techdocsPlugin`, the top-level page as `TechdocsPage`, and the entity content as `EntityTechdocsContent`. +- 8f3443427: Enhance API calls to support trapping 500 errors from techdocs-backend +- Updated dependencies [77ad0003a] +- Updated dependencies [b51ee6ece] +- Updated dependencies [19d354c78] +- Updated dependencies [08142b256] +- Updated dependencies [08142b256] +- Updated dependencies [b51ee6ece] + - @backstage/techdocs-common@0.4.0 + - @backstage/test-utils@0.1.7 + - @backstage/plugin-catalog-react@0.0.3 + - @backstage/core@0.6.1 + +## 0.5.5 + +### Patch Changes + +- 5fa3bdb55: Add `href` in addition to `onClick` to `ItemCard`. Ensure that the height of a + `ItemCard` with and without tags is equal. +- e44925723: `techdocs.requestUrl` and `techdocs.storageUrl` are now optional configs and the discovery API will be used to get the URL where techdocs plugin is hosted. +- 019fe39a0: Switch dependency from `@backstage/plugin-catalog` to `@backstage/plugin-catalog-react`. +- Updated dependencies [c777df180] +- Updated dependencies [12ece98cd] +- Updated dependencies [d82246867] +- Updated dependencies [7fc89bae2] +- Updated dependencies [c810082ae] +- Updated dependencies [5fa3bdb55] +- Updated dependencies [6e612ce25] +- Updated dependencies [e44925723] +- Updated dependencies [025e122c3] +- Updated dependencies [21e624ba9] +- Updated dependencies [da9f53c60] +- Updated dependencies [32c95605f] +- Updated dependencies [7881f2117] +- Updated dependencies [f0320190d] +- Updated dependencies [54c7d02f7] +- Updated dependencies [11cb5ef94] + - @backstage/techdocs-common@0.3.7 + - @backstage/core@0.6.0 + - @backstage/plugin-catalog-react@0.0.2 + - @backstage/theme@0.2.3 + - @backstage/catalog-model@0.7.1 + +## 0.5.4 + +### Patch Changes + +- a5e27d5c1: Create type for TechDocsMetadata (#3716) + + This change introduces a new type (TechDocsMetadata) in packages/techdocs-common. This type is then introduced in the endpoint response in techdocs-backend and in the api interface in techdocs (frontend). + +- Updated dependencies [def2307f3] + +- Updated dependencies [efd6ef753] + +- Updated dependencies [593632f07] + +- Updated dependencies [33846acfc] + +- Updated dependencies [a187b8ad0] + +- Updated dependencies [f04db53d7] + +- Updated dependencies [53c9c51f2] + +- Updated dependencies [a5e27d5c1] + +- Updated dependencies [a93f42213] + - @backstage/catalog-model@0.7.0 + - @backstage/core@0.5.0 + - @backstage/plugin-catalog@0.2.12 + - @backstage/techdocs-common@0.3.5 + +## 0.5.3 + +### Patch Changes + +- dbe4450c3: Google Cloud authentication in TechDocs has been improved. + + 1. `techdocs.publisher.googleGcs.credentials` is now optional. If it is missing, `GOOGLE_APPLICATION_CREDENTIALS` + environment variable (and some other methods) will be used to authenticate. + Read more here + + 2. `techdocs.publisher.googleGcs.projectId` is no longer used. You can remove it from your `app-config.yaml`. + +- a6f9dca0d: Remove dependency on `@backstage/core-api`. No plugin should ever depend on that package; it's an internal concern whose important bits are re-exported by `@backstage/core` which is the public facing dependency to use. + +- b3b9445df: AWS S3 authentication in TechDocs has been improved. + + 1. `techdocs.publisher.awsS3.bucketName` is now the only required config. `techdocs.publisher.awsS3.credentials` and `techdocs.publisher.awsS3.region` are optional. + + 2. If `techdocs.publisher.awsS3.credentials` and `techdocs.publisher.awsS3.region` are missing, the AWS environment variables `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY` and `AWS_REGION` will be used. There are more better ways of setting up AWS authentication. Read the guide at + +- e5d12f705: Use `history.pushState` for hash link navigation. + +- Updated dependencies [68ad5af51] + +- Updated dependencies [f3b064e1c] + +- Updated dependencies [371f67ecd] + +- Updated dependencies [f1e74777a] + +- Updated dependencies [dbe4450c3] + +- Updated dependencies [c00488983] + +- Updated dependencies [265a7ab30] + +- Updated dependencies [5826d0973] + +- Updated dependencies [b3b9445df] + +- Updated dependencies [abbee6fff] + +- Updated dependencies [147fadcb9] + - @backstage/techdocs-common@0.3.3 + - @backstage/catalog-model@0.6.1 + - @backstage/plugin-catalog@0.2.11 + - @backstage/core@0.4.4 + +## 0.5.2 + +### Patch Changes + +- 359f9d2d8: Added configuration schema for the commonly used properties of techdocs and techdocs-backend plugins +- Updated dependencies [a08c32ced] +- Updated dependencies [7e0b8cac5] +- Updated dependencies [8804e8981] +- Updated dependencies [87c0c53c2] +- Updated dependencies [86c3c652a] +- Updated dependencies [27f2af935] + - @backstage/core-api@0.2.8 + - @backstage/core@0.4.3 + - @backstage/plugin-catalog@0.2.9 + - @backstage/techdocs-common@0.3.1 + +## 0.5.1 + +### Patch Changes + +- Updated dependencies [d681db2b5] +- Updated dependencies [1dc445e89] +- Updated dependencies [342270e4d] +- Updated dependencies [1dc445e89] +- Updated dependencies [a8573e53b] + - @backstage/core-api@0.2.7 + - @backstage/core@0.4.2 + - @backstage/test-utils@0.1.6 + - @backstage/plugin-catalog@0.2.8 + - @backstage/techdocs-common@0.3.0 + +## 0.5.0 + +### Minor Changes + +- dae4f3983: _Breaking changes_ + + 1. Added option to use Google Cloud Storage as a choice to store the static generated files for TechDocs. + It can be configured using `techdocs.publisher.type` option in `app-config.yaml`. + Step-by-step guide to configure GCS is available here + Set `techdocs.publisher.type` to `'local'` if you want to continue using local filesystem to store TechDocs files. + + 2. `techdocs.builder` is now required and can be set to `'local'` or `'external'`. (Set it to `'local'` for now, since CI/CD build + workflow for TechDocs will be available soon (in few weeks)). + If builder is set to 'local' and you open a TechDocs page, `techdocs-backend` will try to generate the docs, publish to storage and + show the generated docs afterwords. + If builder is set to `'external'`, `techdocs-backend` will only fetch the docs and will NOT try to generate and publish. In this case of `'external'`, + we assume that docs are being built in the CI/CD pipeline of the repository. + TechDocs will not assume a default value for `techdocs.builder`. It is better to explicitly define it in the `app-config.yaml`. + + 3. When configuring TechDocs in your backend, there is a difference in how a new publisher is created. + + --- const publisher = new LocalPublish(logger, discovery); + +++ const publisher = Publisher.fromConfig(config, logger, discovery); + + Based on the config `techdocs.publisher.type`, the publisher could be either Local publisher or Google Cloud Storage publisher. + + 4. `techdocs.storageUrl` is now a required config. Should be `http://localhost:7000/api/techdocs/static/docs` in most setups. + + 5. Parts of `@backstage/plugin-techdocs-backend` have been moved to a new package `@backstage/techdocs-common` to generate docs. Also to publish docs + to-and-fro between TechDocs and a storage (either local or external). However, a Backstage app does NOT need to import the `techdocs-common` package - + app should only import `@backstage/plugin-techdocs` and `@backstage/plugin-techdocs-backend`. + + _Patch changes_ + + 1. See all of TechDocs config options and its documentation + + 2. Logic about serving static files and metadata retrieval have been abstracted away from the router in `techdocs-backend` to the instance of publisher. + + 3. Removed Material UI Spinner from TechDocs header. Spinners cause unnecessary UX distraction. + Case 1 (when docs are built and are to be served): Spinners appear for a split second before the name of site shows up. This unnecessarily distracts eyes because spinners increase the size of the Header. A dot (.) would do fine. Definitely more can be done. + Case 2 (when docs are being generated): There is already a linear progress bar (which is recommended in Storybook). + +### Patch Changes + +- Updated dependencies [c911061b7] +- Updated dependencies [dae4f3983] +- Updated dependencies [8ef71ed32] +- Updated dependencies [0e6298f7e] +- Updated dependencies [7dd2ef7d1] +- Updated dependencies [ac3560b42] + - @backstage/catalog-model@0.6.0 + - @backstage/techdocs-common@0.2.0 + - @backstage/core@0.4.1 + - @backstage/core-api@0.2.6 + - @backstage/plugin-catalog@0.2.7 + +## 0.4.0 + +### Minor Changes + +- 87a33d2fe: Removed modifyCss transformer and moved the css to injectCss transformer + Fixed issue where some internal doc links would cause a reload of the page + +### Patch Changes + +- Updated dependencies [b6557c098] +- Updated dependencies [2527628e1] +- Updated dependencies [6011b7d3e] +- Updated dependencies [e1f4e24ef] +- Updated dependencies [1c69d4716] +- Updated dependencies [d8d5a17da] +- Updated dependencies [83b6e0c1f] +- Updated dependencies [1665ae8bb] +- Updated dependencies [04f26f88d] +- Updated dependencies [ff243ce96] + - @backstage/core-api@0.2.5 + - @backstage/core@0.4.0 + - @backstage/plugin-catalog@0.2.6 + - @backstage/test-utils@0.1.5 + - @backstage/catalog-model@0.5.0 + - @backstage/theme@0.2.2 + +## 0.3.1 + +### Patch Changes + +- da2ad65cb: Use type EntityName from catalog-model for entities +- Updated dependencies [b4488ddb0] +- Updated dependencies [08835a61d] +- Updated dependencies [a9fd599f7] +- Updated dependencies [bcc211a08] +- Updated dependencies [ebf37bbae] + - @backstage/core-api@0.2.4 + - @backstage/catalog-model@0.4.0 + - @backstage/plugin-catalog@0.2.5 + - @backstage/test-utils@0.1.4 + +## 0.3.0 + +### Minor Changes + +- 4b53294a6: - Use techdocs annotation to add repo_url if missing in mkdocs.yml. Having repo_url creates a Edit button on techdocs pages. + - techdocs-backend: API endpoint `/metadata/mkdocs/*` renamed to `/metadata/techdocs/*` + +### Patch Changes + +- Updated dependencies [6f70ed7a9] +- Updated dependencies [ab94c9542] +- Updated dependencies [2daf18e80] +- Updated dependencies [069cda35f] +- Updated dependencies [700a212b4] + - @backstage/plugin-catalog@0.2.4 + - @backstage/catalog-model@0.3.1 + - @backstage/core-api@0.2.3 + +## 0.2.3 + +### Patch Changes + +- Updated dependencies [475fc0aaa] +- Updated dependencies [1166fcc36] +- Updated dependencies [1185919f3] + - @backstage/core@0.3.2 + - @backstage/catalog-model@0.3.0 + - @backstage/plugin-catalog@0.2.3 + +## 0.2.2 + +### Patch Changes + +- 1722cb53c: Added configuration schema +- Updated dependencies [1722cb53c] +- Updated dependencies [8b7737d0b] + - @backstage/core@0.3.1 + - @backstage/plugin-catalog@0.2.2 + - @backstage/test-utils@0.1.3 + +## 0.2.1 + +### Patch Changes + +- Updated dependencies [c5bab94ab] +- Updated dependencies [7b37d65fd] +- Updated dependencies [4aca74e08] +- Updated dependencies [e8f69ba93] +- Updated dependencies [0c0798f08] +- Updated dependencies [0c0798f08] +- Updated dependencies [199237d2f] +- Updated dependencies [6627b626f] +- Updated dependencies [4577e377b] +- Updated dependencies [2d0bd1be7] + - @backstage/core-api@0.2.1 + - @backstage/core@0.3.0 + - @backstage/theme@0.2.1 + - @backstage/plugin-catalog@0.2.1 + +## 0.2.0 + +### Minor Changes + +- 28edd7d29: Create backend plugin through CLI +- 8351ad79b: Add a message if techdocs takes long time to load + + Fixes #2416. + + The UI after the change should look like this: + + ![techdocs-progress-bar](https://user-images.githubusercontent.com/33940798/94189286-296ac980-fec8-11ea-9051-1b3db938d12f.gif) + +### Patch Changes + +- 782f3b354: add test case for Progress component +- 57b54c8ed: While techdocs fetches site name and metadata for the component, the page title was displayed as '[object Object] | Backstage'. This has now been fixed to display the component ID if site name is not present or being fetched. +- Updated dependencies [28edd7d29] +- Updated dependencies [819a70229] +- Updated dependencies [3a4236570] +- Updated dependencies [ae5983387] +- Updated dependencies [0d4459c08] +- Updated dependencies [cbbd271c4] +- Updated dependencies [482b6313d] +- Updated dependencies [e0be86b6f] +- Updated dependencies [f70a52868] +- Updated dependencies [12b5fe940] +- Updated dependencies [368fd8243] +- Updated dependencies [1c60f716e] +- Updated dependencies [144c66d50] +- Updated dependencies [a768a07fb] +- Updated dependencies [b79017fd3] +- Updated dependencies [6d97d2d6f] +- Updated dependencies [5adfc005e] +- Updated dependencies [f0aa01bcc] +- Updated dependencies [0aecfded0] +- Updated dependencies [93a3fa3ae] +- Updated dependencies [782f3b354] +- Updated dependencies [8b9c8196f] +- Updated dependencies [2713f28f4] +- Updated dependencies [406015b0d] +- Updated dependencies [82759d3e4] +- Updated dependencies [60d40892c] +- Updated dependencies [ac8d5d5c7] +- Updated dependencies [2ebcfac8d] +- Updated dependencies [fa56f4615] +- Updated dependencies [ebca83d48] +- Updated dependencies [aca79334f] +- Updated dependencies [c0d5242a0] +- Updated dependencies [b3d57961c] +- Updated dependencies [0b956f21b] +- Updated dependencies [26e69ab1a] +- Updated dependencies [97c2cb19b] +- Updated dependencies [3beb5c9fc] +- Updated dependencies [cbab5bbf8] +- Updated dependencies [754e31db5] +- Updated dependencies [1611c6dbc] + - @backstage/plugin-catalog@0.2.0 + - @backstage/core-api@0.2.0 + - @backstage/core@0.2.0 + - @backstage/catalog-model@0.2.0 + - @backstage/theme@0.2.0 + - @backstage/test-utils@0.1.2 + +## @backstage/ui@0.9.0-next.1 + +### Minor Changes + +- 5c614ff: **BREAKING**: Migrated Checkbox component from Base UI to React Aria Components. + + API changes required: + + - `checked` → `isSelected` + - `defaultChecked` → `defaultSelected` + - `disabled` → `isDisabled` + - `required` → `isRequired` + - `label` prop removed - use `children` instead + - CSS: `bui-CheckboxLabel` class removed + - Data attribute: `data-checked` → `data-selected` + - Use without label is no longer supported + + Migration examples: + + Before: + + ```tsx + + ``` + + After: + + ```tsx + + Accept terms + + ``` + + Before: + + ```tsx + + ``` + + After: + + ```tsx + Option + ``` + + Before: + + ```tsx + + ``` + + After: + + ```tsx + + Accessible label + + ``` + +- b78fc45: **BREAKING**: Changed className prop behavior to augment default styles instead of being ignored or overriding them. + + Affected components: + + - Menu, MenuListBox, MenuAutocomplete, MenuAutocompleteListbox, MenuItem, MenuListBoxItem, MenuSection, MenuSeparator + - Switch + - Skeleton + - FieldLabel + - Header, HeaderToolbar + - HeaderPage + - Tabs, TabList, Tab, TabPanel + + If you were passing custom className values to any of these components that relied on the previous behavior, you may need to adjust your styles to account for the default classes now being applied alongside your custom classes. + +### Patch Changes + +- ff9f0c3: Enable tree-shaking of imports other than `*.css`. +- 1ef3ca4: Added new VisuallyHidden component for hiding content visually while keeping it accessible to screen readers. + +## @backstage/plugin-notifications-backend-module-email@0.3.16-next.0 + +# @backstage/plugin-notifications-backend-module-email + +## 0.3.15-next.0 + +### Patch Changes + +- 22a5362: Updated `AWS SES` client to version 2 to support `nodemailer` version 7. +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/plugin-notifications-node@0.2.21-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/integration-aws-node@0.1.19-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/catalog-client@1.12.1-next.0 + - @backstage/types@1.2.2 + - @backstage/plugin-catalog-node@1.19.2-next.0 + - @backstage/plugin-notifications-common@0.1.2-next.0 + +## 0.3.14 + +### Patch Changes + +- b8cf31a: chore(deps): bump `nodemailer` from 6.9.16 to 7.0.7 +- f5e0963: Removed unused dependencies +- Updated dependencies + - @backstage/config@1.3.5 + - @backstage/backend-plugin-api@1.4.4 + - @backstage/integration-aws-node@0.1.18 + - @backstage/plugin-catalog-node@1.19.1 + - @backstage/plugin-notifications-common@0.1.1 + - @backstage/plugin-notifications-node@0.2.20 + +## 0.3.14-next.1 + +### Patch Changes + +- b8cf31a: chore(deps): bump `nodemailer` from 6.9.16 to 7.0.7 +- f5e0963: Removed unused dependencies + +## 0.3.14-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.4-next.0 + - @backstage/integration-aws-node@0.1.18-next.0 + - @backstage/backend-plugin-api@1.4.4-next.0 + - @backstage/plugin-notifications-common@0.1.1-next.0 + - @backstage/plugin-catalog-node@1.19.1-next.0 + - @backstage/plugin-notifications-node@0.2.20-next.0 + - @backstage/catalog-client@1.12.0 + +## 0.3.13 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.19.0 + - @backstage/catalog-client@1.12.0 + - @backstage/types@1.2.2 + - @backstage/plugin-notifications-node@0.2.19 + - @backstage/backend-plugin-api@1.4.3 + +## 0.3.13-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.12.0-next.0 + - @backstage/plugin-catalog-node@1.19.0-next.1 + - @backstage/integration-aws-node@0.1.17 + - @backstage/plugin-notifications-node@0.2.19-next.1 + +## 0.3.13-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.3-next.0 + - @backstage/plugin-catalog-node@1.18.1-next.0 + - @backstage/plugin-notifications-node@0.2.19-next.0 + +## 0.3.12 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.11.0 + - @backstage/plugin-catalog-node@1.18.0 + - @backstage/plugin-notifications-common@0.1.0 + - @backstage/plugin-notifications-node@0.2.18 + - @backstage/backend-plugin-api@1.4.2 + +## 0.3.12-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.11.0-next.0 + - @backstage/plugin-catalog-node@1.18.0-next.0 + - @backstage/plugin-notifications-node@0.2.18-next.0 + - @backstage/backend-plugin-api@1.4.2-next.0 + - @backstage/catalog-model@1.7.5 + - @backstage/config@1.3.3 + - @backstage/integration-aws-node@0.1.17 + - @backstage/types@1.2.1 + - @backstage/plugin-notifications-common@0.0.10 + +## 0.3.11 + +### Patch Changes + +- f92c9fc: Add optional config for `ses` mail options with `sourceArn`, `fromArn`, `configurationSetName` +- Updated dependencies + - @backstage/config@1.3.3 + - @backstage/catalog-model@1.7.5 + - @backstage/catalog-client@1.10.2 + - @backstage/backend-plugin-api@1.4.1 + - @backstage/integration-aws-node@0.1.17 + - @backstage/plugin-catalog-node@1.17.2 + - @backstage/plugin-notifications-common@0.0.10 + - @backstage/plugin-notifications-node@0.2.17 + +## 0.3.11-next.1 + +### Patch Changes + +- f92c9fc: Add optional config for `ses` mail options with `sourceArn`, `fromArn`, `configurationSetName` + +## 0.3.11-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/catalog-model@1.7.5-next.0 + - @backstage/catalog-client@1.10.2-next.0 + - @backstage/integration-aws-node@0.1.17-next.0 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-notifications-common@0.0.10-next.0 + - @backstage/plugin-catalog-node@1.17.2-next.0 + - @backstage/plugin-notifications-node@0.2.17-next.0 + +## 0.3.10 + +### Patch Changes + +- 8a150bf: Internal changes to switch to the non-alpha `catalogServiceRef` +- Updated dependencies + - @backstage/catalog-client@1.10.1 + - @backstage/plugin-notifications-common@0.0.9 + - @backstage/plugin-catalog-node@1.17.1 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/plugin-notifications-node@0.2.16 + - @backstage/catalog-model@1.7.4 + - @backstage/config@1.3.2 + - @backstage/integration-aws-node@0.1.16 + - @backstage/types@1.2.1 + +## 0.3.10-next.2 + +### Patch Changes + +- 8a150bf: Internal changes to switch to the non-alpha `catalogServiceRef` +- Updated dependencies + - @backstage/backend-plugin-api@1.4.0-next.1 + - @backstage/catalog-client@1.10.1-next.0 + - @backstage/catalog-model@1.7.4 + - @backstage/config@1.3.2 + - @backstage/integration-aws-node@0.1.16 + - @backstage/types@1.2.1 + - @backstage/plugin-catalog-node@1.17.1-next.1 + - @backstage/plugin-notifications-common@0.0.9-next.0 + - @backstage/plugin-notifications-node@0.2.16-next.1 + +## 0.3.10-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.10.1-next.0 + - @backstage/plugin-notifications-common@0.0.9-next.0 + - @backstage/plugin-catalog-node@1.17.1-next.1 + - @backstage/plugin-notifications-node@0.2.16-next.1 + - @backstage/backend-plugin-api@1.4.0-next.1 + - @backstage/catalog-model@1.7.4 + - @backstage/config@1.3.2 + - @backstage/integration-aws-node@0.1.16 + - @backstage/types@1.2.1 + +## 0.3.10-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.0-next.0 + - @backstage/plugin-catalog-node@1.17.1-next.0 + - @backstage/plugin-notifications-node@0.2.16-next.0 + +## 0.3.9 + +### Patch Changes + +- aa3a63a: Enable the ability to configure the endpoint for the SES connection used in the notifications email module. This enables the configuration of alternate endpoints as required, for example for local testing or alternative stacks. +- Updated dependencies + - @backstage/catalog-model@1.7.4 + - @backstage/plugin-catalog-node@1.17.0 + - @backstage/backend-plugin-api@1.3.1 + - @backstage/integration-aws-node@0.1.16 + - @backstage/catalog-client@1.10.0 + - @backstage/config@1.3.2 + - @backstage/plugin-notifications-node@0.2.15 + - @backstage/types@1.2.1 + - @backstage/plugin-notifications-common@0.0.8 + +## 0.3.9-next.3 + +### Patch Changes + +- aa3a63a: Enable the ability to configure the endpoint for the SES connection used in the notifications email module. This enables the configuration of alternate endpoints as required, for example for local testing or alternative stacks. +- Updated dependencies + - @backstage/backend-plugin-api@1.3.1-next.2 + - @backstage/catalog-client@1.10.0-next.0 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/integration-aws-node@0.1.16-next.0 + - @backstage/types@1.2.1 + - @backstage/plugin-catalog-node@1.17.0-next.2 + - @backstage/plugin-notifications-common@0.0.8 + - @backstage/plugin-notifications-node@0.2.15-next.2 + +## 0.3.9-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/integration-aws-node@0.1.16-next.0 + - @backstage/config@1.3.2 + - @backstage/plugin-notifications-node@0.2.15-next.1 + - @backstage/backend-plugin-api@1.3.1-next.1 + - @backstage/catalog-client@1.10.0-next.0 + - @backstage/catalog-model@1.7.3 + - @backstage/types@1.2.1 + - @backstage/plugin-catalog-node@1.17.0-next.1 + - @backstage/plugin-notifications-common@0.0.8 + +## 0.3.9-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.17.0-next.1 + - @backstage/backend-plugin-api@1.3.1-next.1 + - @backstage/catalog-client@1.10.0-next.0 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/integration-aws-node@0.1.15 + - @backstage/types@1.2.1 + - @backstage/plugin-notifications-common@0.0.8 + - @backstage/plugin-notifications-node@0.2.15-next.1 + +## 0.3.9-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.10.0-next.0 + - @backstage/plugin-catalog-node@1.17.0-next.0 + - @backstage/backend-plugin-api@1.3.1-next.0 + - @backstage/plugin-notifications-node@0.2.15-next.0 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/integration-aws-node@0.1.15 + - @backstage/types@1.2.1 + - @backstage/plugin-notifications-common@0.0.8 + +## 0.3.8 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.16.3 + - @backstage/backend-plugin-api@1.3.0 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/integration-aws-node@0.1.15 + - @backstage/types@1.2.1 + - @backstage/plugin-notifications-common@0.0.8 + - @backstage/plugin-notifications-node@0.2.14 + +## 0.3.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.16.3-next.0 + - @backstage/backend-plugin-api@1.2.1 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/integration-aws-node@0.1.15 + - @backstage/types@1.2.1 + - @backstage/plugin-notifications-common@0.0.8 + - @backstage/plugin-notifications-node@0.2.13 + +## 0.3.7 + +### Patch Changes + +- Updated dependencies + - @backstage/integration-aws-node@0.1.15 + - @backstage/backend-plugin-api@1.2.1 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/types@1.2.1 + - @backstage/plugin-catalog-node@1.16.1 + - @backstage/plugin-notifications-common@0.0.8 + - @backstage/plugin-notifications-node@0.2.13 + +## 0.3.7-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/integration-aws-node@0.1.15 + - @backstage/types@1.2.1 + - @backstage/plugin-catalog-node@1.16.1-next.1 + - @backstage/plugin-notifications-common@0.0.8 + - @backstage/plugin-notifications-node@0.2.13-next.2 + +## 0.3.7-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.1-next.1 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/integration-aws-node@0.1.15 + - @backstage/types@1.2.1 + - @backstage/plugin-catalog-node@1.16.1-next.1 + - @backstage/plugin-notifications-common@0.0.8 + - @backstage/plugin-notifications-node@0.2.13-next.1 + +## 0.3.7-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.1-next.0 + - @backstage/plugin-catalog-node@1.16.1-next.0 + - @backstage/plugin-notifications-node@0.2.13-next.0 + +## 0.3.6 + +### Patch Changes + +- 6259aa9: Add transport for Azure Communication Service +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0 + - @backstage/plugin-catalog-node@1.16.0 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/integration-aws-node@0.1.15 + - @backstage/types@1.2.1 + - @backstage/plugin-notifications-common@0.0.8 + - @backstage/plugin-notifications-node@0.2.12 + +## 0.3.6-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.16.0-next.3 + - @backstage/backend-plugin-api@1.2.0-next.2 + - @backstage/plugin-notifications-node@0.2.12-next.2 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/integration-aws-node@0.1.15 + - @backstage/types@1.2.1 + - @backstage/plugin-notifications-common@0.0.8 + +## 0.3.6-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.2.0-next.1 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/integration-aws-node@0.1.15 + - @backstage/types@1.2.1 + - @backstage/plugin-catalog-node@1.16.0-next.2 + - @backstage/plugin-notifications-common@0.0.8 + - @backstage/plugin-notifications-node@0.2.12-next.1 + +## 0.3.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.16.0-next.1 + - @backstage/backend-plugin-api@1.2.0-next.0 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/integration-aws-node@0.1.15 + - @backstage/types@1.2.1 + - @backstage/plugin-notifications-common@0.0.8 + - @backstage/plugin-notifications-node@0.2.12-next.0 + +## 0.3.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.15.2-next.0 + - @backstage/backend-plugin-api@1.2.0-next.0 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/integration-aws-node@0.1.15 + - @backstage/types@1.2.1 + - @backstage/plugin-notifications-common@0.0.8 + - @backstage/plugin-notifications-node@0.2.12-next.0 + +## 0.3.5 + +### Patch Changes + +- bed5f35: Added more examples of the plugin configuration +- Updated dependencies + - @backstage/types@1.2.1 + - @backstage/backend-plugin-api@1.1.1 + - @backstage/catalog-client@1.9.1 + - @backstage/catalog-model@1.7.3 + - @backstage/config@1.3.2 + - @backstage/integration-aws-node@0.1.15 + - @backstage/plugin-catalog-node@1.15.1 + - @backstage/plugin-notifications-common@0.0.8 + - @backstage/plugin-notifications-node@0.2.11 + +## 0.3.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/types@1.2.1-next.0 + - @backstage/backend-plugin-api@1.1.1-next.1 + - @backstage/catalog-model@1.7.3-next.0 + - @backstage/config@1.3.2-next.0 + - @backstage/plugin-catalog-node@1.15.1-next.1 + - @backstage/integration-aws-node@0.1.15-next.0 + - @backstage/plugin-notifications-node@0.2.11-next.1 + - @backstage/catalog-client@1.9.1-next.0 + - @backstage/plugin-notifications-common@0.0.8-next.0 + +## 0.3.5-next.0 + +### Patch Changes + +- bed5f35: Added more examples of the plugin configuration +- Updated dependencies + - @backstage/backend-plugin-api@1.1.1-next.0 + - @backstage/catalog-client@1.9.0 + - @backstage/catalog-model@1.7.2 + - @backstage/config@1.3.1 + - @backstage/integration-aws-node@0.1.14 + - @backstage/types@1.2.0 + - @backstage/plugin-catalog-node@1.15.1-next.0 + - @backstage/plugin-notifications-common@0.0.7 + - @backstage/plugin-notifications-node@0.2.11-next.0 + +## 0.3.4 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.1.0 + - @backstage/plugin-catalog-node@1.15.0 + - @backstage/catalog-client@1.9.0 + - @backstage/plugin-notifications-node@0.2.10 + - @backstage/catalog-model@1.7.2 + - @backstage/config@1.3.1 + - @backstage/integration-aws-node@0.1.14 + - @backstage/types@1.2.0 + - @backstage/plugin-notifications-common@0.0.7 + +## 0.3.4-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.1.0-next.2 + - @backstage/plugin-catalog-node@1.15.0-next.2 + - @backstage/plugin-notifications-node@0.2.10-next.2 + - @backstage/catalog-client@1.9.0-next.2 + - @backstage/catalog-model@1.7.2-next.0 + - @backstage/config@1.3.1-next.0 + - @backstage/integration-aws-node@0.1.14-next.0 + - @backstage/types@1.2.0 + - @backstage/plugin-notifications-common@0.0.7-next.0 + +## 0.3.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.15.0-next.1 + - @backstage/catalog-client@1.9.0-next.1 + - @backstage/plugin-notifications-node@0.2.10-next.1 + - @backstage/backend-plugin-api@1.1.0-next.1 + - @backstage/catalog-model@1.7.1 + - @backstage/config@1.3.0 + - @backstage/integration-aws-node@0.1.13 + - @backstage/types@1.2.0 + - @backstage/plugin-notifications-common@0.0.6 + +## 0.3.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.0.3-next.0 + - @backstage/catalog-client@1.8.1-next.0 + - @backstage/catalog-model@1.7.1 + - @backstage/config@1.3.0 + - @backstage/integration-aws-node@0.1.13 + - @backstage/types@1.2.0 + - @backstage/plugin-catalog-node@1.14.1-next.0 + - @backstage/plugin-notifications-common@0.0.6 + - @backstage/plugin-notifications-node@0.2.10-next.0 + +## 0.3.3 + +### Patch Changes + +- d52d7f9: Support ISO and ms string forms of durations in config too +- 5d74716: Remove unused backend-common dependency +- Updated dependencies + - @backstage/catalog-client@1.8.0 + - @backstage/config@1.3.0 + - @backstage/types@1.2.0 + - @backstage/integration-aws-node@0.1.13 + - @backstage/plugin-catalog-node@1.14.0 + - @backstage/backend-plugin-api@1.0.2 + - @backstage/plugin-notifications-common@0.0.6 + - @backstage/plugin-notifications-node@0.2.9 + - @backstage/catalog-model@1.7.1 + +## 0.3.3-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/integration-aws-node@0.1.13-next.0 + - @backstage/backend-plugin-api@1.0.2-next.2 + - @backstage/catalog-client@1.8.0-next.1 + - @backstage/catalog-model@1.7.0 + - @backstage/config@1.2.0 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-node@1.14.0-next.2 + - @backstage/plugin-notifications-common@0.0.6-next.0 + - @backstage/plugin-notifications-node@0.2.9-next.3 + +## 0.3.3-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.8.0-next.1 + - @backstage/plugin-catalog-node@1.14.0-next.2 + - @backstage/plugin-notifications-node@0.2.9-next.2 + - @backstage/backend-plugin-api@1.0.2-next.2 + - @backstage/catalog-model@1.7.0 + - @backstage/config@1.2.0 + - @backstage/integration-aws-node@0.1.12 + - @backstage/types@1.1.1 + - @backstage/plugin-notifications-common@0.0.6-next.0 + +## 0.3.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.0.2-next.1 + - @backstage/catalog-client@1.8.0-next.0 + - @backstage/catalog-model@1.7.0 + - @backstage/config@1.2.0 + - @backstage/integration-aws-node@0.1.12 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-node@1.14.0-next.1 + - @backstage/plugin-notifications-common@0.0.6-next.0 + - @backstage/plugin-notifications-node@0.2.9-next.1 + +## 0.3.3-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.14.0-next.0 + - @backstage/plugin-notifications-common@0.0.6-next.0 + - @backstage/catalog-client@1.8.0-next.0 + - @backstage/backend-plugin-api@1.0.2-next.0 + - @backstage/catalog-model@1.7.0 + - @backstage/config@1.2.0 + - @backstage/integration-aws-node@0.1.12 + - @backstage/types@1.1.1 + - @backstage/plugin-notifications-node@0.2.9-next.0 + +## 0.3.1 + +### Patch Changes + +- 094eaa3: Remove references to in-repo backend-common +- Updated dependencies + - @backstage/plugin-notifications-node@0.2.7 + - @backstage/plugin-catalog-node@1.13.1 + - @backstage/catalog-client@1.7.1 + - @backstage/backend-plugin-api@1.0.1 + - @backstage/catalog-model@1.7.0 + - @backstage/config@1.2.0 + - @backstage/integration-aws-node@0.1.12 + - @backstage/types@1.1.1 + - @backstage/plugin-notifications-common@0.0.5 + +## 0.3.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.13.1-next.1 + - @backstage/catalog-client@1.7.1-next.0 + - @backstage/backend-plugin-api@1.0.1-next.1 + - @backstage/catalog-model@1.7.0 + - @backstage/config@1.2.0 + - @backstage/integration-aws-node@0.1.12 + - @backstage/types@1.1.1 + - @backstage/plugin-notifications-common@0.0.5 + - @backstage/plugin-notifications-node@0.2.7-next.1 + +## 0.3.1-next.0 + +### Patch Changes + +- 094eaa3: Remove references to in-repo backend-common +- Updated dependencies + - @backstage/plugin-notifications-node@0.2.7-next.0 + - @backstage/backend-plugin-api@1.0.1-next.0 + - @backstage/catalog-client@1.7.0 + - @backstage/catalog-model@1.7.0 + - @backstage/config@1.2.0 + - @backstage/integration-aws-node@0.1.12 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-node@1.13.1-next.0 + - @backstage/plugin-notifications-common@0.0.5 + +## 0.3.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- 5edd344: Refactor to use injected catalog client in the new backend system +- Updated dependencies + - @backstage/backend-common@0.25.0 + - @backstage/backend-plugin-api@1.0.0 + - @backstage/catalog-model@1.7.0 + - @backstage/catalog-client@1.7.0 + - @backstage/plugin-catalog-node@1.13.0 + - @backstage/config@1.2.0 + - @backstage/integration-aws-node@0.1.12 + - @backstage/types@1.1.1 + - @backstage/plugin-notifications-common@0.0.5 + - @backstage/plugin-notifications-node@0.2.6 + +## 0.3.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.25.0-next.2 + - @backstage/backend-plugin-api@1.0.0-next.2 + - @backstage/catalog-client@1.7.0-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/integration-aws-node@0.1.12 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-node@1.12.7-next.2 + - @backstage/plugin-notifications-common@0.0.5 + - @backstage/plugin-notifications-node@0.2.6-next.2 + +## 0.3.0-next.1 + +### Patch Changes + +- 5edd344: Refactor to use injected catalog client in the new backend system +- Updated dependencies + - @backstage/backend-common@0.25.0-next.1 + - @backstage/catalog-client@1.6.7-next.0 + - @backstage/backend-plugin-api@0.9.0-next.1 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/integration-aws-node@0.1.12 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-node@1.12.7-next.1 + - @backstage/plugin-notifications-common@0.0.5 + - @backstage/plugin-notifications-node@0.2.6-next.1 + +## 0.3.0-next.0 + +### Minor Changes + +- d425fc4: **BREAKING**: The return values from `createBackendPlugin`, `createBackendModule`, and `createServiceFactory` are now simply `BackendFeature` and `ServiceFactory`, instead of the previously deprecated form of a function that returns them. For this reason, `createServiceFactory` also no longer accepts the callback form where you provide direct options to the service. This also affects all `coreServices.*` service refs. + + This may in particular affect tests; if you were effectively doing `createBackendModule({...})()` (note the parentheses), you can now remove those extra parentheses at the end. You may encounter cases of this in your `packages/backend/src/index.ts` too, where you add plugins, modules, and services. If you were using `createServiceFactory` with a function as its argument for the purpose of passing in options, this pattern has been deprecated for a while and is no longer supported. You may want to explore the new multiton patterns to achieve your goals, or moving settings to app-config. + + As part of this change, the `IdentityFactoryOptions` type was removed, and can no longer be used to tweak that service. The identity service was also deprecated some time ago, and you will want to [migrate to the new auth system](https://backstage.io/docs/tutorials/auth-service-migration) if you still rely on it. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.9.0-next.0 + - @backstage/backend-common@0.25.0-next.0 + - @backstage/plugin-notifications-node@0.2.6-next.0 + - @backstage/catalog-client@1.6.6 + - @backstage/catalog-model@1.6.0 + - @backstage/config@1.2.0 + - @backstage/integration-aws-node@0.1.12 + - @backstage/types@1.1.1 + - @backstage/plugin-notifications-common@0.0.5 + +## 0.2.0 + +### Minor Changes + +- def53a7: **BREAKING** Following `NotificationTemplateRenderer` methods now return a Promise and **must** be awaited: `getSubject`, `getText` and `getHtml`. + + Required changes and example usage: + + ```diff + import { notificationsEmailTemplateExtensionPoint } from '@backstage/plugin-notifications-backend-module-email'; + import { Notification } from '@backstage/plugin-notifications-common'; + +import { getNotificationSubject, getNotificationTextContent, getNotificationHtmlContent } from 'my-notification-processing-library` + export const notificationsModuleEmailDecorator = createBackendModule({ + pluginId: 'notifications', + moduleId: 'email.templates', + register(reg) { + reg.registerInit({ + deps: { + emailTemplates: notificationsEmailTemplateExtensionPoint, + }, + async init({ emailTemplates }) { + emailTemplates.setTemplateRenderer({ + - getSubject(notification) { + + async getSubject(notification) { + - return `New notification from ${notification.source}`; + + const subject = await getNotificationSubject(notification); + + return `New notification from ${subject}`; + }, + - getText(notification) { + + async getText(notification) { + - return notification.content; + + const text = await getNotificationTextContent(notification); + + return text; + }, + - getHtml(notification) { + + async getHtml(notification) { + - return `

    ${notification.content}

    `; + + const html = await getNotificationHtmlContent(notification); + + return html; + }, + }); + }, + }); + }, + }); + ``` + +### Patch Changes + +- d55b8e3: Avoid sending broadcast emails as a fallback in case the entity-typed notification user can not be resolved. +- cdb630d: Add support for stream transport for debugging purposes +- 83faf24: Notification email processor supports allowing or denying specific email addresses from receiving notifications +- Updated dependencies + - @backstage/backend-plugin-api@0.8.0 + - @backstage/backend-common@0.24.0 + - @backstage/plugin-notifications-node@0.2.4 + - @backstage/catalog-model@1.6.0 + - @backstage/catalog-client@1.6.6 + - @backstage/config@1.2.0 + - @backstage/integration-aws-node@0.1.12 + - @backstage/types@1.1.1 + - @backstage/plugin-notifications-common@0.0.5 + +## 0.2.0-next.3 + +### Patch Changes + +- 83faf24: Notification email processor supports allowing or denying specific email addresses from receiving notifications +- Updated dependencies + - @backstage/backend-plugin-api@0.8.0-next.3 + - @backstage/backend-common@0.23.4-next.3 + - @backstage/catalog-model@1.6.0-next.0 + - @backstage/catalog-client@1.6.6-next.0 + - @backstage/config@1.2.0 + - @backstage/integration-aws-node@0.1.12 + - @backstage/types@1.1.1 + - @backstage/plugin-notifications-common@0.0.5 + - @backstage/plugin-notifications-node@0.2.4-next.3 + +## 0.2.0-next.2 + +### Patch Changes + +- cdb630d: Add support for stream transport for debugging purposes +- Updated dependencies + - @backstage/backend-plugin-api@0.8.0-next.2 + - @backstage/plugin-notifications-node@0.2.4-next.2 + - @backstage/backend-common@0.23.4-next.2 + - @backstage/integration-aws-node@0.1.12 + - @backstage/catalog-client@1.6.5 + - @backstage/catalog-model@1.5.0 + - @backstage/config@1.2.0 + - @backstage/types@1.1.1 + - @backstage/plugin-notifications-common@0.0.5 + +## 0.2.0-next.1 + +### Minor Changes + +- def53a7: **BREAKING** Following `NotificationTemplateRenderer` methods now return a Promise and **must** be awaited: `getSubject`, `getText` and `getHtml`. + + Required changes and example usage: + + ```diff + import { notificationsEmailTemplateExtensionPoint } from '@backstage/plugin-notifications-backend-module-email'; + import { Notification } from '@backstage/plugin-notifications-common'; + +import { getNotificationSubject, getNotificationTextContent, getNotificationHtmlContent } from 'my-notification-processing-library` + export const notificationsModuleEmailDecorator = createBackendModule({ + pluginId: 'notifications', + moduleId: 'email.templates', + register(reg) { + reg.registerInit({ + deps: { + emailTemplates: notificationsEmailTemplateExtensionPoint, + }, + async init({ emailTemplates }) { + emailTemplates.setTemplateRenderer({ + - getSubject(notification) { + + async getSubject(notification) { + - return `New notification from ${notification.source}`; + + const subject = await getNotificationSubject(notification); + + return `New notification from ${subject}`; + }, + - getText(notification) { + + async getText(notification) { + - return notification.content; + + const text = await getNotificationTextContent(notification); + + return text; + }, + - getHtml(notification) { + + async getHtml(notification) { + - return `

    ${notification.content}

    `; + + const html = await getNotificationHtmlContent(notification); + + return html; + }, + }); + }, + }); + }, + }); + ``` + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.7.1-next.1 + - @backstage/backend-common@0.23.4-next.1 + - @backstage/integration-aws-node@0.1.12 + - @backstage/catalog-client@1.6.5 + - @backstage/catalog-model@1.5.0 + - @backstage/config@1.2.0 + - @backstage/types@1.1.1 + - @backstage/plugin-notifications-common@0.0.5 + - @backstage/plugin-notifications-node@0.2.4-next.1 + +## 0.1.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.23.4-next.0 + - @backstage/backend-plugin-api@0.7.1-next.0 + - @backstage/catalog-client@1.6.5 + - @backstage/catalog-model@1.5.0 + - @backstage/config@1.2.0 + - @backstage/integration-aws-node@0.1.12 + - @backstage/types@1.1.1 + - @backstage/plugin-notifications-common@0.0.5 + - @backstage/plugin-notifications-node@0.2.4-next.0 + +## 0.1.3 + +### Patch Changes + +- 4e4ef2b: Move notification processor filter parsing to common package +- Updated dependencies + - @backstage/backend-plugin-api@0.7.0 + - @backstage/backend-common@0.23.3 + - @backstage/plugin-notifications-common@0.0.5 + - @backstage/plugin-notifications-node@0.2.3 + - @backstage/catalog-client@1.6.5 + - @backstage/catalog-model@1.5.0 + - @backstage/config@1.2.0 + - @backstage/integration-aws-node@0.1.12 + - @backstage/types@1.1.1 + +## 0.1.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.23.3-next.1 + - @backstage/backend-plugin-api@0.6.22-next.1 + - @backstage/catalog-client@1.6.5 + - @backstage/catalog-model@1.5.0 + - @backstage/config@1.2.0 + - @backstage/integration-aws-node@0.1.12 + - @backstage/types@1.1.1 + - @backstage/plugin-notifications-common@0.0.4 + - @backstage/plugin-notifications-node@0.2.3-next.1 + +## 0.1.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.21-next.0 + - @backstage/backend-common@0.23.2-next.0 + - @backstage/plugin-notifications-node@0.2.2-next.0 + - @backstage/catalog-client@1.6.5 + - @backstage/catalog-model@1.5.0 + - @backstage/config@1.2.0 + - @backstage/integration-aws-node@0.1.12 + - @backstage/types@1.1.1 + - @backstage/plugin-notifications-common@0.0.4 + +## 0.1.0 + +### Minor Changes + +- 07a789b: add notification filters + +### Patch Changes + +- 78a0b08: Internal refactor to handle `BackendFeature` contract change. +- d44a20a: Added additional plugin metadata to `package.json`. +- Updated dependencies + - @backstage/backend-common@0.23.0 + - @backstage/backend-plugin-api@0.6.19 + - @backstage/plugin-notifications-node@0.2.0 + - @backstage/plugin-notifications-common@0.0.4 + - @backstage/catalog-client@1.6.5 + - @backstage/catalog-model@1.5.0 + - @backstage/config@1.2.0 + - @backstage/integration-aws-node@0.1.12 + - @backstage/types@1.1.1 + +## 0.1.0-next.3 + +### Patch Changes + +- d44a20a: Added additional plugin metadata to `package.json`. +- Updated dependencies + - @backstage/backend-plugin-api@0.6.19-next.3 + - @backstage/plugin-notifications-common@0.0.4-next.0 + - @backstage/plugin-notifications-node@0.2.0-next.3 + - @backstage/backend-common@0.23.0-next.3 + - @backstage/catalog-client@1.6.5 + - @backstage/catalog-model@1.5.0 + - @backstage/config@1.2.0 + - @backstage/integration-aws-node@0.1.12 + - @backstage/types@1.1.1 + +## 0.1.0-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.19-next.2 + - @backstage/backend-common@0.23.0-next.2 + - @backstage/plugin-notifications-node@0.2.0-next.2 + - @backstage/catalog-client@1.6.5 + - @backstage/catalog-model@1.5.0 + - @backstage/config@1.2.0 + - @backstage/integration-aws-node@0.1.12 + - @backstage/types@1.1.1 + - @backstage/plugin-notifications-common@0.0.3 + +## 0.1.0-next.1 + +### Minor Changes + +- 07a789b: add notification filters + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.19-next.1 + - @backstage/plugin-notifications-node@0.2.0-next.1 + - @backstage/backend-common@0.23.0-next.1 + +## 0.0.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.22.1-next.0 + - @backstage/backend-plugin-api@0.6.19-next.0 + - @backstage/plugin-notifications-node@0.1.5-next.0 + - @backstage/catalog-client@1.6.5 + - @backstage/catalog-model@1.5.0 + - @backstage/config@1.2.0 + - @backstage/integration-aws-node@0.1.12 + - @backstage/types@1.1.1 + - @backstage/plugin-notifications-common@0.0.3 + +## 0.0.1 + +### Patch Changes + +- d541ff6: Fixed email processor `esm` issue and config reading +- e538b10: Support relative links in notifications sent via email +- dbf2696: Allow sending notifications by email with the new notifications module +- Updated dependencies + - @backstage/catalog-model@1.5.0 + - @backstage/backend-common@0.22.0 + - @backstage/plugin-notifications-node@0.1.4 + - @backstage/backend-plugin-api@0.6.18 + - @backstage/catalog-client@1.6.5 + +## 0.0.1-next.1 + +### Patch Changes + +- d541ff6: Fixed email processor `esm` issue and config reading +- e538b10: Support relative links in notifications sent via email +- Updated dependencies + - @backstage/backend-common@0.22.0-next.2 + +## 0.0.1-next.0 + +### Patch Changes + +- dbf2696: Allow sending notifications by email with the new notifications module +- Updated dependencies + - @backstage/plugin-notifications-node@0.1.4-next.1 + - @backstage/backend-common@0.22.0-next.1 + - @backstage/backend-plugin-api@0.6.18-next.1 + +## @backstage/create-app@0.7.6-next.1 + +### Patch Changes + +- Bumped create-app version. + +## @backstage/dev-utils@1.1.17-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/ui@0.9.0-next.1 + +## @backstage/plugin-mui-to-bui@0.2.1-next.1 + +### Patch Changes + +- 5c614ff: Updated BUI checkbox preview example to align with new component API. +- Updated dependencies + - @backstage/ui@0.9.0-next.1 + +## example-app@0.2.115-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/ui@0.9.0-next.1 + - @backstage/plugin-mui-to-bui@0.2.1-next.1 + +## example-app-next@0.0.29-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/ui@0.9.0-next.1 + +## techdocs-cli-embedded-app@0.2.114-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/ui@0.9.0-next.1 diff --git a/docs/releases/v1.45.0-next.2-changelog.md b/docs/releases/v1.45.0-next.2-changelog.md new file mode 100644 index 0000000000..4bac9094e7 --- /dev/null +++ b/docs/releases/v1.45.0-next.2-changelog.md @@ -0,0 +1,1448 @@ +# Release v1.45.0-next.2 + +Upgrade Helper: [https://backstage.github.io/upgrade-helper/?to=1.45.0-next.2](https://backstage.github.io/upgrade-helper/?to=1.45.0-next.2) + +## @backstage/backend-app-api@1.3.0-next.1 + +### Minor Changes + +- a17d9df: Updates API for `instanceMetadata` service to return a list of plugins not features. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + +## @backstage/backend-plugin-api@1.5.0-next.1 + +### Minor Changes + +- a17d9df: Promote `instanceMetadata` service to main entrypoint. + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-common@0.9.3-next.1 + - @backstage/plugin-auth-node@0.6.9-next.1 + - @backstage/plugin-permission-node@0.10.6-next.1 + +## @backstage/ui@0.9.0-next.2 + +### Minor Changes + +- 539cf26: **BREAKING**: Migrated Avatar component from Base UI to custom implementation with size changes: + + - Base UI-specific props are no longer supported + - Size values have been updated: + - New `x-small` size added (1.25rem / 20px) + - `small` size unchanged (1.5rem / 24px) + - `medium` size unchanged (2rem / 32px, default) + - `large` size **changed from 3rem to 2.5rem** (40px) + - New `x-large` size added (3rem / 48px) + + Migration: + + ```diff + # Remove Base UI-specific props + - + + + + # Update large size usage to x-large for same visual size + - + + + ``` + + Added `purpose` prop for accessibility control (`'informative'` or `'decoration'`). + +- 134151f: Fixing styles on SearchField in Backstage UI after migration to CSS modules. `SearchField` has now its own set of class names. We previously used class names from `TextField` but this approach was creating some confusion so going forward in your theme you'll be able to theme `TextField` and `SearchField` separately. + +### Patch Changes + +- d01de00: Fix broken external links in Backstage UI Header component. +- deaa427: Fixed Text component to prevent `truncate` prop from being spread to the underlying DOM element. +- 1059f95: Improved the Link component structure in Backstage UI. +- 6874094: Migrated CellProfile component from Base UI Avatar to Backstage UI Avatar component. +- 719d772: Avatar components in x-small and small sizes now display only one initial instead of two, improving readability at smaller dimensions. +- 3b18d80: Fixed RadioGroup radio button ellipse distortion by preventing flex shrink and grow. +- e16ece5: Set the color-scheme property depending on theme + +## @backstage/plugin-catalog@1.32.0-next.1 + +### Minor Changes + +- a521911: Add support for customizable icons in `SearchResultListItemBlueprint` and related components + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-react@1.10.0-next.1 + - @backstage/plugin-catalog-react@1.21.3-next.1 + - @backstage/core-components@0.18.3-next.1 + - @backstage/core-plugin-api@1.11.2-next.1 + - @backstage/frontend-plugin-api@0.12.2-next.1 + - @backstage/core-compat-api@0.5.4-next.0 + +## @backstage/plugin-catalog-backend@3.2.0-next.1 + +### Minor Changes + +- 2d229b2: Enable YAML merge keys in yamlPlaceholderResolver +- 9d3ec06: Make YAML merge (<<:) support configurable in the Backstage Catalog instead of always being enabled +- 8c26af4: Enable YAML merge keys in yamlPlaceholderResolver + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.20.0-next.1 + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-permission-common@0.9.3-next.1 + - @backstage/backend-openapi-utils@0.6.3-next.1 + - @backstage/plugin-events-node@0.4.17-next.1 + - @backstage/plugin-permission-node@0.10.6-next.1 + +## @backstage/plugin-catalog-backend-module-ldap@0.12.0-next.1 + +### Minor Changes + +- 980f240: Moved from `ldapjs` dependency to `ldapts` + + ### Breaking Changes + + **Type Migration** + + Custom transformers must now accept `Entry` from ldapts instead of `SearchEntry` + from ldapjs The Entry type provides direct property access without need for + `.object()` or `.raw()` methods. + + If you have custom user or group transformers, update the signature from: + + ```typescript + (vendor: LdapVendor, config: UserConfig, entry: SearchEntry) => + Promise; + ``` + + to + + ```typescript + (vendor: LdapVendor, config: UserConfig, entry: Entry) => + Promise; + ``` + + **Search Options** + + Updated LDAP search configuration `typesOnly: false` → `attributeValues: true` + This inverts the boolean logic: ldapjs used negative form while ldapts uses + positive form. Both achieve the same result: retrieving attribute values rather + than just attribute names. + + Update LDAP search options in configuration from + + ```yaml + options: + typesOnly: false + ``` + + to + + ```yaml + options: + attributeValues: true + ``` + + **API Changes** Removed `LdapClient.searchStreaming()` method. Users should + migrate to `LdapClient.search()` instead + + If you're using `searchStreaming` directly: + + ```typescript + // Before + await client.searchStreaming(dn, options, async entry => { + // process each entry + }); + + // After + const entries = await client.search(dn, options); + for (const entry of entries) { + // process each entry + } + ``` + + > **_NOTE:_**: Both methods have always loaded all entries into memory. The + > searchStreaming method was only needed internally to handle ldapjs's + > event-based API. + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.20.0-next.1 + - @backstage/backend-plugin-api@1.5.0-next.1 + +## @backstage/plugin-catalog-node@1.20.0-next.1 + +### Minor Changes + +- 9d3ec06: Make YAML merge (<<:) support configurable in the Backstage Catalog instead of always being enabled +- 8c26af4: Enable YAML merge keys in yamlPlaceholderResolver + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-permission-common@0.9.3-next.1 + - @backstage/plugin-permission-node@0.10.6-next.1 + +## @backstage/plugin-gateway-backend@1.1.0-next.1 + +### Minor Changes + +- a17d9df: Update usage of the `instanceMetadata` service. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + +## @backstage/plugin-scaffolder-backend-module-gitlab@0.10.0-next.1 + +### Minor Changes + +- ff96d7e: fix scaffolder action createDeployToken to allow usage of oauth tokens + +### Patch Changes + +- b2bef92: Convert all enums to erasable-syntax compliant patterns +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-scaffolder-node@0.12.1-next.1 + +## @backstage/plugin-search@1.5.0-next.1 + +### Minor Changes + +- a521911: Add support for customizable icons in `SearchResultListItemBlueprint` and related components + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-react@1.10.0-next.1 + - @backstage/plugin-catalog-react@1.21.3-next.1 + - @backstage/core-components@0.18.3-next.1 + - @backstage/core-plugin-api@1.11.2-next.1 + - @backstage/frontend-plugin-api@0.12.2-next.1 + - @backstage/core-compat-api@0.5.4-next.0 + +## @backstage/plugin-search-react@1.10.0-next.1 + +### Minor Changes + +- a521911: Add support for customizable icons in `SearchResultListItemBlueprint` and related components + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.3-next.1 + - @backstage/core-plugin-api@1.11.2-next.1 + - @backstage/frontend-plugin-api@0.12.2-next.1 + +## @backstage/plugin-techdocs@1.16.0-next.1 + +### Minor Changes + +- a521911: Add support for customizable icons in `SearchResultListItemBlueprint` and related components + +### Patch Changes + +- 71c22f3: Removed/moved unused dependencies +- Updated dependencies + - @backstage/plugin-search-react@1.10.0-next.1 + - @backstage/plugin-catalog-react@1.21.3-next.1 + - @backstage/core-components@0.18.3-next.1 + - @backstage/core-plugin-api@1.11.2-next.1 + - @backstage/frontend-plugin-api@0.12.2-next.1 + - @backstage/core-compat-api@0.5.4-next.0 + +## @backstage/backend-defaults@0.13.1-next.1 + +### Patch Changes + +- 91ab2eb: Fix a bug in the Gitlab URL reader where `search` did not handle multiple globs +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/backend-app-api@1.3.0-next.1 + - @backstage/plugin-auth-node@0.6.9-next.1 + - @backstage/plugin-events-node@0.4.17-next.1 + - @backstage/plugin-permission-node@0.10.6-next.1 + +## @backstage/backend-dynamic-feature-service@0.7.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@3.2.0-next.1 + - @backstage/backend-defaults@0.13.1-next.1 + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-permission-common@0.9.3-next.1 + - @backstage/backend-openapi-utils@0.6.3-next.1 + - @backstage/plugin-app-node@0.1.39-next.1 + - @backstage/plugin-auth-node@0.6.9-next.1 + - @backstage/plugin-events-backend@0.5.8-next.1 + - @backstage/plugin-events-node@0.4.17-next.1 + - @backstage/plugin-permission-node@0.10.6-next.1 + - @backstage/plugin-scaffolder-node@0.12.1-next.1 + - @backstage/plugin-search-backend-node@1.3.17-next.1 + +## @backstage/backend-openapi-utils@0.6.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + +## @backstage/backend-test-utils@1.10.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.13.1-next.1 + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/backend-app-api@1.3.0-next.1 + - @backstage/plugin-permission-common@0.9.3-next.1 + - @backstage/plugin-auth-node@0.6.9-next.1 + - @backstage/plugin-events-node@0.4.17-next.1 + +## @backstage/cli@0.34.5-next.1 + +### Patch Changes + +- da19cb5: Fix inconsistent behavior in the `new` command for the `@internal` scope: it now consistently defaults to the `backstage-plugin-` infix whether the `--scope` option is not set or it's set to `internal`. +- b2bef92: Convert all enums to erasable-syntax compliant patterns + +## @backstage/core-app-api@1.19.2-next.1 + +### Patch Changes + +- b2bef92: Convert all enums to erasable-syntax compliant patterns +- Updated dependencies + - @backstage/core-plugin-api@1.11.2-next.1 + +## @backstage/core-components@0.18.3-next.1 + +### Patch Changes + +- b2bef92: Convert all enums to erasable-syntax compliant patterns +- Updated dependencies + - @backstage/core-plugin-api@1.11.2-next.1 + +## @backstage/core-plugin-api@1.11.2-next.1 + +### Patch Changes + +- b2bef92: Convert all enums to erasable-syntax compliant patterns + +## @backstage/create-app@0.7.6-next.2 + +### Patch Changes + +- 9f939a6: Added `@backstage/plugin-app-visualizer` to the app in the `--next` template. + +## @backstage/frontend-plugin-api@0.12.2-next.1 + +### Patch Changes + +- 878c251: Updated to `ExtensionInput` to make all type parameters optional. +- Updated dependencies + - @backstage/core-components@0.18.3-next.1 + - @backstage/core-plugin-api@1.11.2-next.1 + +## @backstage/repo-tools@0.15.4-next.1 + +### Patch Changes + +- 8f56eae: Updated knip-reports to detect dependencies in dev/alpha pattern +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + +## @backstage/plugin-api-docs@0.13.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog@1.32.0-next.1 + - @backstage/plugin-catalog-react@1.21.3-next.1 + - @backstage/core-components@0.18.3-next.1 + - @backstage/core-plugin-api@1.11.2-next.1 + - @backstage/frontend-plugin-api@0.12.2-next.1 + - @backstage/core-compat-api@0.5.4-next.0 + +## @backstage/plugin-app-backend@0.5.8-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-app-node@0.1.39-next.1 + - @backstage/plugin-auth-node@0.6.9-next.1 + +## @backstage/plugin-app-node@0.1.39-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + +## @backstage/plugin-app-visualizer@0.1.25-next.1 + +### Patch Changes + +- e81b3f0: Improve tree visualizer to use a horizontal layout and fill the content space. +- Updated dependencies + - @backstage/core-components@0.18.3-next.1 + - @backstage/core-plugin-api@1.11.2-next.1 + - @backstage/frontend-plugin-api@0.12.2-next.1 + +## @backstage/plugin-auth-backend@0.25.6-next.1 + +### Patch Changes + +- 51ff7d8: Allow configuring dynamic client registration token expiration with config `auth.experimentalDynamicClientRegistration.tokenExpiration`. + + Maximum expiration for the DCR token is 24 hours. Default expiration is 1 hour. + +- Updated dependencies + - @backstage/plugin-catalog-node@1.20.0-next.1 + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-auth-node@0.6.9-next.1 + +## @backstage/plugin-auth-backend-module-atlassian-provider@0.4.9-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-auth-node@0.6.9-next.1 + +## @backstage/plugin-auth-backend-module-auth0-provider@0.2.9-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-auth-node@0.6.9-next.1 + +## @backstage/plugin-auth-backend-module-aws-alb-provider@0.4.9-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-auth-backend@0.25.6-next.1 + - @backstage/plugin-auth-node@0.6.9-next.1 + +## @backstage/plugin-auth-backend-module-azure-easyauth-provider@0.2.14-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-auth-node@0.6.9-next.1 + +## @backstage/plugin-auth-backend-module-bitbucket-provider@0.3.9-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-auth-node@0.6.9-next.1 + +## @backstage/plugin-auth-backend-module-bitbucket-server-provider@0.2.9-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-auth-node@0.6.9-next.1 + +## @backstage/plugin-auth-backend-module-cloudflare-access-provider@0.4.9-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-auth-node@0.6.9-next.1 + +## @backstage/plugin-auth-backend-module-gcp-iap-provider@0.4.9-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-auth-node@0.6.9-next.1 + +## @backstage/plugin-auth-backend-module-github-provider@0.3.9-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-auth-node@0.6.9-next.1 + +## @backstage/plugin-auth-backend-module-gitlab-provider@0.3.9-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-auth-node@0.6.9-next.1 + +## @backstage/plugin-auth-backend-module-google-provider@0.3.9-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-auth-node@0.6.9-next.1 + +## @backstage/plugin-auth-backend-module-guest-provider@0.2.14-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-auth-node@0.6.9-next.1 + +## @backstage/plugin-auth-backend-module-microsoft-provider@0.3.9-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-auth-node@0.6.9-next.1 + +## @backstage/plugin-auth-backend-module-oauth2-provider@0.4.9-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-auth-node@0.6.9-next.1 + +## @backstage/plugin-auth-backend-module-oauth2-proxy-provider@0.2.14-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-auth-node@0.6.9-next.1 + +## @backstage/plugin-auth-backend-module-oidc-provider@0.4.9-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-auth-backend@0.25.6-next.1 + - @backstage/plugin-auth-node@0.6.9-next.1 + +## @backstage/plugin-auth-backend-module-okta-provider@0.2.9-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-auth-node@0.6.9-next.1 + +## @backstage/plugin-auth-backend-module-onelogin-provider@0.3.9-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-auth-node@0.6.9-next.1 + +## @backstage/plugin-auth-backend-module-openshift-provider@0.1.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-auth-node@0.6.9-next.1 + +## @backstage/plugin-auth-backend-module-pinniped-provider@0.3.9-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-auth-node@0.6.9-next.1 + +## @backstage/plugin-auth-backend-module-vmware-cloud-provider@0.5.9-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-auth-node@0.6.9-next.1 + +## @backstage/plugin-auth-node@0.6.9-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + +## @backstage/plugin-catalog-backend-module-aws@0.4.17-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.20.0-next.1 + - @backstage/backend-defaults@0.13.1-next.1 + - @backstage/backend-plugin-api@1.5.0-next.1 + +## @backstage/plugin-catalog-backend-module-azure@0.3.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.20.0-next.1 + - @backstage/backend-plugin-api@1.5.0-next.1 + +## @backstage/plugin-catalog-backend-module-backstage-openapi@0.5.8-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.20.0-next.1 + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/backend-openapi-utils@0.6.3-next.1 + +## @backstage/plugin-catalog-backend-module-bitbucket-cloud@0.5.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.20.0-next.1 + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-events-node@0.4.17-next.1 + +## @backstage/plugin-catalog-backend-module-bitbucket-server@0.5.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.20.0-next.1 + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-events-node@0.4.17-next.1 + +## @backstage/plugin-catalog-backend-module-gcp@0.3.14-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.20.0-next.1 + - @backstage/backend-plugin-api@1.5.0-next.1 + +## @backstage/plugin-catalog-backend-module-gerrit@0.3.8-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.20.0-next.1 + - @backstage/backend-plugin-api@1.5.0-next.1 + +## @backstage/plugin-catalog-backend-module-gitea@0.1.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.20.0-next.1 + - @backstage/backend-plugin-api@1.5.0-next.1 + +## @backstage/plugin-catalog-backend-module-github@0.11.2-next.1 + +### Patch Changes + +- 999d1c1: Added configurable `pageSizes` for GitHub GraphQL API queries to prevent `RESOURCE_LIMITS_EXCEEDED` errors with organizations with large number of repositories. Please see the [GitHub Discovery documentation](https://backstage.io/docs/integrations/github/discovery#configuration) for new configuration options. +- Updated dependencies + - @backstage/plugin-catalog-node@1.20.0-next.1 + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-events-node@0.4.17-next.1 + +## @backstage/plugin-catalog-backend-module-github-org@0.3.16-next.1 + +### Patch Changes + +- 999d1c1: Added configurable `pageSizes` for GitHub GraphQL API queries to prevent `RESOURCE_LIMITS_EXCEEDED` errors with organizations with large number of teams and members. Please see the [GitHub Org Data documentation](https://backstage.io/docs/integrations/github/org#configuration-details) for new configuration options. +- Updated dependencies + - @backstage/plugin-catalog-node@1.20.0-next.1 + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-catalog-backend-module-github@0.11.2-next.1 + - @backstage/plugin-events-node@0.4.17-next.1 + +## @backstage/plugin-catalog-backend-module-gitlab@0.7.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.20.0-next.1 + - @backstage/backend-defaults@0.13.1-next.1 + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-events-node@0.4.17-next.1 + +## @backstage/plugin-catalog-backend-module-gitlab-org@0.2.15-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.20.0-next.1 + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-catalog-backend-module-gitlab@0.7.5-next.1 + - @backstage/plugin-events-node@0.4.17-next.1 + +## @backstage/plugin-catalog-backend-module-incremental-ingestion@0.7.6-next.1 + +### Patch Changes + +- 70745c5: Correctly handle entity removal computation when DB count query returns string +- Updated dependencies + - @backstage/plugin-catalog-backend@3.2.0-next.1 + - @backstage/plugin-catalog-node@1.20.0-next.1 + - @backstage/backend-defaults@0.13.1-next.1 + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-permission-common@0.9.3-next.1 + - @backstage/plugin-events-node@0.4.17-next.1 + +## @backstage/plugin-catalog-backend-module-logs@0.1.16-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@3.2.0-next.1 + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-events-node@0.4.17-next.1 + +## @backstage/plugin-catalog-backend-module-msgraph@0.8.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.20.0-next.1 + - @backstage/backend-plugin-api@1.5.0-next.1 + +## @backstage/plugin-catalog-backend-module-openapi@0.2.16-next.1 + +### Patch Changes + +- a5bcb2a: fix wrong dereferencing for AsyncApi 3 documents +- Updated dependencies + - @backstage/plugin-catalog-node@1.20.0-next.1 + - @backstage/backend-plugin-api@1.5.0-next.1 + +## @backstage/plugin-catalog-backend-module-puppetdb@0.2.16-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.20.0-next.1 + - @backstage/backend-plugin-api@1.5.0-next.1 + +## @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.2.14-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.20.0-next.1 + - @backstage/backend-plugin-api@1.5.0-next.1 + +## @backstage/plugin-catalog-backend-module-unprocessed@0.6.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.20.0-next.1 + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-permission-common@0.9.3-next.1 + - @backstage/plugin-auth-node@0.6.9-next.1 + +## @backstage/plugin-catalog-graph@0.5.3-next.1 + +### Patch Changes + +- b2bef92: Convert all enums to erasable-syntax compliant patterns +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.3-next.1 + - @backstage/core-components@0.18.3-next.1 + - @backstage/core-plugin-api@1.11.2-next.1 + - @backstage/frontend-plugin-api@0.12.2-next.1 + - @backstage/core-compat-api@0.5.4-next.0 + +## @backstage/plugin-catalog-react@1.21.3-next.1 + +### Patch Changes + +- 2b7924b: Apply default ordering of templates +- Updated dependencies + - @backstage/plugin-permission-common@0.9.3-next.1 + - @backstage/core-components@0.18.3-next.1 + - @backstage/core-plugin-api@1.11.2-next.1 + - @backstage/frontend-plugin-api@0.12.2-next.1 + - @backstage/core-compat-api@0.5.4-next.0 + +## @backstage/plugin-devtools-backend@0.5.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.13.1-next.1 + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-permission-common@0.9.3-next.1 + - @backstage/plugin-devtools-common@0.1.19-next.1 + - @backstage/plugin-permission-node@0.10.6-next.1 + +## @backstage/plugin-devtools-common@0.1.19-next.1 + +### Patch Changes + +- b2bef92: Convert all enums to erasable-syntax compliant patterns +- Updated dependencies + - @backstage/plugin-permission-common@0.9.3-next.1 + +## @backstage/plugin-events-backend@0.5.8-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/backend-openapi-utils@0.6.3-next.1 + - @backstage/plugin-events-node@0.4.17-next.1 + +## @backstage/plugin-events-backend-module-aws-sqs@0.4.17-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-events-node@0.4.17-next.1 + +## @backstage/plugin-events-backend-module-azure@0.2.26-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-events-node@0.4.17-next.1 + +## @backstage/plugin-events-backend-module-bitbucket-cloud@0.2.26-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-events-node@0.4.17-next.1 + +## @backstage/plugin-events-backend-module-bitbucket-server@0.1.7-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-events-node@0.4.17-next.1 + +## @backstage/plugin-events-backend-module-gerrit@0.2.26-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-events-node@0.4.17-next.1 + +## @backstage/plugin-events-backend-module-github@0.4.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-events-node@0.4.17-next.1 + +## @backstage/plugin-events-backend-module-gitlab@0.3.7-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-events-node@0.4.17-next.1 + +## @backstage/plugin-events-backend-module-google-pubsub@0.1.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-events-node@0.4.17-next.1 + +## @backstage/plugin-events-backend-module-kafka@0.1.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-events-node@0.4.17-next.1 + +## @backstage/plugin-events-node@0.4.17-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + +## @backstage/plugin-kubernetes-backend@0.20.4-next.1 + +### Patch Changes + +- 71c22f3: Removed/moved unused dependencies +- Updated dependencies + - @backstage/plugin-catalog-node@1.20.0-next.1 + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-permission-common@0.9.3-next.1 + - @backstage/plugin-kubernetes-node@0.3.6-next.1 + - @backstage/plugin-permission-node@0.10.6-next.1 + +## @backstage/plugin-kubernetes-node@0.3.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + +## @backstage/plugin-mcp-actions-backend@0.1.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.20.0-next.1 + - @backstage/backend-defaults@0.13.1-next.1 + - @backstage/backend-plugin-api@1.5.0-next.1 + +## @backstage/plugin-notifications-backend@0.5.12-next.1 + +### Patch Changes + +- 15fb764: Show default settings for notifications even before receiving first notification. + + Previously, it was not possible for the users to see or modify their notification settings until they had received at + least one notification from specific origin or topic. + This update ensures that default settings are displayed from the outset, + allowing users to customize their preferences immediately. + +- Updated dependencies + - @backstage/plugin-catalog-node@1.20.0-next.1 + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-notifications-node@0.2.21-next.1 + - @backstage/plugin-signals-node@0.1.26-next.1 + +## @backstage/plugin-notifications-backend-module-email@0.3.16-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.20.0-next.1 + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-notifications-node@0.2.21-next.1 + +## @backstage/plugin-notifications-backend-module-slack@0.2.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.20.0-next.1 + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-notifications-node@0.2.21-next.1 + +## @backstage/plugin-notifications-node@0.2.21-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-signals-node@0.1.26-next.1 + +## @backstage/plugin-permission-backend@0.7.6-next.1 + +### Patch Changes + +- b2bef92: Convert all enums to erasable-syntax compliant patterns +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-permission-common@0.9.3-next.1 + - @backstage/plugin-auth-node@0.6.9-next.1 + - @backstage/plugin-permission-node@0.10.6-next.1 + +## @backstage/plugin-permission-backend-module-allow-all-policy@0.2.14-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-permission-common@0.9.3-next.1 + - @backstage/plugin-auth-node@0.6.9-next.1 + - @backstage/plugin-permission-node@0.10.6-next.1 + +## @backstage/plugin-permission-common@0.9.3-next.1 + +### Patch Changes + +- b2bef92: Convert all enums to erasable-syntax compliant patterns + +## @backstage/plugin-permission-node@0.10.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-permission-common@0.9.3-next.1 + - @backstage/plugin-auth-node@0.6.9-next.1 + +## @backstage/plugin-proxy-backend@0.6.8-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-proxy-node@0.1.10-next.1 + +## @backstage/plugin-proxy-node@0.1.10-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + +## @backstage/plugin-scaffolder-backend@3.0.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.20.0-next.1 + - @backstage/backend-defaults@0.13.1-next.1 + - @backstage/plugin-scaffolder-backend-module-gitlab@0.10.0-next.1 + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-permission-common@0.9.3-next.1 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.2.14-next.1 + - @backstage/plugin-scaffolder-backend-module-github@0.9.2-next.1 + - @backstage/backend-openapi-utils@0.6.3-next.1 + - @backstage/plugin-auth-node@0.6.9-next.1 + - @backstage/plugin-events-node@0.4.17-next.1 + - @backstage/plugin-permission-node@0.10.6-next.1 + - @backstage/plugin-scaffolder-backend-module-azure@0.2.15-next.1 + - @backstage/plugin-scaffolder-backend-module-bitbucket@0.3.16-next.1 + - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.15-next.1 + - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.15-next.1 + - @backstage/plugin-scaffolder-backend-module-gerrit@0.2.15-next.1 + - @backstage/plugin-scaffolder-backend-module-gitea@0.2.15-next.1 + - @backstage/plugin-scaffolder-node@0.12.1-next.1 + +## @backstage/plugin-scaffolder-backend-module-azure@0.2.15-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-scaffolder-node@0.12.1-next.1 + +## @backstage/plugin-scaffolder-backend-module-bitbucket@0.3.16-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.15-next.1 + - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.15-next.1 + - @backstage/plugin-scaffolder-node@0.12.1-next.1 + +## @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.15-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-scaffolder-node@0.12.1-next.1 + +## @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.15-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-scaffolder-node@0.12.1-next.1 + +## @backstage/plugin-scaffolder-backend-module-confluence-to-markdown@0.3.15-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-scaffolder-node@0.12.1-next.1 + +## @backstage/plugin-scaffolder-backend-module-cookiecutter@0.3.17-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.13.1-next.1 + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-scaffolder-node@0.12.1-next.1 + +## @backstage/plugin-scaffolder-backend-module-gcp@0.2.15-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-scaffolder-node@0.12.1-next.1 + +## @backstage/plugin-scaffolder-backend-module-gerrit@0.2.15-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-scaffolder-node@0.12.1-next.1 + +## @backstage/plugin-scaffolder-backend-module-gitea@0.2.15-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-scaffolder-node@0.12.1-next.1 + +## @backstage/plugin-scaffolder-backend-module-github@0.9.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.20.0-next.1 + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-scaffolder-node@0.12.1-next.1 + +## @backstage/plugin-scaffolder-backend-module-notifications@0.1.16-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-notifications-node@0.2.21-next.1 + - @backstage/plugin-scaffolder-node@0.12.1-next.1 + +## @backstage/plugin-scaffolder-backend-module-rails@0.5.15-next.1 + +### Patch Changes + +- b2bef92: Convert all enums to erasable-syntax compliant patterns +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-scaffolder-node@0.12.1-next.1 + +## @backstage/plugin-scaffolder-backend-module-sentry@0.2.15-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-scaffolder-node@0.12.1-next.1 + +## @backstage/plugin-scaffolder-backend-module-yeoman@0.4.16-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-scaffolder-node@0.12.1-next.1 + - @backstage/plugin-scaffolder-node-test-utils@0.3.5-next.1 + +## @backstage/plugin-scaffolder-node@0.12.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-permission-common@0.9.3-next.1 + +## @backstage/plugin-scaffolder-node-test-utils@0.3.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/backend-test-utils@1.10.0-next.1 + - @backstage/plugin-scaffolder-node@0.12.1-next.1 + +## @backstage/plugin-search-backend@2.0.8-next.1 + +### Patch Changes + +- b2bef92: Convert all enums to erasable-syntax compliant patterns +- Updated dependencies + - @backstage/backend-defaults@0.13.1-next.1 + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-permission-common@0.9.3-next.1 + - @backstage/backend-openapi-utils@0.6.3-next.1 + - @backstage/plugin-permission-node@0.10.6-next.1 + - @backstage/plugin-search-backend-node@1.3.17-next.1 + +## @backstage/plugin-search-backend-module-catalog@0.3.10-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.20.0-next.1 + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-permission-common@0.9.3-next.1 + - @backstage/plugin-search-backend-node@1.3.17-next.1 + +## @backstage/plugin-search-backend-module-elasticsearch@1.7.8-next.1 + +### Patch Changes + +- 71c22f3: Removed/moved unused dependencies +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-search-backend-node@1.3.17-next.1 + +## @backstage/plugin-search-backend-module-explore@0.3.9-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-search-backend-node@1.3.17-next.1 + +## @backstage/plugin-search-backend-module-pg@0.5.50-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-search-backend-node@1.3.17-next.1 + +## @backstage/plugin-search-backend-module-stack-overflow-collator@0.3.15-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-search-backend-node@1.3.17-next.1 + +## @backstage/plugin-search-backend-module-techdocs@0.4.8-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.20.0-next.1 + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-permission-common@0.9.3-next.1 + - @backstage/plugin-search-backend-node@1.3.17-next.1 + - @backstage/plugin-techdocs-node@1.13.9-next.1 + +## @backstage/plugin-search-backend-node@1.3.17-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-permission-common@0.9.3-next.1 + +## @backstage/plugin-signals@0.0.25-next.1 + +### Patch Changes + +- 71c22f3: Removed/moved unused dependencies +- f0f006e: Fixes a bug where the `SignalClient` would try to subscribe to the same channel twice after an error, instead of just once. +- Updated dependencies + - @backstage/core-components@0.18.3-next.1 + - @backstage/core-plugin-api@1.11.2-next.1 + - @backstage/frontend-plugin-api@0.12.2-next.1 + - @backstage/core-compat-api@0.5.4-next.0 + +## @backstage/plugin-signals-backend@0.3.10-next.1 + +### Patch Changes + +- 71c22f3: Removed/moved unused dependencies +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-events-node@0.4.17-next.1 + - @backstage/plugin-signals-node@0.1.26-next.1 + +## @backstage/plugin-signals-node@0.1.26-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-auth-node@0.6.9-next.1 + - @backstage/plugin-events-node@0.4.17-next.1 + +## @backstage/plugin-techdocs-addons-test-utils@1.1.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-techdocs@1.16.0-next.1 + - @backstage/plugin-search-react@1.10.0-next.1 + - @backstage/plugin-catalog@1.32.0-next.1 + - @backstage/plugin-catalog-react@1.21.3-next.1 + - @backstage/core-plugin-api@1.11.2-next.1 + - @backstage/core-app-api@1.19.2-next.1 + +## @backstage/plugin-techdocs-backend@2.1.2-next.1 + +### Patch Changes + +- 71c22f3: Removed/moved unused dependencies +- Updated dependencies + - @backstage/plugin-catalog-node@1.20.0-next.1 + - @backstage/backend-defaults@0.13.1-next.1 + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-techdocs-node@1.13.9-next.1 + +## @backstage/plugin-techdocs-module-addons-contrib@1.1.30-next.1 + +### Patch Changes + +- 6929480: ExpandableCollapse Techdocs Addon was breaking native sidebar collapse on Firefox +- Updated dependencies + - @backstage/core-components@0.18.3-next.1 + - @backstage/core-plugin-api@1.11.2-next.1 + - @backstage/frontend-plugin-api@0.12.2-next.1 + +## @backstage/plugin-techdocs-node@1.13.9-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + +## @backstage/plugin-user-settings-backend@0.3.8-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.13.1-next.1 + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-auth-node@0.6.9-next.1 + - @backstage/plugin-signals-node@0.1.26-next.1 + +## example-app@0.2.115-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/ui@0.9.0-next.2 + - @backstage/plugin-techdocs@1.16.0-next.1 + - @backstage/plugin-signals@0.0.25-next.1 + - @backstage/plugin-search-react@1.10.0-next.1 + - @backstage/plugin-catalog@1.32.0-next.1 + - @backstage/plugin-search@1.5.0-next.1 + - @backstage/cli@0.34.5-next.1 + - @backstage/plugin-techdocs-module-addons-contrib@1.1.30-next.1 + - @backstage/plugin-catalog-react@1.21.3-next.1 + - @backstage/core-components@0.18.3-next.1 + - @backstage/core-plugin-api@1.11.2-next.1 + - @backstage/core-app-api@1.19.2-next.1 + - @backstage/plugin-catalog-graph@0.5.3-next.1 + - @backstage/plugin-scaffolder@1.34.3-next.0 + - @backstage/plugin-api-docs@0.13.1-next.1 + - @backstage/plugin-catalog-import@0.13.7-next.0 + - @backstage/plugin-org@0.6.46-next.0 + - @backstage/plugin-scaffolder-react@1.19.3-next.0 + - @backstage/plugin-user-settings@0.8.29-next.0 + +## example-app-next@0.0.29-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/ui@0.9.0-next.2 + - @backstage/plugin-techdocs@1.16.0-next.1 + - @backstage/plugin-signals@0.0.25-next.1 + - @backstage/plugin-search-react@1.10.0-next.1 + - @backstage/plugin-catalog@1.32.0-next.1 + - @backstage/plugin-search@1.5.0-next.1 + - @backstage/plugin-app-visualizer@0.1.25-next.1 + - @backstage/cli@0.34.5-next.1 + - @backstage/plugin-techdocs-module-addons-contrib@1.1.30-next.1 + - @backstage/plugin-catalog-react@1.21.3-next.1 + - @backstage/core-components@0.18.3-next.1 + - @backstage/core-plugin-api@1.11.2-next.1 + - @backstage/core-app-api@1.19.2-next.1 + - @backstage/plugin-catalog-graph@0.5.3-next.1 + - @backstage/frontend-plugin-api@0.12.2-next.1 + - @backstage/plugin-scaffolder@1.34.3-next.0 + - @backstage/core-compat-api@0.5.4-next.0 + - @backstage/plugin-api-docs@0.13.1-next.1 + - @backstage/plugin-catalog-import@0.13.7-next.0 + - @backstage/plugin-org@0.6.46-next.0 + - @backstage/plugin-scaffolder-react@1.19.3-next.0 + - @backstage/plugin-user-settings@0.8.29-next.0 + +## example-backend@0.0.44-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@3.2.0-next.1 + - @backstage/plugin-search-backend-module-elasticsearch@1.7.8-next.1 + - @backstage/plugin-kubernetes-backend@0.20.4-next.1 + - @backstage/plugin-techdocs-backend@2.1.2-next.1 + - @backstage/plugin-signals-backend@0.3.10-next.1 + - @backstage/backend-defaults@0.13.1-next.1 + - @backstage/plugin-notifications-backend@0.5.12-next.1 + - @backstage/plugin-catalog-backend-module-openapi@0.2.16-next.1 + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-permission-backend@0.7.6-next.1 + - @backstage/plugin-permission-common@0.9.3-next.1 + - @backstage/plugin-search-backend@2.0.8-next.1 + - @backstage/plugin-auth-backend@0.25.6-next.1 + - @backstage/plugin-catalog-backend-module-backstage-openapi@0.5.8-next.1 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.2.14-next.1 + - @backstage/plugin-catalog-backend-module-unprocessed@0.6.6-next.1 + - @backstage/plugin-mcp-actions-backend@0.1.5-next.1 + - @backstage/plugin-scaffolder-backend@3.0.1-next.1 + - @backstage/plugin-scaffolder-backend-module-github@0.9.2-next.1 + - @backstage/plugin-search-backend-module-catalog@0.3.10-next.1 + - @backstage/plugin-search-backend-module-techdocs@0.4.8-next.1 + - @backstage/plugin-app-backend@0.5.8-next.1 + - @backstage/plugin-auth-backend-module-github-provider@0.3.9-next.1 + - @backstage/plugin-auth-backend-module-guest-provider@0.2.14-next.1 + - @backstage/plugin-auth-backend-module-openshift-provider@0.1.2-next.1 + - @backstage/plugin-auth-node@0.6.9-next.1 + - @backstage/plugin-devtools-backend@0.5.11-next.1 + - @backstage/plugin-events-backend@0.5.8-next.1 + - @backstage/plugin-events-backend-module-google-pubsub@0.1.6-next.1 + - @backstage/plugin-permission-backend-module-allow-all-policy@0.2.14-next.1 + - @backstage/plugin-permission-node@0.10.6-next.1 + - @backstage/plugin-proxy-backend@0.6.8-next.1 + - @backstage/plugin-scaffolder-backend-module-notifications@0.1.16-next.1 + - @backstage/plugin-search-backend-module-explore@0.3.9-next.1 + - @backstage/plugin-search-backend-node@1.3.17-next.1 + +## techdocs-cli-embedded-app@0.2.114-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/ui@0.9.0-next.2 + - @backstage/plugin-techdocs@1.16.0-next.1 + - @backstage/plugin-catalog@1.32.0-next.1 + - @backstage/cli@0.34.5-next.1 + - @backstage/core-components@0.18.3-next.1 + - @backstage/core-plugin-api@1.11.2-next.1 + - @backstage/core-app-api@1.19.2-next.1 + +## @internal/plugin-todo-list-backend@1.0.45-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 diff --git a/docs/releases/v1.45.0-next.3-changelog.md b/docs/releases/v1.45.0-next.3-changelog.md new file mode 100644 index 0000000000..0f66b0ca65 --- /dev/null +++ b/docs/releases/v1.45.0-next.3-changelog.md @@ -0,0 +1,282 @@ +# Release v1.45.0-next.3 + +Upgrade Helper: [https://backstage.github.io/upgrade-helper/?to=1.45.0-next.3](https://backstage.github.io/upgrade-helper/?to=1.45.0-next.3) + +## @backstage/backend-plugin-api@1.5.0-next.2 + +### Minor Changes + +- 62fc2de: Explicitly mark `coreServices.rootInstanceMetadata` as a root service. + +## @backstage/repo-tools@0.16.0-next.2 + +### Minor Changes + +- 11c61f2: The `package-docs` command will now automatically use a `typedoc.json` file if one exists at the root of your project. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.2 + +## @backstage/ui@0.9.0-next.3 + +### Minor Changes + +- 83c100e: **BREAKING**: Removed `Collapsible` component. Migrate to `Accordion` or use React Aria `Disclosure`. + + ## Migration Path 1: Accordion (Opinionated Styled Component) + + Accordion provides preset styling with a similar component structure. + + ```diff + - import { Collapsible } from '@backstage/ui'; + + import { Accordion, AccordionTrigger, AccordionPanel } from '@backstage/ui'; + + - + - } /> + - Content + - + + + + + + + Content + + + ``` + + CSS classes: `.bui-CollapsibleRoot` → `.bui-Accordion`, `.bui-CollapsibleTrigger` → `.bui-AccordionTrigger` (now on heading element), `.bui-CollapsiblePanel` → `.bui-AccordionPanel` + + ## Migration Path 2: React Aria Disclosure (Full Customization) + + For custom styling without preset styles: + + ```tsx + import { Disclosure, Button, DisclosurePanel } from 'react-aria-components'; + + + + Content + ; + ``` + +- 816af0f: **BREAKING**: The `SelectProps` interface now accepts a generic type parameter for selection mode. + + Added searchable and multiple selection support to Select component. The component now accepts `searchable`, `selectionMode`, and `searchPlaceholder` props to enable filtering and multi-selection modes. + + Migration: If you're using `SelectProps` type directly, update from `SelectProps` to `SelectProps<'single' | 'multiple'>`. Component usage remains backward compatible. + +### Patch Changes + +- 35a3614: Fixed CSS issues in Select component including popover width constraints, focus outline behavior, and overflow handling. +- 01476f0: Improved visual consistency of PasswordField, SearchField, and MenuAutocomplete components. +- 836b0c7: Fixed dialog backdrop appearance in dark mode. +- 6d35a6b: Removed `@base-ui-components/react` dependency as all components now use React Aria Components. +- 7839e7b: Added `loading` prop to Button and ButtonIcon components for displaying spinner during async operations. +- a00fb88: Fixed Table Row component to properly support opening links in new tabs via right-click or Cmd+Click when using the href prop. + +## @backstage/plugin-notifications-backend@0.6.0-next.2 + +### Minor Changes + +- 87e597c: Adds support for default configuration for an entire notification channel. + This setting will also be inherited down to origins and topics while still respecting the users individual choices. + + This will be handy if you want to use a "opt-in" strategy. + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-notifications-common@0.2.0-next.1 + - @backstage/backend-plugin-api@1.5.0-next.2 + - @backstage/plugin-notifications-node@0.2.21-next.2 + +## @backstage/plugin-notifications-common@0.2.0-next.1 + +### Minor Changes + +- 87e597c: Adds support for default configuration for an entire notification channel. + This setting will also be inherited down to origins and topics while still respecting the users individual choices. + + This will be handy if you want to use a "opt-in" strategy. + +## @backstage/backend-test-utils@1.10.0-next.2 + +### Patch Changes + +- f3001fd: Tweak some of the mock services to have more precise types +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.2 + +## @backstage/core-components@0.18.3-next.2 + +### Patch Changes + +- 96ad674: Line numbers in LogViewer will not be selectable in UI anymore + +## @backstage/create-app@0.7.6-next.3 + +### Patch Changes + +- Bumped create-app version. + +## @backstage/frontend-plugin-api@0.12.2-next.2 + +### Patch Changes + +- 7c6a66d: Added support for plugin-relative `attachTo` declarations for extension definitions. This allows for the creation of extension and extension blueprints that attach to other extensions of a particular `kind` in the same plugin, rather than needing to provide the exact extension ID. This is particularly useful when wanting to provide extension blueprints with a built-in hierarchy where the extensions created from one blueprint attach to extensions created from the other blueprint, for example: + + ```ts + // kind: 'tabbed-page' + const parentPage = TabbedPageBlueprint.make({ + params: {....} + }) + // attachTo: { kind: 'tabbed-page', input: 'tabs' } + const child1 = TabContentBlueprint.make({ + name: 'tab1', + params: {....} + }) + ``` + +- Updated dependencies + - @backstage/core-components@0.18.3-next.2 + +## @backstage/plugin-app-visualizer@0.1.25-next.2 + +### Patch Changes + +- 722e2df: Migrated to use `@backstage/ui`. +- Updated dependencies + - @backstage/ui@0.9.0-next.3 + - @backstage/frontend-plugin-api@0.12.2-next.2 + - @backstage/core-components@0.18.3-next.2 + +## @backstage/plugin-catalog-graph@0.5.3-next.2 + +### Patch Changes + +- a2d7ae7: Ensure the catalog graph entity card respects the height prop so the visualization scales down properly on wide screens. +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.3-next.2 + - @backstage/frontend-plugin-api@0.12.2-next.2 + - @backstage/core-components@0.18.3-next.2 + +## @backstage/plugin-catalog-react@1.21.3-next.2 + +### Patch Changes + +- 36d7582: Added missing i18n +- Updated dependencies + - @backstage/frontend-plugin-api@0.12.2-next.2 + - @backstage/core-components@0.18.3-next.2 + +## @backstage/plugin-home@0.8.14-next.1 + +### Patch Changes + +- 2ac5d29: Allow customization of VisitList by adding optional enrichVisit, transformPathname, canSave functions to VisitsStorageApi, along with VisitDisplayProvider for colors, labels +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.3-next.2 + - @backstage/frontend-plugin-api@0.12.2-next.2 + - @backstage/core-components@0.18.3-next.2 + +## @backstage/plugin-kubernetes-backend@0.20.4-next.2 + +### Patch Changes + +- 1906d37: Updated dependency `@kubernetes/client-node` to `1.4.0`. +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.2 + - @backstage/plugin-kubernetes-common@0.9.8-next.1 + - @backstage/plugin-kubernetes-node@0.3.6-next.2 + +## @backstage/plugin-kubernetes-common@0.9.8-next.1 + +### Patch Changes + +- 1906d37: Updated dependency `@kubernetes/client-node` to `1.4.0`. + +## @backstage/plugin-kubernetes-node@0.3.6-next.2 + +### Patch Changes + +- 1906d37: Updated dependency `@kubernetes/client-node` to `1.4.0`. +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.2 + - @backstage/plugin-kubernetes-common@0.9.8-next.1 + +## @backstage/plugin-kubernetes-react@0.5.13-next.1 + +### Patch Changes + +- 1906d37: Updated dependency `@kubernetes/client-node` to `1.4.0`. +- Updated dependencies + - @backstage/plugin-kubernetes-common@0.9.8-next.1 + - @backstage/core-components@0.18.3-next.2 + +## @backstage/plugin-notifications@0.5.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-notifications-common@0.2.0-next.1 + - @backstage/frontend-plugin-api@0.12.2-next.2 + - @backstage/core-components@0.18.3-next.2 + +## @backstage/plugin-notifications-backend-module-email@0.3.16-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-notifications-common@0.2.0-next.1 + - @backstage/backend-plugin-api@1.5.0-next.2 + - @backstage/plugin-notifications-node@0.2.21-next.2 + +## @backstage/plugin-notifications-backend-module-slack@0.2.1-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-notifications-common@0.2.0-next.1 + - @backstage/backend-plugin-api@1.5.0-next.2 + - @backstage/plugin-notifications-node@0.2.21-next.2 + +## @backstage/plugin-notifications-node@0.2.21-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-notifications-common@0.2.0-next.1 + - @backstage/backend-plugin-api@1.5.0-next.2 + +## @backstage/plugin-org@0.6.46-next.1 + +### Patch Changes + +- 6db9e7e: Improved responsiveness of GroupProfileCard component +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.3-next.2 + - @backstage/frontend-plugin-api@0.12.2-next.2 + - @backstage/core-components@0.18.3-next.2 + +## @backstage/plugin-scaffolder-backend-module-notifications@0.1.16-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-notifications-common@0.2.0-next.1 + - @backstage/backend-plugin-api@1.5.0-next.2 + - @backstage/plugin-notifications-node@0.2.21-next.2 + +## example-backend@0.0.44-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-notifications-backend@0.6.0-next.2 + - @backstage/backend-plugin-api@1.5.0-next.2 + - @backstage/plugin-kubernetes-backend@0.20.4-next.2 + - @backstage/plugin-scaffolder-backend-module-notifications@0.1.16-next.2 + - @backstage/plugin-catalog-backend@3.2.0-next.1 + - @backstage/plugin-events-backend@0.5.8-next.1 + - @backstage/plugin-scaffolder-backend@3.0.1-next.1 + - @backstage/plugin-search-backend@2.0.8-next.1 diff --git a/docs/tooling/cli/03-commands.md b/docs/tooling/cli/03-commands.md index d176278655..ce8f862c98 100644 --- a/docs/tooling/cli/03-commands.md +++ b/docs/tooling/cli/03-commands.md @@ -177,11 +177,12 @@ Usage: backstage-cli package start [options] Start a package for local development Options: - --config Config files to load instead of app-config.yaml (default: []) - --role Run the command with an explicit package role - --check Enable type checking and linting if available - --inspect Enable debugger in Node.js environments - --inspect-brk Enable debugger in Node.js environments, breaking before code starts + --config Config files to load instead of app-config.yaml (default: []) + --role Run the command with an explicit package role + --check Enable type checking and linting if available + --inspect Enable debugger in Node.js environments + --inspect-brk Enable debugger in Node.js environments, breaking before code starts + --entrypoint Entry directory path (uses index file) or entry file path (without extension). Defaults to "dev" ``` ## package build diff --git a/docs/tooling/cli/04-templates.md b/docs/tooling/cli/04-templates.md index c3bfb57d11..894cc7efd0 100644 --- a/docs/tooling/cli/04-templates.md +++ b/docs/tooling/cli/04-templates.md @@ -90,6 +90,7 @@ When defining the `templates` array it will override the default set of template "@backstage/cli/templates/plugin-common-library", "@backstage/cli/templates/web-library", "@backstage/cli/templates/node-library", + "@backstage/cli/templates/catalog-provider-module", "@backstage/cli/templates/scaffolder-backend-module" ] } @@ -147,3 +148,13 @@ The `role` property in the template yaml file is used to determine what input wi | `plugin-web-library` | `pluginId` | `plugins` | none | | `plugin-node-library` | `pluginId` | `plugins` | none | | `plugin-common-library` | `pluginId` | `plugins` | none | + +## Dependency Versioning + +The `yarn new` command automatically detects if the [Backstage Yarn plugin](https://github.com/backstage/backstage/tree/master/packages/yarn-plugin) is installed in your repository and adjusts dependency versioning accordingly. + +When the Backstage Yarn plugin is installed (detected via `.yarnrc.yml`), `yarn new` will generate `backstage:^` ranges for all `@backstage/*` dependencies. This ensures that new packages use the same Backstage version as defined in your `backstage.json` file. + +When the plugin is not installed, `yarn new` uses the standard npm version ranges (e.g., `^1.0.0`) for all dependencies, maintaining backward compatibility. + +Regardless of plugin installation, `workspace:` ranges found in your `yarn.lock` file will always take precedence over both `backstage:^` and npm ranges. This ensures that packages within monorepos continue to use workspace linking when available. diff --git a/docs/tooling/package-metadata.md b/docs/tooling/package-metadata.md index ad9d5ec5a0..3911ada413 100644 --- a/docs/tooling/package-metadata.md +++ b/docs/tooling/package-metadata.md @@ -96,6 +96,32 @@ This field can be generated by the `backstage-cli repo fix --publish` command. T The presence of this field is checked by the `backstage-cli package prepack` command, which is used to prepare a package for publishing. You can read more about this requirement in the section on [metadata for published packages](#metadata-for-published-packages). +### `backstage.features` + +This field declares where to find Backstage features exported from the package. It is a map of features export paths to their feature type, similar to the `exports` field. + +```js title="Example usage of the backstage.features field" +{ + "name": "@backstage/plugin-catalog", + "backstage": { + "features": { + "./alpha": "@backstage/FrontendPlugin" + } + } +} +``` + +This field is automatically generated by the `backstage-cli package prepack` command when publishing a package and therefore does not need to be committed to the repository. + +If you are using your own tooling to publish a package, you need to populate this field manually, using the following feature types: + +- `@backstage/BackendFeature` +- `@backstage/FrontendPlugin` +- `@backstage/FrontendModule` +- `@backstage/FrontendFeatureLoader` + +You can read more about the feature types in the [backend system](../backend-system/architecture/01-index.md) and [frontend system](../frontend-system/architecture/00-index.md) documentation. + ### `backstage.pluginPackages` For any package that is part of a plugin, this field should be set to a list of all packages that are directly part of the same plugin. This includes frontend and backend plugin packages as well as related libraries, but not modules. diff --git a/docs/tutorials/manual-knex-rollback.md b/docs/tutorials/manual-knex-rollback.md index b0f67ba3e0..442a48e2f5 100644 --- a/docs/tutorials/manual-knex-rollback.md +++ b/docs/tutorials/manual-knex-rollback.md @@ -23,7 +23,7 @@ You can interact with Knex running the commands below in the project root: We want to check the migration status: ```sh -$ node_modules/.bin/knex migrate:status --connection "postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@$POSTGRES_HOST/backstage_plugin_app" --client pg --migrations-directory node_modules/@backstage/plugin-catalog-backend/migrations/ +$ node_modules/.bin/knex migrate:status --connection "postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@$POSTGRES_HOST/backstage_plugin_catalog" --client pg --migrations-directory node_modules/@backstage/plugin-catalog-backend/migrations/ Using environment: production Found 2 Completed Migration file/files. 20211229105307_init.js @@ -34,7 +34,7 @@ No Pending Migration files Found. Now lets rollback a specific migration called `20240113144027_assets-namespace.js`: ```sh -$ node_modules/.bin/knex migrate:down 20240113144027_assets-namespace.js --connection "postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@$POSTGRES_HOST/backstage_plugin_app" --client pg --migrations-directory node_modules/@backstage/plugin-catalog-backend/migrations/ +$ node_modules/.bin/knex migrate:down 20240113144027_assets-namespace.js --connection "postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@$POSTGRES_HOST/backstage_plugin_catalog" --client pg --migrations-directory node_modules/@backstage/plugin-catalog-backend/migrations/ Using environment: production Batch 2 rolled back the following migrations: 20240113144027_assets-namespace.js @@ -43,7 +43,7 @@ Batch 2 rolled back the following migrations: Now we can check the migration status again to confirm the rollback: ```sh -$ node_modules/.bin/knex migrate:status --connection "postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@$POSTGRES_HOST/backstage_plugin_app" --client pg --migrations-directory node_modules/@backstage/plugin-catalog-backend/migrations/ +$ node_modules/.bin/knex migrate:status --connection "postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@$POSTGRES_HOST/backstage_plugin_catalog" --client pg --migrations-directory node_modules/@backstage/plugin-catalog-backend/migrations/ Using environment: production Found 1 Completed Migration file/files. 20211229105307_init.js @@ -54,7 +54,7 @@ Found 1 Pending Migration file/files. Now lets use `migrate:currentVersion` which retrieves the current migration version. If there aren't any migrations run yet, it will return "none". ```sh -$ node_modules/.bin/knex migrate:currentVersion --connection "postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@$POSTGRES_HOST/backstage_plugin_app" --client pg +$ node_modules/.bin/knex migrate:currentVersion --connection "postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@$POSTGRES_HOST/backstage_plugin_catalog" --client pg Using environment: production Current Version: 20240113144027 ``` diff --git a/docs/tutorials/quickstart-app-plugin.md b/docs/tutorials/quickstart-app-plugin.md index e9b670c3ba..e9821f3f70 100644 --- a/docs/tutorials/quickstart-app-plugin.md +++ b/docs/tutorials/quickstart-app-plugin.md @@ -246,7 +246,7 @@ const auth = useApi(githubAuthApiRef); 2. The access token we need to make our GitHub request and the request itself is obtained in an asynchronous manner. -3. Add the useAsync block inside the ExampleFetchComponent +3. Add the `useAsync` block inside the ExampleFetchComponent ```tsx const { value, loading, error } = useAsync(async (): Promise => { diff --git a/microsite/blog/2025-10-15-backstage-contribfest-kubecon-guide.mdx b/microsite/blog/2025-10-15-backstage-contribfest-kubecon-guide.mdx new file mode 100644 index 0000000000..6ec142cc7c --- /dev/null +++ b/microsite/blog/2025-10-15-backstage-contribfest-kubecon-guide.mdx @@ -0,0 +1,74 @@ +--- +title: 'Get ready for Backstage ContribFest at KubeCon!' +author: André Wanlin, Spotify +authorURL: https://github.com/awanlin +authorImageURL: https://avatars.githubusercontent.com/u/67169551?v=4 +--- + +![Get ready for Backstage ContribFest at KubeCon!](assets/2025-10-15/backstage-contribfest-kubecon-guide-header.png) + +Pack your laptop and mark your calendars! Backstage will once again be taking part in the ContribFest track at KubeCon! Join us at KubeCon 2025 North America in Atlanta on Monday, November 13. Feel free to [bookmark it on your schedule](https://kccncna2025.sched.com/event/27Nl6/contribfest-level-up-your-open-source-journey-hands-on-backstage-contributions-andre-wanlin-patrik-oldsberg-emma-indal-spotify-aramis-sennyey-doordash-kurt-king-procore). Then read on to get yourself prepared for the session beforehand to maximize your time working with other contributors and Backstage experts during the session. + +{/* truncate */} + +## Contrib-what? + +Before we dive into preparation for the Backstage ContribFest session we should probably take a detour and answer the question: What the heck is ContribFest? + +ContribFest is a track at KubeCon where various CNCF projects will host hands-on sessions working with their respective communities on contributions towards their projects. You don't have to be a past contributor to participate — new community members are encouraged to join! + +These sessions are 75 minutes long and take place in a room with roughly a dozen circular tables that seat about eight people making it easy to work and collaborate. They usually lead off with some getting started steps and then give attendees the rest of the time to work on their contributions with the aid of experts from the project. + +## What to prepare before you get there + +For the Backstage ContribFest session, there are some preparation steps you can complete on your own well before the session. Let's cover those now: + +### 1. Fork the repos + +You'll have the option to contribute to the Backstage repo or the Backstage Community Plugins repo. To get those onto your system, you need to follow the GitHub ["Forking a repository"](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/fork-a-repo#forking-a-repository) guide and fork these: + +- Backstage: [https://github.com/backstage/backstage](https://github.com/backstage/backstage) +- Backstage Community Plugins: [https://github.com/backstage/community-plugins](https://github.com/backstage/community-plugins) + +### 2. Update Node.js and Yarn + +Backstage has [a few prerequisites](https://backstage.io/docs/getting-started/#prerequisites) that you'll need to have in place before you can run Backstage or the various Backstage Community Plugins. Here's what you need: + +- Backstage uses Node.js — you'll want to install version 22 for the session. + - To make this easier, we recommend you use Node Version Manager nvm, you can [follow these instructions to install it](https://github.com/nvm-sh/nvm#install--update-script). + - Once you have nvm installed, you can run this command to get Node 22 installed and activated: `nvm install 22` +- Yarn is the package manager used by Backstage — you'll want to install it as well. + + - Simply run `corepack enable` to do so. + +### 3. Test your setups + +Now let's do a quick test to confirm that everything is working. + +First, let's check that you can run the Backstage codebase: + +1. Navigate to your cloned fork of the Backstage repo +2. From the root, run `yarn install` +3. Then run `yarn tsc` +4. Finally run `yarn start` +5. Backstage will open in a new browser window or tab + +Now, let's check the Backstage Community Plugins: + +1. Navigate to your your cloned fork of the Backstage Community Plugins repo +2. This repo is structured in a way where there are many plugins that live in their own dedicated workspace — for this test, we'll use the `linguist` workspace. From the root, run `cd workspaces/linguist` +3. From here run `yarn install` +4. Then run `yarn tsc` +5. Finally run `yarn start` +6. An example Backstage app will open in a new browser window or tab + +### Bonus: Check out the contribution guides + +At this point, you have all the prerequisites in place and are ready to take part in the Backstage ContribFest session. From here, we recommend you take some time to read the contributions guides as that will get you more familiar with the overall process. Here they are: + +- [Backstage Contribution Guide](https://github.com/backstage/backstage/blob/master/CONTRIBUTING.md) +- [Backstage Community Plugins Contribution Guide](https://github.com/backstage/community-plugins/blob/main/CONTRIBUTING.md) + +## 👋 See you in Atlanta! + +On behalf of myself and the other co-hosts of Backstage ContribFest, thanks for following along! We look forward to seeing you in Atlanta and working with you on your contributions. Make sure to say hello! diff --git a/microsite/blog/assets/2025-10-15/backstage-contribfest-kubecon-guide-header.png b/microsite/blog/assets/2025-10-15/backstage-contribfest-kubecon-guide-header.png new file mode 100644 index 0000000000..23ed0309c1 Binary files /dev/null and b/microsite/blog/assets/2025-10-15/backstage-contribfest-kubecon-guide-header.png differ diff --git a/microsite/data/plugins/datadog-entity-sync.yaml b/microsite/data/plugins/datadog-entity-sync.yaml new file mode 100644 index 0000000000..2f0cd06cd5 --- /dev/null +++ b/microsite/data/plugins/datadog-entity-sync.yaml @@ -0,0 +1,10 @@ +--- +title: Datadog Entity Sync +author: datadog +authorUrl: https://www.datadoghq.com/ +category: Development +description: Sync entities from the Backstage catalog to the Datadog Software Catalog. +documentation: https://github.com/DataDog/datadog-backstage-plugins/tree/main/plugins/datadog-entity-sync-backend#installation +iconUrl: https://imgix.datadoghq.com/img/about/presskit/logo-v/dd_vertical_purple.png?auto=format&fit=max&w=847&dpr=2 +npmPackageName: '@datadog/backstage-plugin-datadog-entity-sync-backend' +addedDate: '2025-09-19' diff --git a/microsite/data/plugins/stackoverflow-teams.yaml b/microsite/data/plugins/stackoverflow-teams.yaml index c2fc1f5480..262a6e9c69 100644 --- a/microsite/data/plugins/stackoverflow-teams.yaml +++ b/microsite/data/plugins/stackoverflow-teams.yaml @@ -6,5 +6,5 @@ category: Discovery description: Provide seamless access to Stack Overflow Teams most relevant data, allowing you to display the top users, top tags, and top questions directly within Backstage. It also allows to securely create SO Teams questions from Backstage. documentation: https://stackoverflowteams.help/en/articles/9692515-backstage-io-integration iconUrl: /img/stack-overflow-logo.svg -npmPackageName: 'backstage-plugin-stack-overflow-teams' +npmPackageName: '@stackoverflow/backstage-plugin-stack-overflow-teams' addedDate: '2025-06-10' diff --git a/microsite/data/plugins/targetboard.yaml b/microsite/data/plugins/targetboard.yaml new file mode 100644 index 0000000000..2cac367eec --- /dev/null +++ b/microsite/data/plugins/targetboard.yaml @@ -0,0 +1,10 @@ +--- +title: TargetBoard +author: TargetBoard +authorUrl: https://www.targetboard.ai +category: Metrics +description: Embed TargetBoard dashboards and metric cards inside Backstage to visualize real-time engineering performance, KPIs, and delivery metrics. +documentation: https://github.com/targetboard/backstage-plugin/blob/main/README.md +iconUrl: https://app.targetboard.ai/assets/TargetBoard-Backstage-logo.svg +npmPackageName: '@targetboard/backstage-plugin' +addedDate: '2025-11-02' diff --git a/microsite/data/plugins/usage-statistics.yaml b/microsite/data/plugins/usage-statistics.yaml new file mode 100644 index 0000000000..d32fb54ac4 --- /dev/null +++ b/microsite/data/plugins/usage-statistics.yaml @@ -0,0 +1,9 @@ +--- +title: Usage Statistics +author: CodeVerse-GP +authorUrl: https://github.com/CodeVerse-GP +category: Monitoring +description: Shows usage statistics for scaffolder templates +documentation: https://github.com/CodeVerse-GP/usage-statistics/blob/main/README.md +npmPackageName: '@codeverse-gp/plugin-usage-statistics' +addedDate: '2025-11-05' diff --git a/microsite/docusaurus.config.ts b/microsite/docusaurus.config.ts index d801d41ca3..850e6058af 100644 --- a/microsite/docusaurus.config.ts +++ b/microsite/docusaurus.config.ts @@ -269,6 +269,10 @@ const config: Config = { from: '/docs/plugins/url-reader/', to: '/docs/backend-system/core-services/url-reader', }, + { + from: '/docs/getting-started/app-custom-theme', + to: '/docs/conf/user-interface', + }, ], }), [ diff --git a/microsite/sidebars.ts b/microsite/sidebars.ts index 72608f4bac..9a4f03620f 100644 --- a/microsite/sidebars.ts +++ b/microsite/sidebars.ts @@ -22,9 +22,8 @@ export default { docs: { Overview: [ 'overview/what-is-backstage', + 'overview/technical-overview', 'overview/architecture-overview', - 'overview/background', - 'overview/vision', 'overview/roadmap', 'overview/threat-model', 'overview/versioning-policy', @@ -38,7 +37,6 @@ export default { 'getting-started/config/database', 'getting-started/config/authentication', 'getting-started/configure-app-with-plugins', - 'getting-started/app-custom-theme', 'getting-started/homepage', ], }, @@ -64,6 +62,42 @@ export default { 'overview/support', 'getting-started/keeping-backstage-updated', ], + ...(process.env.GOLDEN_PATH + ? { + 'Golden Paths': [ + { + type: 'category', + label: '001 - create-app', + items: [ + 'golden-path/create-app/index', + 'golden-path/create-app/npx-create-app', + 'golden-path/create-app/local-development', + 'golden-path/create-app/installing-plugins', + 'golden-path/create-app/logging-in', + 'golden-path/create-app/custom-theme', + 'golden-path/create-app/keeping-backstage-updated', + ], + }, + { + type: 'category', + label: '002 - Plugins', + items: [ + 'golden-path/plugins/index', + 'golden-path/plugins/why-build-plugins', + 'golden-path/plugins/sustainable-plugin-development', + { + type: 'category', + label: 'Backend Plugins', + items: [ + 'golden-path/plugins/backend/001-first-steps', + 'golden-path/plugins/backend/002-poking-around', + ], + }, + ], + }, + ], + } + : {}), 'Core Features': [ { type: 'category', @@ -561,6 +595,16 @@ export default { 'tooling/package-metadata', ], }, + { + type: 'category', + label: 'User Interface', + items: [ + 'conf/user-interface/index', + 'conf/user-interface/logo', + 'conf/user-interface/icons', + 'conf/user-interface/sidebar', + ], + }, ], Tutorials: [ { 'Non-technical': ['overview/adopting'] }, diff --git a/microsite/src/theme/customTheme.scss b/microsite/src/theme/customTheme.scss index 707acb4073..823327b28f 100644 --- a/microsite/src/theme/customTheme.scss +++ b/microsite/src/theme/customTheme.scss @@ -171,6 +171,11 @@ html[data-theme='light'] { border-left: 3px solid rgb(255, 255, 255, 0.5); } +table { + width: 100%; + display: table; +} + /* #endregion */ /* For the docusaurus-pushfeedback plugin */ diff --git a/microsite/yarn.lock b/microsite/yarn.lock index 9040e75d86..20c3f39fbd 100644 --- a/microsite/yarn.lock +++ b/microsite/yarn.lock @@ -2460,14 +2460,14 @@ __metadata: linkType: hard "@mdx-js/react@npm:^3.0.0": - version: 3.1.0 - resolution: "@mdx-js/react@npm:3.1.0" + version: 3.1.1 + resolution: "@mdx-js/react@npm:3.1.1" dependencies: "@types/mdx": "npm:^2.0.0" peerDependencies: "@types/react": ">=16" react: ">=16" - checksum: 10/cf89d6392c76091622fb647f205e1ab5cbdf5edd4401dde7092138cefc9fbb6d61428aa63557de0bccca3695d5a8854dd4a93b34a27cb8e27369da7eaeaa3e73 + checksum: 10/52a740e2f37761694fa94d4704b7825084b4055616a95c8b8f4c1676190d399ddc5cdbb399ffc45b550beecd30497a7224c2e5b05bf43ecb668c7473641037d1 languageName: node linkType: hard diff --git a/mkdocs.yml b/mkdocs.yml index eea5fc9c40..45fdd2c9d6 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -28,7 +28,7 @@ nav: - Database: 'getting-started/config/database.md' - Authentication: 'getting-started/config/authentication.md' - Configuring App with plugins: 'getting-started/configure-app-with-plugins.md' - - Customize the look-and-feel of your App: 'getting-started/app-custom-theme.md' + - Customize the look-and-feel of your App: 'conf/user-interface/index.md' - Customizing your Homepage: 'getting-started/homepage.md' - Deployment: - Deploying Backstage: 'deployment/index.md' diff --git a/package.json b/package.json index b510a5a864..5039c29e2b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "root", - "version": "1.44.0-next.1", + "version": "1.45.0-next.3", "backstage": { "cli": { "new": { @@ -45,6 +45,7 @@ "lint:docs": "node ./scripts/check-docs-quality", "lint:peer-deps": "backstage-repo-tools peer-deps", "lint:type-deps": "backstage-repo-tools type-deps", + "mui-to-bui": "node scripts/mui-to-bui/backstage-migration-analytics.js", "new": "backstage-cli new", "prepare": "husky", "prettier:check": "prettier --check .", @@ -54,6 +55,7 @@ "snyk:test:package": "yarn snyk:test --include", "start": "backstage-cli repo start", "start-backend": "echo \"Use 'yarn start example-backend' instead\"", + "start:docker": "docker compose -f docker-compose.deps.yml up --wait && BACKSTAGE_ENV=docker yarn start", "start:microsite": "cd microsite/ && yarn start", "start:next": "yarn start example-app-next example-backend", "storybook": "storybook dev -p 6006", @@ -105,6 +107,7 @@ "@types/react": "^18.0.0", "@types/react-dom": "^18.0.0", "@yarnpkg/plugin-npm@npm:^3.1.0": "patch:@yarnpkg/plugin-npm@npm%3A3.1.0#~/.yarn/patches/@yarnpkg-plugin-npm-npm-3.1.0-6533d0f5a1.patch", + "GendocuPublicApis": "npm:gendocu-public-apis@^1.0.0", "ast-types@0.14.2": "patch:ast-types@npm%3A0.14.2#./.yarn/patches/ast-types-npm-0.14.2-43c4ac4b0d.patch", "ast-types@^0.14.1": "patch:ast-types@npm%3A0.14.2#./.yarn/patches/ast-types-npm-0.14.2-43c4ac4b0d.patch", "ast-types@npm:0.14.2": "patch:ast-types@npm%3A0.16.1#./.yarn/patches/ast-types-npm-0.16.1-43c4ac4b0d.patch", @@ -164,6 +167,7 @@ "sloc": "^0.3.1", "sort-package-json": "^2.8.0", "storybook": "^9.1.7", + "ts-morph": "^24.0.0", "typedoc": "^0.28.0", "typescript": "~5.7.0", "vite": "^7.1.5" diff --git a/packages/app-defaults/CHANGELOG.md b/packages/app-defaults/CHANGELOG.md index 7e0fe1b55d..fbbbdf6569 100644 --- a/packages/app-defaults/CHANGELOG.md +++ b/packages/app-defaults/CHANGELOG.md @@ -1,5 +1,35 @@ # @backstage/app-defaults +## 1.7.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-react@0.4.38-next.0 + - @backstage/core-app-api@1.19.2-next.0 + - @backstage/core-plugin-api@1.11.2-next.0 + - @backstage/core-components@0.18.3-next.0 + - @backstage/theme@0.7.0 + +## 1.7.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2 + - @backstage/theme@0.7.0 + - @backstage/core-app-api@1.19.1 + - @backstage/core-plugin-api@1.11.1 + - @backstage/plugin-permission-react@0.4.37 + +## 1.7.1-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.2 + - @backstage/theme@0.6.9-next.0 + ## 1.7.1-next.1 ### Patch Changes diff --git a/packages/app-defaults/package.json b/packages/app-defaults/package.json index 1a2cbecc12..8cd48112b2 100644 --- a/packages/app-defaults/package.json +++ b/packages/app-defaults/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/app-defaults", - "version": "1.7.1-next.1", + "version": "1.7.2-next.0", "description": "Provides the default wiring of a Backstage App", "backstage": { "role": "web-library" diff --git a/packages/app-next-example-plugin/CHANGELOG.md b/packages/app-next-example-plugin/CHANGELOG.md index 4dfaaa596d..74ba940174 100644 --- a/packages/app-next-example-plugin/CHANGELOG.md +++ b/packages/app-next-example-plugin/CHANGELOG.md @@ -1,5 +1,21 @@ # app-next-example-plugin +## 0.0.29-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.3-next.0 + - @backstage/frontend-plugin-api@0.12.2-next.0 + +## 0.0.28 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2 + - @backstage/frontend-plugin-api@0.12.1 + ## 0.0.28-next.0 ### Patch Changes diff --git a/packages/app-next-example-plugin/package.json b/packages/app-next-example-plugin/package.json index 384911f923..5415d793b3 100644 --- a/packages/app-next-example-plugin/package.json +++ b/packages/app-next-example-plugin/package.json @@ -1,6 +1,6 @@ { "name": "app-next-example-plugin", - "version": "0.0.28-next.0", + "version": "0.0.29-next.0", "description": "Backstage internal example plugin", "backstage": { "role": "frontend-plugin", diff --git a/packages/app-next/CHANGELOG.md b/packages/app-next/CHANGELOG.md index 87c84cf26c..c632471817 100644 --- a/packages/app-next/CHANGELOG.md +++ b/packages/app-next/CHANGELOG.md @@ -1,5 +1,156 @@ # example-app-next +## 0.0.29-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/ui@0.9.0-next.2 + - @backstage/plugin-techdocs@1.16.0-next.1 + - @backstage/plugin-signals@0.0.25-next.1 + - @backstage/plugin-search-react@1.10.0-next.1 + - @backstage/plugin-catalog@1.32.0-next.1 + - @backstage/plugin-search@1.5.0-next.1 + - @backstage/plugin-app-visualizer@0.1.25-next.1 + - @backstage/cli@0.34.5-next.1 + - @backstage/plugin-techdocs-module-addons-contrib@1.1.30-next.1 + - @backstage/plugin-catalog-react@1.21.3-next.1 + - @backstage/core-components@0.18.3-next.1 + - @backstage/core-plugin-api@1.11.2-next.1 + - @backstage/core-app-api@1.19.2-next.1 + - @backstage/plugin-catalog-graph@0.5.3-next.1 + - @backstage/frontend-plugin-api@0.12.2-next.1 + - @backstage/plugin-scaffolder@1.34.3-next.0 + - @backstage/core-compat-api@0.5.4-next.0 + - @backstage/plugin-api-docs@0.13.1-next.1 + - @backstage/plugin-catalog-import@0.13.7-next.0 + - @backstage/plugin-org@0.6.46-next.0 + - @backstage/plugin-scaffolder-react@1.19.3-next.0 + - @backstage/plugin-user-settings@0.8.29-next.0 + +## 0.0.29-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/ui@0.9.0-next.1 + +## 0.0.29-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/cli@0.34.5-next.0 + - @backstage/ui@0.8.2-next.0 + - @backstage/plugin-scaffolder-react@1.19.3-next.0 + - @backstage/plugin-techdocs@1.15.2-next.0 + - @backstage/plugin-user-settings@0.8.29-next.0 + - @backstage/plugin-auth@0.1.2-next.0 + - @backstage/plugin-permission-react@0.4.38-next.0 + - @backstage/plugin-search-react@1.9.6-next.0 + - @backstage/plugin-signals@0.0.25-next.0 + - @backstage/plugin-catalog-react@1.21.3-next.0 + - @backstage/plugin-scaffolder@1.34.3-next.0 + - @backstage/plugin-catalog-unprocessed-entities@0.2.23-next.0 + - @backstage/core-app-api@1.19.2-next.0 + - @backstage/core-plugin-api@1.11.2-next.0 + - @backstage/frontend-app-api@0.13.2-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/core-components@0.18.3-next.0 + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/frontend-plugin-api@0.12.2-next.0 + - @backstage/plugin-catalog-import@0.13.7-next.0 + - @backstage/app-defaults@1.7.2-next.0 + - @backstage/core-compat-api@0.5.4-next.0 + - @backstage/frontend-defaults@0.3.3-next.0 + - @backstage/integration-react@1.2.12-next.0 + - @backstage/theme@0.7.0 + - @backstage/plugin-api-docs@0.13.1-next.0 + - @backstage/plugin-app@0.3.2-next.0 + - @backstage/plugin-app-visualizer@0.1.25-next.0 + - @backstage/plugin-auth-react@0.1.21-next.0 + - @backstage/plugin-catalog@1.31.5-next.0 + - @backstage/plugin-catalog-common@1.1.7-next.0 + - @backstage/plugin-catalog-graph@0.5.3-next.0 + - @backstage/plugin-home@0.8.14-next.0 + - @backstage/plugin-kubernetes@0.12.13-next.0 + - @backstage/plugin-kubernetes-cluster@0.0.31-next.0 + - @backstage/plugin-notifications@0.5.11-next.0 + - @backstage/plugin-org@0.6.46-next.0 + - @backstage/plugin-search@1.4.32-next.0 + - @backstage/plugin-search-common@1.2.21-next.0 + - @backstage/plugin-techdocs-module-addons-contrib@1.1.30-next.0 + - @backstage/plugin-techdocs-react@1.3.5-next.0 + +## 0.0.28 + +### Patch Changes + +- Updated dependencies + - @backstage/ui@0.8.0 + - @backstage/plugin-catalog-graph@0.5.2 + - @backstage/plugin-scaffolder-react@1.19.2 + - @backstage/plugin-catalog-react@1.21.2 + - @backstage/core-components@0.18.2 + - @backstage/plugin-app@0.3.1 + - @backstage/plugin-signals@0.0.24 + - @backstage/cli@0.34.4 + - @backstage/plugin-home@0.8.13 + - @backstage/plugin-org@0.6.45 + - @backstage/frontend-plugin-api@0.12.1 + - @backstage/plugin-scaffolder@1.34.2 + - @backstage/plugin-search-react@1.9.5 + - @backstage/plugin-search@1.4.31 + - @backstage/plugin-notifications@0.5.10 + - @backstage/plugin-api-docs@0.13.0 + - @backstage/config@1.3.5 + - @backstage/plugin-user-settings@0.8.27 + - @backstage/plugin-app-visualizer@0.1.24 + - @backstage/theme@0.7.0 + - @backstage/plugin-kubernetes-cluster@0.0.30 + - @backstage/plugin-kubernetes@0.12.12 + - @backstage/core-compat-api@0.5.3 + - @backstage/frontend-app-api@0.13.1 + - @backstage/plugin-catalog@1.31.4 + - @backstage/app-defaults@1.7.1 + - @backstage/plugin-auth@0.1.1 + - @backstage/plugin-techdocs@1.15.1 + - @backstage/plugin-techdocs-react@1.3.4 + - @backstage/core-app-api@1.19.1 + - @backstage/core-plugin-api@1.11.1 + - @backstage/frontend-defaults@0.3.2 + - @backstage/integration-react@1.2.11 + - @backstage/plugin-auth-react@0.1.20 + - @backstage/plugin-catalog-common@1.1.6 + - @backstage/plugin-catalog-import@0.13.6 + - @backstage/plugin-catalog-unprocessed-entities@0.2.22 + - @backstage/plugin-permission-react@0.4.37 + - @backstage/plugin-search-common@1.2.20 + - @backstage/plugin-techdocs-module-addons-contrib@1.1.29 + +## 0.0.28-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-react@1.19.2-next.2 + - @backstage/plugin-org@0.6.45-next.2 + - @backstage/cli@0.34.4-next.2 + - @backstage/ui@0.7.2-next.1 + - @backstage/core-components@0.18.2-next.2 + - @backstage/theme@0.6.9-next.0 + - @backstage/plugin-scaffolder@1.34.2-next.2 + - @backstage/app-defaults@1.7.1-next.2 + - @backstage/plugin-app@0.3.1-next.2 + - @backstage/plugin-auth@0.1.1-next.1 + - @backstage/plugin-home@0.8.13-next.2 + - @backstage/plugin-notifications@0.5.10-next.2 + - @backstage/plugin-search-react@1.9.5-next.2 + - @backstage/plugin-signals@0.0.24-next.2 + - @backstage/plugin-techdocs@1.15.1-next.2 + - @backstage/plugin-techdocs-react@1.3.4-next.1 + - @backstage/plugin-user-settings@0.8.27-next.2 + ## 0.0.28-next.1 ### Patch Changes diff --git a/packages/app-next/app-config.yaml b/packages/app-next/app-config.yaml index 35380c961f..dcaddfb9bf 100644 --- a/packages/app-next/app-config.yaml +++ b/packages/app-next/app-config.yaml @@ -21,6 +21,11 @@ app: ownerEntityRefs: [cubic-belugas] extensions: + - entity-card:org/members-list: + config: + showAggregateMembersToggle: true + initialRelationAggregation: aggregated + # - apis.plugin.graphiql.browse.gitlab: true # - graphiql-endpoint:graphiql/gitlab: true diff --git a/packages/app-next/package.json b/packages/app-next/package.json index ebfb59c96a..5dae3c9750 100644 --- a/packages/app-next/package.json +++ b/packages/app-next/package.json @@ -1,6 +1,6 @@ { "name": "example-app-next", - "version": "0.0.28-next.1", + "version": "0.0.29-next.2", "backstage": { "role": "frontend" }, diff --git a/packages/app-next/public/index.html b/packages/app-next/public/index.html index 63ab0bec0c..74387edea3 100644 --- a/packages/app-next/public/index.html +++ b/packages/app-next/public/index.html @@ -4,6 +4,7 @@ + + } /> + } /> ); diff --git a/packages/app/src/AuthProxyDiscoveryApi.ts b/packages/app/src/AuthProxyDiscoveryApi.ts index 761803f0de..23d6e404db 100644 --- a/packages/app/src/AuthProxyDiscoveryApi.ts +++ b/packages/app/src/AuthProxyDiscoveryApi.ts @@ -20,12 +20,16 @@ import { DiscoveryApi } from '@backstage/core-plugin-api'; export class AuthProxyDiscoveryApi implements DiscoveryApi { private urlPatternDiscovery: UrlPatternDiscovery; + private readonly isAuthProxyingEnabled?: boolean; + private readonly authProxyUrl?: string; constructor( baseUrl: string, - private readonly isAuthProxyingEnabled?: boolean, - private readonly authProxyUrl?: string, + isAuthProxyingEnabled?: boolean, + authProxyUrl?: string, ) { + this.isAuthProxyingEnabled = isAuthProxyingEnabled; + this.authProxyUrl = authProxyUrl; this.urlPatternDiscovery = UrlPatternDiscovery.compile( `${baseUrl}/api/{{ pluginId }}`, ); diff --git a/packages/app/src/components/home/CustomizableHomePage.tsx b/packages/app/src/components/home/CustomizableHomePage.tsx index b592490f2e..1d3cab646b 100644 --- a/packages/app/src/components/home/CustomizableHomePage.tsx +++ b/packages/app/src/components/home/CustomizableHomePage.tsx @@ -37,6 +37,7 @@ const defaultConfig = [ y: 0, width: 24, height: 2, + deletable: false, }, { component: 'HomePageRecentlyVisited', diff --git a/packages/backend-app-api/CHANGELOG.md b/packages/backend-app-api/CHANGELOG.md index a0176a008f..8514a08cf2 100644 --- a/packages/backend-app-api/CHANGELOG.md +++ b/packages/backend-app-api/CHANGELOG.md @@ -1,5 +1,41 @@ # @backstage/backend-app-api +## 1.3.0-next.1 + +### Minor Changes + +- a17d9df: Updates API for `instanceMetadata` service to return a list of plugins not features. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + +## 1.2.9-next.0 + +### Patch Changes + +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/config@1.3.6-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/errors@1.2.7 + +## 1.2.8 + +### Patch Changes + +- dd69cf6: Moved up registration of unhandled rejections and errors listeners to be done as early as possible, avoiding flakiness in backend startups and instead always logging these failures rather than sometimes crashing the process. +- Updated dependencies + - @backstage/config@1.3.5 + - @backstage/backend-plugin-api@1.4.4 + +## 1.2.8-next.1 + +### Patch Changes + +- dd69cf6: Moved up registration of unhandled rejections and errors listeners to be done as early as possible, avoiding flakiness in backend startups and instead always logging these failures rather than sometimes crashing the process. + ## 1.2.8-next.0 ### Patch Changes diff --git a/packages/backend-app-api/package.json b/packages/backend-app-api/package.json index b082de0cfa..b55d31a8a8 100644 --- a/packages/backend-app-api/package.json +++ b/packages/backend-app-api/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/backend-app-api", - "version": "1.2.8-next.0", + "version": "1.3.0-next.1", "description": "Core API used by Backstage backend apps", "backstage": { "role": "node-library" diff --git a/packages/backend-app-api/src/lib/DependencyGraph.ts b/packages/backend-app-api/src/lib/DependencyGraph.ts index 8b3de48ff2..8e36c39aec 100644 --- a/packages/backend-app-api/src/lib/DependencyGraph.ts +++ b/packages/backend-app-api/src/lib/DependencyGraph.ts @@ -30,11 +30,15 @@ class Node { ); } - private constructor( - readonly value: T, - readonly consumes: Set, - readonly provides: Set, - ) {} + readonly value: T; + readonly consumes: Set; + readonly provides: Set; + + private constructor(value: T, consumes: Set, provides: Set) { + this.value = value; + this.consumes = consumes; + this.provides = provides; + } } /** @internal */ diff --git a/packages/backend-app-api/src/wiring/BackendInitializer.test.ts b/packages/backend-app-api/src/wiring/BackendInitializer.test.ts index 9e8a6579fd..965e467501 100644 --- a/packages/backend-app-api/src/wiring/BackendInitializer.test.ts +++ b/packages/backend-app-api/src/wiring/BackendInitializer.test.ts @@ -22,9 +22,9 @@ import { createExtensionPoint, createBackendFeatureLoader, ServiceRef, + coreServices, } from '@backstage/backend-plugin-api'; import { BackendInitializer } from './BackendInitializer'; -import { instanceMetadataServiceRef } from '@backstage/backend-plugin-api/alpha'; import { mockServices } from '@backstage/backend-test-utils'; const baseFactories = [ @@ -57,6 +57,42 @@ const testPlugin = createBackendPlugin({ }); describe('BackendInitializer', () => { + it('should only load modules if the plugin is present', async () => { + let loadedModule = false; + const backend1 = new BackendInitializer(baseFactories); + const testModule = createBackendModule({ + pluginId: 'test', + moduleId: 'producer', + register(reg) { + reg.registerInit({ + deps: {}, + async init() { + loadedModule = true; + }, + }); + }, + }); + await backend1.add(testModule); + await backend1.start(); + expect(loadedModule).toBe(false); + + const backend2 = new BackendInitializer(baseFactories); + await backend2.add(testModule); + await backend2.add( + createBackendPlugin({ + pluginId: 'test', + register(reg) { + reg.registerInit({ + deps: {}, + async init() {}, + }); + }, + }), + ); + await backend2.start(); + expect(loadedModule).toBe(true); + }); + it('should initialize root scoped services', async () => { const ref1 = createServiceRef<{ x: number }>({ id: '1', @@ -1074,7 +1110,7 @@ describe('BackendInitializer', () => { }); it('should properly add plugins + modules to the instance metadata service', async () => { - expect.assertions(2); + expect.assertions(1); const backend = new BackendInitializer(baseFactories); const plugin = createBackendPlugin({ pluginId: 'test', @@ -1090,31 +1126,25 @@ describe('BackendInitializer', () => { register(reg) { reg.registerInit({ deps: { - instanceMetadata: instanceMetadataServiceRef, + instanceMetadata: coreServices.rootInstanceMetadata, }, async init({ instanceMetadata }) { - expect(instanceMetadata.getInstalledFeatures()).toEqual([ + await expect( + instanceMetadata.getInstalledPlugins(), + ).resolves.toEqual([ { pluginId: 'test', - type: 'plugin', - }, - { - pluginId: 'test', - moduleId: 'test', - type: 'module', + modules: [ + { + moduleId: 'test', + }, + ], }, { pluginId: 'instance-metadata', - type: 'plugin', + modules: [], }, ]); - expect(instanceMetadata.getInstalledFeatures().map(String)).toEqual( - [ - 'plugin{pluginId=test}', - 'module{moduleId=test,pluginId=test}', - 'plugin{pluginId=instance-metadata}', - ], - ); }, }); }, @@ -1135,6 +1165,67 @@ describe('BackendInitializer', () => { await backend.start(); }); + it('should ignore modules that do not have a matching plugin', async () => { + expect.assertions(1); + const backend = new BackendInitializer(baseFactories); + const instanceMetadataPlugin = createBackendPlugin({ + pluginId: 'instance-metadata', + register(reg) { + reg.registerInit({ + deps: { + instanceMetadata: coreServices.rootInstanceMetadata, + }, + async init({ instanceMetadata }) { + await expect( + instanceMetadata.getInstalledPlugins(), + ).resolves.toEqual([ + { + pluginId: 'instance-metadata', + modules: [], + }, + ]); + }, + }); + }, + }); + const module = createBackendModule({ + pluginId: 'test', + moduleId: 'test', + register(reg) { + reg.registerInit({ + deps: {}, + async init() {}, + }); + }, + }); + backend.add(module); + backend.add(instanceMetadataPlugin); + await backend.start(); + }); + + it('should prevent writes to the instance metadata service', async () => { + expect.assertions(1); + const backend = new BackendInitializer(baseFactories); + const plugin = createBackendPlugin({ + pluginId: 'test', + register(reg) { + reg.registerInit({ + deps: { + instanceMetadata: coreServices.rootInstanceMetadata, + }, + async init({ instanceMetadata }) { + const plugins = await instanceMetadata.getInstalledPlugins(); + await expect(() => { + (plugins[0] as any).pluginId = 'foo'; + }).toThrow(/Cannot assign to read only property/); + }, + }); + }, + }); + backend.add(plugin); + await backend.start(); + }); + it('should properly wait for all modules that consume an extension point to really finish, before starting the module that provides that extension point', async () => { expect.assertions(3); const backend = new BackendInitializer(baseFactories); diff --git a/packages/backend-app-api/src/wiring/BackendInitializer.ts b/packages/backend-app-api/src/wiring/BackendInitializer.ts index 9b925adb30..e7480d3beb 100644 --- a/packages/backend-app-api/src/wiring/BackendInitializer.ts +++ b/packages/backend-app-api/src/wiring/BackendInitializer.ts @@ -36,14 +36,11 @@ import type { // eslint-disable-next-line @backstage/no-relative-monorepo-imports import type { InternalServiceFactory } from '../../../backend-plugin-api/src/services/system/types'; import { ForwardedError, ConflictError, assertError } from '@backstage/errors'; -import { - instanceMetadataServiceRef, - BackendFeatureMeta, -} from '@backstage/backend-plugin-api/alpha'; import { DependencyGraph } from '../lib/DependencyGraph'; import { ServiceRegistry } from './ServiceRegistry'; import { createInitializationLogger } from './createInitializationLogger'; -import { unwrapFeature } from './helpers'; +import { deepFreeze, unwrapFeature } from './helpers'; +import type { RootInstanceMetadataServicePluginInfo } from '@backstage/backend-plugin-api'; export interface BackendRegisterInit { consumes: Set; @@ -101,56 +98,52 @@ const instanceRegistry = new (class InstanceRegistry { }; })(); -function createInstanceMetadataServiceFactory( - registrations: InternalBackendRegistrations[], +function createRootInstanceMetadataServiceFactory( + rawRegistrations: InternalBackendRegistrations[], ) { - const installedFeatures = registrations - .map(registration => { - if (registration.featureType === 'registrations') { - return registration - .getRegistrations() - .map(feature => { - if (feature.type === 'plugin') { - return Object.defineProperty( - { - type: 'plugin', - pluginId: feature.pluginId, - }, - 'toString', - { - enumerable: false, - configurable: true, - value: () => `plugin{pluginId=${feature.pluginId}}`, - }, - ); - } else if (feature.type === 'module') { - return Object.defineProperty( - { - type: 'module', - pluginId: feature.pluginId, - moduleId: feature.moduleId, - }, - 'toString', - { - enumerable: false, - configurable: true, - value: () => - `module{moduleId=${feature.moduleId},pluginId=${feature.pluginId}}`, - }, - ); - } - // Ignore unknown feature types. - return undefined; - }) - .filter(Boolean) as BackendFeatureMeta[]; - } - return []; - }) - .flat(); + const installedPlugins: Map = + new Map(); + const registrations = rawRegistrations + .filter(registration => registration.featureType === 'registrations') + .flatMap(registration => registration.getRegistrations()); + const plugins = registrations.filter( + registration => registration.type === 'plugin', + ); + const modules = registrations.filter( + registration => registration.type === 'module', + ); + for (const plugin of plugins) { + const { pluginId } = plugin; + if (!installedPlugins.get(pluginId)) { + installedPlugins.set(pluginId, { + pluginId, + modules: [], + }); + } + } + for (const module of modules) { + const { pluginId, moduleId } = module; + const installedPlugin = installedPlugins.get(pluginId); + if (installedPlugin) { + (installedPlugin.modules as Array<{ moduleId: string }>).push({ + moduleId, + }); + } + } + return createServiceFactory({ - service: instanceMetadataServiceRef, + service: coreServices.rootInstanceMetadata, deps: {}, - factory: async () => ({ getInstalledFeatures: () => installedFeatures }), + factory: async () => { + const readonlyInstalledPlugins = deepFreeze([ + ...installedPlugins.values(), + ]); + const instanceMetadata = { + getInstalledPlugins: () => Promise.resolve(readonlyInstalledPlugins), + }; + + return instanceMetadata; + }, }); } @@ -255,9 +248,33 @@ export class BackendInitializer { await this.#applyBackendFeatureLoaders(this.#registeredFeatureLoaders); this.#serviceRegistry.add( - createInstanceMetadataServiceFactory(this.#registrations), + createRootInstanceMetadataServiceFactory(this.#registrations), ); + // This makes sure that any uncaught errors or unhandled rejections are + // caught and logged, rather than terminating the process. We register these + // as early as possible while still using the root logger service, the + // tradeoff that if there are any unhandled errors as part of the that + // instationation, it will cause the process to crash. If there are multiple + // backend instances, each instance will log the error, because we can't + // determine which instance the error came from. + if (process.env.NODE_ENV !== 'test') { + const rootLogger = await this.#serviceRegistry.get( + coreServices.rootLogger, + 'root', + ); + process.on('unhandledRejection', (reason: Error) => { + rootLogger + ?.child({ type: 'unhandledRejection' }) + ?.error('Unhandled rejection', reason); + }); + process.on('uncaughtException', error => { + rootLogger + ?.child({ type: 'uncaughtException' }) + ?.error('Uncaught exception', error); + }); + } + // Initialize all root scoped services await this.#serviceRegistry.initializeEagerServicesWithScope('root'); @@ -449,25 +466,6 @@ export class BackendInitializer { await lifecycleService.startup(); initLogger.onAllStarted(); - - // Once the backend is started, any uncaught errors or unhandled rejections are caught - // and logged, in order to avoid crashing the entire backend on local failures. - if (process.env.NODE_ENV !== 'test') { - const rootLogger = await this.#serviceRegistry.get( - coreServices.rootLogger, - 'root', - ); - process.on('unhandledRejection', (reason: Error) => { - rootLogger - ?.child({ type: 'unhandledRejection' }) - ?.error('Unhandled rejection', reason); - }); - process.on('uncaughtException', error => { - rootLogger - ?.child({ type: 'uncaughtException' }) - ?.error('Uncaught exception', error); - }); - } } // It's fine to call .stop() multiple times, which for example can happen with manual stop + process exit diff --git a/packages/backend-app-api/src/wiring/helpers.ts b/packages/backend-app-api/src/wiring/helpers.ts index ffb7e3b079..1be7f62da2 100644 --- a/packages/backend-app-api/src/wiring/helpers.ts +++ b/packages/backend-app-api/src/wiring/helpers.ts @@ -34,3 +34,22 @@ export function unwrapFeature( return feature; } + +/** @internal */ +export type DeepReadonly = { + readonly [K in keyof T]: T[K] extends object ? DeepReadonly : T[K]; +}; + +/** + * Deeply freezes an object by recursively freezing all of its properties. + * From https://gist.github.com/tkrotoff/e997cd6ff8d6cf6e51e6bb6146407fc3 + + * https://stackoverflow.com/a/69656011 + */ +export function deepFreeze(obj: T) { + // Can cause: "Type instantiation is excessively deep and possibly infinite." + // @ts-expect-error + Object.values(obj).forEach( + value => Object.isFrozen(value) || deepFreeze(value), + ); + return Object.freeze(obj) as DeepReadonly; +} diff --git a/packages/backend-defaults/CHANGELOG.md b/packages/backend-defaults/CHANGELOG.md index 1201918fb5..f168e43ee4 100644 --- a/packages/backend-defaults/CHANGELOG.md +++ b/packages/backend-defaults/CHANGELOG.md @@ -1,5 +1,73 @@ # @backstage/backend-defaults +## 0.13.1-next.1 + +### Patch Changes + +- 91ab2eb: Fix a bug in the Gitlab URL reader where `search` did not handle multiple globs +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/backend-app-api@1.3.0-next.1 + - @backstage/plugin-auth-node@0.6.9-next.1 + - @backstage/plugin-events-node@0.4.17-next.1 + - @backstage/plugin-permission-node@0.10.6-next.1 + +## 0.13.1-next.0 + +### Patch Changes + +- 9bcfa77: Adjusted the log line wording of task worker starting +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- b2f6a5a: Fix #31348 issue where BitbucketUrlReader ignored provided token and instead always used integration credentials +- Updated dependencies + - @backstage/plugin-events-node@0.4.17-next.0 + - @backstage/plugin-auth-node@0.6.9-next.0 + - @backstage/backend-app-api@1.2.9-next.0 + - @backstage/config-loader@1.10.6-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/cli-node@0.2.15-next.0 + - @backstage/integration@1.18.2-next.0 + - @backstage/integration-aws-node@0.1.19-next.0 + - @backstage/plugin-permission-node@0.10.6-next.0 + - @backstage/backend-dev-utils@0.1.5 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + +## 0.13.0 + +### Minor Changes + +- 2d3e2b2: implement support for direct url for AzureBlobStorageUrlReader search function +- 8b91238: Adds support for configuring server-level HTTP options through the + `app-config.yaml` file under the `backend.server` key. Supported options + include `headersTimeout`, `keepAliveTimeout`, `requestTimeout`, `timeout`, + `maxHeadersCount`, and `maxRequestsPerSocket`. + + These are passed directly to the underlying Node.js HTTP server. + If omitted, Node.js defaults are used. + +- 8495b18: Add a new `externalTokenHandlersServiceRef` to allow custom external token validations + +### Patch Changes + +- Updated dependencies + - @backstage/backend-app-api@1.2.8 + - @backstage/integration@1.18.1 + - @backstage/config-loader@1.10.5 + - @backstage/config@1.3.5 + - @backstage/backend-plugin-api@1.4.4 + - @backstage/integration-aws-node@0.1.18 + - @backstage/plugin-auth-node@0.6.8 + - @backstage/plugin-events-node@0.4.16 + - @backstage/plugin-permission-node@0.10.5 + +## 0.13.0-next.2 + +### Minor Changes + +- 8495b18: Add a new `externalTokenHandlersServiceRef` to allow custom external token validations + ## 0.13.0-next.1 ### Patch Changes diff --git a/packages/backend-defaults/package.json b/packages/backend-defaults/package.json index f85d34db9e..a089998dee 100644 --- a/packages/backend-defaults/package.json +++ b/packages/backend-defaults/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/backend-defaults", - "version": "0.13.0-next.1", + "version": "0.13.1-next.1", "description": "Backend defaults used by Backstage backend apps", "backstage": { "role": "node-library" diff --git a/packages/backend-defaults/report-alpha.api.md b/packages/backend-defaults/report-alpha.api.md index 58277b5e1e..2ca3525946 100644 --- a/packages/backend-defaults/report-alpha.api.md +++ b/packages/backend-defaults/report-alpha.api.md @@ -5,6 +5,7 @@ ```ts import { ActionsRegistryService } from '@backstage/backend-plugin-api/alpha'; import { ActionsService } from '@backstage/backend-plugin-api/alpha'; +import { InstanceMetadataService } from '@backstage/backend-plugin-api/alpha'; import { ServiceFactory } from '@backstage/backend-plugin-api'; // @public (undocumented) @@ -21,5 +22,12 @@ export const actionsServiceFactory: ServiceFactory< 'singleton' >; +// @alpha @deprecated (undocumented) +export const instanceMetadataServiceFactory: ServiceFactory< + InstanceMetadataService, + 'plugin', + 'singleton' +>; + // (No @packageDocumentation comment for this package) ``` diff --git a/packages/backend-defaults/report-auth.api.md b/packages/backend-defaults/report-auth.api.md index 1883e65a6a..2fc0f16452 100644 --- a/packages/backend-defaults/report-auth.api.md +++ b/packages/backend-defaults/report-auth.api.md @@ -4,6 +4,7 @@ ```ts import { AuthService } from '@backstage/backend-plugin-api'; +import type { Config } from '@backstage/config'; import { ServiceFactory } from '@backstage/backend-plugin-api'; import { ServiceRef } from '@backstage/backend-plugin-api'; @@ -14,6 +15,36 @@ export const authServiceFactory: ServiceFactory< 'singleton' >; +// @public +export function createExternalTokenHandler( + handler: ExternalTokenHandler, +): ExternalTokenHandler; + +// @public +export interface ExternalTokenHandler { + // (undocumented) + initialize(ctx: { options: Config }): TContext; + // (undocumented) + type: string; + // (undocumented) + verifyToken( + token: string, + ctx: TContext, + ): Promise< + | { + subject: string; + } + | undefined + >; +} + +// @public +export const externalTokenHandlersServiceRef: ServiceRef< + ExternalTokenHandler, + 'plugin', + 'multiton' +>; + // @public export interface PluginTokenHandler { // (undocumented) diff --git a/packages/backend-defaults/src/CreateBackend.ts b/packages/backend-defaults/src/CreateBackend.ts index 4b201fded3..44df59b5f1 100644 --- a/packages/backend-defaults/src/CreateBackend.ts +++ b/packages/backend-defaults/src/CreateBackend.ts @@ -38,6 +38,7 @@ import { eventsServiceFactory } from '@backstage/plugin-events-node'; import { actionsRegistryServiceFactory, actionsServiceFactory, + instanceMetadataServiceFactory, } from '@backstage/backend-defaults/alpha'; export const defaultServiceFactories = [ @@ -65,6 +66,7 @@ export const defaultServiceFactories = [ // alpha services actionsRegistryServiceFactory, actionsServiceFactory, + instanceMetadataServiceFactory, ]; /** diff --git a/packages/backend-defaults/src/PackageDiscoveryService.ts b/packages/backend-defaults/src/PackageDiscoveryService.ts index abf015baf3..e4c7b3ab3b 100644 --- a/packages/backend-defaults/src/PackageDiscoveryService.ts +++ b/packages/backend-defaults/src/PackageDiscoveryService.ts @@ -80,10 +80,13 @@ async function findClosestPackageDir( /** @internal */ export class PackageDiscoveryService { - constructor( - private readonly config: RootConfigService, - private readonly logger: RootLoggerService, - ) {} + private readonly config: RootConfigService; + private readonly logger: RootLoggerService; + + constructor(config: RootConfigService, logger: RootLoggerService) { + this.config = config; + this.logger = logger; + } getDependencyNames(path: string) { const { dependencies } = require(path) as BackstagePackageJson; diff --git a/packages/backend-defaults/src/alpha/entrypoints/actions/DefaultActionsService.ts b/packages/backend-defaults/src/alpha/entrypoints/actions/DefaultActionsService.ts index d8847c2187..155bbf8b35 100644 --- a/packages/backend-defaults/src/alpha/entrypoints/actions/DefaultActionsService.ts +++ b/packages/backend-defaults/src/alpha/entrypoints/actions/DefaultActionsService.ts @@ -28,12 +28,22 @@ import { } from '@backstage/backend-plugin-api/alpha'; export class DefaultActionsService implements ActionsService { + private readonly discovery: DiscoveryService; + private readonly config: RootConfigService; + private readonly logger: LoggerService; + private readonly auth: AuthService; + private constructor( - private readonly discovery: DiscoveryService, - private readonly config: RootConfigService, - private readonly logger: LoggerService, - private readonly auth: AuthService, - ) {} + discovery: DiscoveryService, + config: RootConfigService, + logger: LoggerService, + auth: AuthService, + ) { + this.discovery = discovery; + this.config = config; + this.logger = logger; + this.auth = auth; + } static create({ discovery, diff --git a/packages/backend-defaults/src/alpha/entrypoints/actionsRegistry/DefaultActionsRegistryService.ts b/packages/backend-defaults/src/alpha/entrypoints/actionsRegistry/DefaultActionsRegistryService.ts index 2284935787..7348e0a185 100644 --- a/packages/backend-defaults/src/alpha/entrypoints/actionsRegistry/DefaultActionsRegistryService.ts +++ b/packages/backend-defaults/src/alpha/entrypoints/actionsRegistry/DefaultActionsRegistryService.ts @@ -39,12 +39,22 @@ export class DefaultActionsRegistryService implements ActionsRegistryService { private actions: Map> = new Map(); + private readonly logger: LoggerService; + private readonly httpAuth: HttpAuthService; + private readonly auth: AuthService; + private readonly metadata: PluginMetadataService; + private constructor( - private readonly logger: LoggerService, - private readonly httpAuth: HttpAuthService, - private readonly auth: AuthService, - private readonly metadata: PluginMetadataService, - ) {} + logger: LoggerService, + httpAuth: HttpAuthService, + auth: AuthService, + metadata: PluginMetadataService, + ) { + this.logger = logger; + this.httpAuth = httpAuth; + this.auth = auth; + this.metadata = metadata; + } static create({ httpAuth, diff --git a/packages/backend-defaults/src/alpha/entrypoints/instanceMetadata/index.ts b/packages/backend-defaults/src/alpha/entrypoints/instanceMetadata/index.ts new file mode 100644 index 0000000000..00ee75400a --- /dev/null +++ b/packages/backend-defaults/src/alpha/entrypoints/instanceMetadata/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2025 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { instanceMetadataServiceFactory } from './instanceMetadataServiceFactory'; diff --git a/packages/backend-defaults/src/alpha/entrypoints/instanceMetadata/instanceMetadataServiceFactory.test.ts b/packages/backend-defaults/src/alpha/entrypoints/instanceMetadata/instanceMetadataServiceFactory.test.ts new file mode 100644 index 0000000000..2368ea74e8 --- /dev/null +++ b/packages/backend-defaults/src/alpha/entrypoints/instanceMetadata/instanceMetadataServiceFactory.test.ts @@ -0,0 +1,58 @@ +/* + * Copyright 2025 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + coreServices, + createBackendPlugin, +} from '@backstage/backend-plugin-api'; +import { instanceMetadataServiceFactory } from './instanceMetadataServiceFactory'; +import { startTestBackend } from '@backstage/backend-test-utils'; +import { instanceMetadataServiceRef } from '@backstage/backend-plugin-api/alpha'; +import Router from 'express-promise-router'; +import request from 'supertest'; + +describe('instanceMetadataServiceFactory', () => { + it('should create an instance metadata service', async () => { + const pluginSubject = createBackendPlugin({ + pluginId: 'my-plugin', + register(reg) { + reg.registerInit({ + deps: { + instanceMetadata: instanceMetadataServiceRef, + httpRouter: coreServices.httpRouter, + }, + async init({ instanceMetadata, httpRouter }) { + const router = Router(); + router.get('/instance-metadata', async (_, res) => { + const metadata = await instanceMetadata.getInstalledFeatures(); + res.json(metadata); + }); + httpRouter.use(router); + }, + }); + }, + }); + + const { server } = await startTestBackend({ + features: [pluginSubject, instanceMetadataServiceFactory], + }); + + const response = await request(server) + .get('/api/my-plugin/instance-metadata') + .expect(200); + expect(response.body).toEqual([{ pluginId: 'my-plugin', type: 'plugin' }]); + }); +}); diff --git a/packages/backend-defaults/src/alpha/entrypoints/instanceMetadata/instanceMetadataServiceFactory.ts b/packages/backend-defaults/src/alpha/entrypoints/instanceMetadata/instanceMetadataServiceFactory.ts new file mode 100644 index 0000000000..bd2d427a59 --- /dev/null +++ b/packages/backend-defaults/src/alpha/entrypoints/instanceMetadata/instanceMetadataServiceFactory.ts @@ -0,0 +1,58 @@ +/* + * Copyright 2025 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + coreServices, + createServiceFactory, +} from '@backstage/backend-plugin-api'; +import { + BackendFeatureMeta, + InstanceMetadataService, + instanceMetadataServiceRef, +} from '@backstage/backend-plugin-api/alpha'; + +/** + * @alpha + * @deprecated use {@link @backstage/backend-plugin-api#coreServices.rootInstanceMetadata} instead + */ +export const instanceMetadataServiceFactory = createServiceFactory({ + service: instanceMetadataServiceRef, + deps: { + instanceMetadata: coreServices.rootInstanceMetadata, + }, + factory: async ({ instanceMetadata }) => { + const plugins = await instanceMetadata.getInstalledPlugins(); + const features: BackendFeatureMeta[] = []; + for (const plugin of plugins) { + features.push({ + type: 'plugin' as const, + pluginId: plugin.pluginId, + }); + for (const module of plugin.modules) { + features.push({ + type: 'module' as const, + pluginId: plugin.pluginId, + moduleId: module.moduleId, + }); + } + } + const service: InstanceMetadataService = { + getInstalledFeatures: () => features, + }; + + return service; + }, +}); diff --git a/packages/backend-defaults/src/alpha/index.ts b/packages/backend-defaults/src/alpha/index.ts index 13bb439acd..744d5bc552 100644 --- a/packages/backend-defaults/src/alpha/index.ts +++ b/packages/backend-defaults/src/alpha/index.ts @@ -15,3 +15,4 @@ */ export { actionsRegistryServiceFactory } from './entrypoints/actionsRegistry'; export { actionsServiceFactory } from './entrypoints/actions'; +export { instanceMetadataServiceFactory } from './entrypoints/instanceMetadata'; diff --git a/packages/backend-defaults/src/entrypoints/auditor/WinstonRootAuditorService.ts b/packages/backend-defaults/src/entrypoints/auditor/WinstonRootAuditorService.ts index 67a28cf74b..32c21e642e 100644 --- a/packages/backend-defaults/src/entrypoints/auditor/WinstonRootAuditorService.ts +++ b/packages/backend-defaults/src/entrypoints/auditor/WinstonRootAuditorService.ts @@ -81,7 +81,11 @@ export type WinstonRootAuditorServiceOptions = { * ``` */ export class WinstonRootAuditorService { - private constructor(private readonly winstonLogger: WinstonLogger) {} + private readonly winstonLogger: WinstonLogger; + + private constructor(winstonLogger: WinstonLogger) { + this.winstonLogger = winstonLogger; + } /** * Creates a {@link WinstonRootAuditorService} instance. diff --git a/packages/backend-defaults/src/entrypoints/auth/DefaultAuthService.ts b/packages/backend-defaults/src/entrypoints/auth/DefaultAuthService.ts index d8cb60e8b5..bee40de65e 100644 --- a/packages/backend-defaults/src/entrypoints/auth/DefaultAuthService.ts +++ b/packages/backend-defaults/src/entrypoints/auth/DefaultAuthService.ts @@ -25,7 +25,7 @@ import { import { AuthenticationError } from '@backstage/errors'; import { JsonObject } from '@backstage/types'; import { decodeJwt } from 'jose'; -import { ExternalTokenHandler } from './external/ExternalTokenHandler'; +import { ExternalAuthTokenHandler } from './external/ExternalAuthTokenHandler'; import { createCredentialsWithNonePrincipal, createCredentialsWithServicePrincipal, @@ -38,14 +38,28 @@ import { UserTokenHandler } from './user/UserTokenHandler'; /** @internal */ export class DefaultAuthService implements AuthService { + private readonly userTokenHandler: UserTokenHandler; + private readonly pluginTokenHandler: PluginTokenHandler; + private readonly externalTokenHandler: ExternalAuthTokenHandler; + private readonly pluginId: string; + private readonly disableDefaultAuthPolicy: boolean; + private readonly pluginKeySource: PluginKeySource; + constructor( - private readonly userTokenHandler: UserTokenHandler, - private readonly pluginTokenHandler: PluginTokenHandler, - private readonly externalTokenHandler: ExternalTokenHandler, - private readonly pluginId: string, - private readonly disableDefaultAuthPolicy: boolean, - private readonly pluginKeySource: PluginKeySource, - ) {} + userTokenHandler: UserTokenHandler, + pluginTokenHandler: PluginTokenHandler, + externalTokenHandler: ExternalAuthTokenHandler, + pluginId: string, + disableDefaultAuthPolicy: boolean, + pluginKeySource: PluginKeySource, + ) { + this.userTokenHandler = userTokenHandler; + this.pluginTokenHandler = pluginTokenHandler; + this.externalTokenHandler = externalTokenHandler; + this.pluginId = pluginId; + this.disableDefaultAuthPolicy = disableDefaultAuthPolicy; + this.pluginKeySource = pluginKeySource; + } async authenticate( token: string, diff --git a/packages/backend-defaults/src/entrypoints/auth/JwksClient.ts b/packages/backend-defaults/src/entrypoints/auth/JwksClient.ts index f956ceb70a..cd283fe92b 100644 --- a/packages/backend-defaults/src/entrypoints/auth/JwksClient.ts +++ b/packages/backend-defaults/src/entrypoints/auth/JwksClient.ts @@ -30,7 +30,11 @@ export class JwksClient { #keyStore?: GetKeyFunction; #keyStoreUpdated: number = 0; - constructor(private readonly getEndpoint: () => Promise) {} + private readonly getEndpoint: () => Promise; + + constructor(getEndpoint: () => Promise) { + this.getEndpoint = getEndpoint; + } get getKey() { if (!this.#keyStore) { diff --git a/packages/backend-defaults/src/entrypoints/auth/authServiceFactory.test.ts b/packages/backend-defaults/src/entrypoints/auth/authServiceFactory.test.ts index a244614135..b70aedfb9a 100644 --- a/packages/backend-defaults/src/entrypoints/auth/authServiceFactory.test.ts +++ b/packages/backend-defaults/src/entrypoints/auth/authServiceFactory.test.ts @@ -31,6 +31,9 @@ import { toInternalBackstageCredentials } from './helpers'; import { PluginTokenHandler } from './plugin/PluginTokenHandler'; import { createServiceFactory } from '@backstage/backend-plugin-api'; +import { externalTokenHandlersServiceRef } from './external/ExternalAuthTokenHandler'; +import { createExternalTokenHandler } from './external/helpers'; + const server = setupServer(); // TODO: Ship discovery mock service in the service factory tester @@ -450,8 +453,88 @@ describe('authServiceFactory', () => { dependencies: [...mockDeps, customPluginTokenHandler], }); const searchAuth = await tester.getSubject('search'); - searchAuth.authenticate('unlimited-static-token'); + await searchAuth.authenticate('unlimited-static-token'); expect(customLogic).toHaveBeenCalledWith('unlimited-static-token'); }); }); + describe('add custom ExternalTokenHandler', () => { + it('should allow custom logic to be injected into the plugin token handler', async () => { + const customLogic = jest.fn(); + const customConfig = jest.fn(); + const deps = [ + discoveryServiceFactory, + mockServices.rootConfig.factory({ + data: { + backend: { + baseUrl: 'http://localhost', + auth: { + keys: [{ secret: 'abc' }], + externalAccess: [ + { + type: 'static', + options: { + token: 'limited-static-token', + subject: 'limited-static-subject', + }, + accessRestrictions: [ + { plugin: 'catalog', permission: 'do.it' }, + ], + }, + { + type: 'static', + options: { + token: 'unlimited-static-token', + subject: 'unlimited-static-subject', + }, + }, + { + type: 'custom', + options: { + [`custom-config`]: 'custom-config', + foo: 'bar', + }, + }, + ], + }, + }, + }, + }), + ]; + + const customExternalTokenHandler = createExternalTokenHandler<{ + [`custom-config`]: string; + foo: string; + }>({ + type: 'custom', + initialize({ options }) { + const customConfigValue = options.getString('custom-config'); + const foo = options.getString('foo'); + customConfig(customConfigValue); + return { [`custom-config`]: customConfigValue, foo }; + }, + async verifyToken(token, ctx) { + customLogic(token); + return { + subject: ctx.foo, + }; + }, + }); + + const customPluginTokenHandler = createServiceFactory({ + service: externalTokenHandlersServiceRef, + deps: {}, + async factory() { + return customExternalTokenHandler; + }, + }); + const tester = ServiceFactoryTester.from(authServiceFactory, { + dependencies: [...deps, customPluginTokenHandler], + }); + const searchAuth = await tester.getSubject('search'); + await searchAuth.authenticate('custom-token'); + + expect(customLogic).toHaveBeenCalledWith('custom-token'); + expect(customConfig).toHaveBeenCalledWith('custom-config'); + }); + }); }); diff --git a/packages/backend-defaults/src/entrypoints/auth/authServiceFactory.ts b/packages/backend-defaults/src/entrypoints/auth/authServiceFactory.ts index 91910292e6..ee171aef25 100644 --- a/packages/backend-defaults/src/entrypoints/auth/authServiceFactory.ts +++ b/packages/backend-defaults/src/entrypoints/auth/authServiceFactory.ts @@ -20,7 +20,10 @@ import { createServiceRef, } from '@backstage/backend-plugin-api'; import { DefaultAuthService } from './DefaultAuthService'; -import { ExternalTokenHandler } from './external/ExternalTokenHandler'; +import { + ExternalAuthTokenHandler, + externalTokenHandlersServiceRef, +} from './external/ExternalAuthTokenHandler'; import { DefaultPluginTokenHandler, PluginTokenHandler, @@ -64,6 +67,7 @@ export const authServiceFactory = createServiceFactory({ plugin: coreServices.pluginMetadata, database: coreServices.database, pluginTokenHandlerDecorator: pluginTokenHandlerDecoratorServiceRef, + externalTokenHandlers: externalTokenHandlersServiceRef, }, async factory({ config, @@ -72,6 +76,7 @@ export const authServiceFactory = createServiceFactory({ logger, database, pluginTokenHandlerDecorator, + externalTokenHandlers, }) { const disableDefaultAuthPolicy = config.getOptionalBoolean( @@ -102,10 +107,11 @@ export const authServiceFactory = createServiceFactory({ }), ); - const externalTokens = ExternalTokenHandler.create({ + const externalTokens = ExternalAuthTokenHandler.create({ ownPluginId: plugin.getId(), config, logger, + externalTokenHandlers, }); return new DefaultAuthService( diff --git a/packages/backend-defaults/src/entrypoints/auth/external/ExternalAuthTokenHandler.test.ts b/packages/backend-defaults/src/entrypoints/auth/external/ExternalAuthTokenHandler.test.ts new file mode 100644 index 0000000000..50cfb47b3e --- /dev/null +++ b/packages/backend-defaults/src/entrypoints/auth/external/ExternalAuthTokenHandler.test.ts @@ -0,0 +1,502 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { BackstagePrincipalAccessRestrictions } from '@backstage/backend-plugin-api'; +import { ExternalAuthTokenHandler } from './ExternalAuthTokenHandler'; +import { createExternalTokenHandler } from './helpers'; +import { AccessRestrictionsMap, ExternalTokenHandler } from './types'; +import { + mockServices, + registerMswTestHooks, +} from '@backstage/backend-test-utils'; +import { randomBytes } from 'crypto'; +import { SignJWT, exportJWK, generateKeyPair } from 'jose'; +import { DateTime } from 'luxon'; +import { v4 as uuid } from 'uuid'; +import { rest } from 'msw'; +import { setupServer } from 'msw/node'; + +// Simplified copy of TokenFactory in @backstage/plugin-auth-backend +interface AnyJWK extends Record { + use: 'sig'; + alg: string; + kid: string; + kty: string; +} +class FakeTokenFactory { + private readonly keys = new Array(); + private readonly options: { + issuer: string; + keyDurationSeconds: number; + }; + + constructor(options: { issuer: string; keyDurationSeconds: number }) { + this.options = options; + } + + async issueToken(params: { + claims: { + sub: string; + ent?: string[]; + }; + }): Promise { + const pair = await generateKeyPair('RS256'); + const publicKey = await exportJWK(pair.publicKey); + const kid = uuid(); + publicKey.kid = kid; + this.keys.push(publicKey as AnyJWK); + + const iss = this.options.issuer; + const sub = params.claims.sub; + const ent = params.claims.ent; + const aud = 'backstage'; + const iat = Math.floor(Date.now() / 1000); + const exp = iat + this.options.keyDurationSeconds; + + return new SignJWT({ iss, sub, aud, iat, exp, ent, kid }) + .setProtectedHeader({ alg: 'RS256', ent: ent, kid: kid }) + .setIssuer(iss) + .setAudience(aud) + .setSubject(sub) + .setIssuedAt(iat) + .setExpirationTime(exp) + .sign(pair.privateKey); + } + + async listPublicKeys(): Promise<{ keys: AnyJWK[] }> { + return { keys: this.keys }; + } +} + +describe('ExternalTokenHandler', () => { + const server = setupServer(); + registerMswTestHooks(server); + + it('skips over inner handlers that do not match, and applies plugin restrictions', async () => { + const handler1: ExternalTokenHandler = createExternalTokenHandler({ + type: 'type1', + initialize: jest.fn().mockResolvedValue(undefined), + verifyToken: jest.fn().mockResolvedValue(undefined), + }); + + const handler2: ExternalTokenHandler = + createExternalTokenHandler({ + type: 'type2', + initialize: jest.fn().mockResolvedValue(undefined), + verifyToken: jest.fn().mockResolvedValue({ + subject: 'sub', + }), + }); + + const accessRestrictions: AccessRestrictionsMap = new Map( + Object.entries({ + plugin1: { + permissionNames: ['do.it'], + } satisfies BackstagePrincipalAccessRestrictions, + }), + ); + + const plugin1 = new ExternalAuthTokenHandler('plugin1', [ + { + context: undefined, + handler: handler1, + }, + { + context: undefined, + handler: handler2, + allAccessRestrictions: accessRestrictions, + }, + ]); + const plugin2 = new ExternalAuthTokenHandler('plugin2', [ + { + context: undefined, + handler: handler1, + }, + { + context: undefined, + handler: handler2, + allAccessRestrictions: accessRestrictions, + }, + ]); + + await expect(plugin1.verifyToken('token')).resolves.toEqual({ + subject: 'sub', + accessRestrictions: { permissionNames: ['do.it'] }, + }); + await expect( + plugin2.verifyToken('token'), + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"This token's access is restricted to plugin(s) 'plugin1'"`, + ); + }); + + it('successfully parses known methods', async () => { + const legacyKey = randomBytes(24); + + const factory = new FakeTokenFactory({ + issuer: 'blah', + keyDurationSeconds: 100, + }); + + server.use( + rest.get( + 'https://example.com/.well-known/jwks.json', + async (_, res, ctx) => { + const keys = await factory.listPublicKeys(); + return res(ctx.json(keys)); + }, + ), + ); + + const handler = ExternalAuthTokenHandler.create({ + ownPluginId: 'catalog', + logger: mockServices.logger.mock(), + config: mockServices.rootConfig({ + data: { + backend: { + auth: { + externalAccess: [ + { + type: 'legacy', + options: { + secret: legacyKey.toString('base64'), + subject: 'legacy-subject', + }, + accessRestrictions: [ + { plugin: 'catalog', permission: 'catalog.entity.read' }, + ], + }, + { + type: 'static', + options: { + token: 'defdefdef', + subject: 'static-subject', + }, + accessRestrictions: [ + { plugin: 'catalog', permission: 'catalog.entity.read' }, + ], + }, + { + type: 'jwks', + options: { + url: 'https://example.com/.well-known/jwks.json', + algorithm: 'RS256', + issuer: 'blah', + audience: 'backstage', + subjectPrefix: 'custom-prefix', + }, + accessRestrictions: [ + { plugin: 'catalog', permission: 'catalog.entity.read' }, + ], + }, + ], + }, + }, + }, + }), + }); + + const legacyToken = await new SignJWT({ + sub: 'backstage-server', + exp: DateTime.now().plus({ minutes: 1 }).toUnixInteger(), + }) + .setProtectedHeader({ alg: 'HS256' }) + .sign(legacyKey); + + await expect(handler.verifyToken(legacyToken)).resolves.toEqual({ + subject: 'legacy-subject', + accessRestrictions: { permissionNames: ['catalog.entity.read'] }, + }); + + await expect(handler.verifyToken('defdefdef')).resolves.toEqual({ + subject: 'static-subject', + accessRestrictions: { permissionNames: ['catalog.entity.read'] }, + }); + + const jwksToken = await factory.issueToken({ + claims: { sub: 'jwks-subject' }, + }); + await expect(handler.verifyToken(jwksToken)).resolves.toEqual({ + subject: 'external:custom-prefix:jwks-subject', + accessRestrictions: { permissionNames: ['catalog.entity.read'] }, + }); + }); + it('successfully uses legacy configs', async () => { + const legacyKey = randomBytes(24); + const factory = new FakeTokenFactory({ + issuer: 'my-company', + keyDurationSeconds: 100, + }); + + server.use( + rest.get( + 'https://example.com/.well-known/jwks.json', + async (_, res, ctx) => { + const keys = await factory.listPublicKeys(); + return res(ctx.json(keys)); + }, + ), + ); + + const logger = mockServices.logger.mock(); + const handler = ExternalAuthTokenHandler.create({ + ownPluginId: 'catalog', + logger, + config: mockServices.rootConfig({ + data: { + backend: { + auth: { + keys: [ + { + secret: legacyKey.toString('base64'), + }, + ], + }, + }, + }, + }), + }); + + expect(logger.warn).toHaveBeenCalledWith( + `DEPRECATION WARNING: The backend.auth.keys config has been replaced by backend.auth.externalAccess, see https://backstage.io/docs/auth/service-to-service-auth`, + ); + + const legacyToken = await new SignJWT({ + sub: 'backstage-server', + exp: DateTime.now().plus({ minutes: 1 }).toUnixInteger(), + }) + .setProtectedHeader({ alg: 'HS256' }) + .sign(legacyKey); + + await expect(handler.verifyToken(legacyToken)).resolves.toEqual({ + subject: 'external:backstage-plugin', + }); + }); + it('successfully uses custom token handlers', async () => { + const factory = new FakeTokenFactory({ + issuer: 'my-company', + keyDurationSeconds: 100, + }); + + server.use( + rest.get( + 'https://example.com/.well-known/jwks.json', + async (_, res, ctx) => { + const keys = await factory.listPublicKeys(); + return res(ctx.json(keys)); + }, + ), + ); + + const verifyMock = jest.fn().mockResolvedValue({}); + const initializeMock = jest.fn().mockReturnValue({ context: 'a' }); + + const handler = ExternalAuthTokenHandler.create({ + ownPluginId: 'catalog', + logger: mockServices.logger.mock(), + config: mockServices.rootConfig({ + data: { + backend: { + auth: { + externalAccess: [ + { + type: 'internal-custom', + options: { + issuer: 'my-company', + subject: 'internal-subject', + audience: 'backstage', + }, + accessRestrictions: [ + { plugin: 'catalog', permission: 'catalog.entity.read' }, + ], + }, + ], + }, + }, + }, + }), + externalTokenHandlers: [ + createExternalTokenHandler({ + type: 'internal-custom', + initialize: initializeMock, + verifyToken: verifyMock, + }), + ], + }); + + expect(initializeMock).toHaveBeenCalledWith({ + options: expect.objectContaining({ + data: { + issuer: 'my-company', + subject: 'internal-subject', + audience: 'backstage', + }, + }), + }); + + const customToken = await factory.issueToken({ + claims: { sub: 'internal-subject' }, + }); + + await handler.verifyToken(customToken); + + expect(verifyMock).toHaveBeenCalledWith(customToken, { context: 'a' }); + }); + it('should fail if custom handler has same type as builtin handlers', async () => { + const logger = mockServices.logger.mock(); + const customStaticHandler: ExternalTokenHandler = + createExternalTokenHandler({ + type: 'static', + initialize: jest.fn().mockResolvedValue(undefined), + verifyToken: jest.fn().mockResolvedValue({ + subject: 'custom-static-subject', + }), + }); + + const createHandler = () => + ExternalAuthTokenHandler.create({ + ownPluginId: 'catalog', + logger, + config: mockServices.rootConfig({ + data: { + backend: { + auth: { + externalAccess: [ + { + type: 'static', + options: { + token: 'mytoken', + subject: 'static-subject', + }, + accessRestrictions: [ + { plugin: 'catalog', permission: 'catalog.entity.read' }, + ], + }, + ], + }, + }, + }, + }), + externalTokenHandlers: [customStaticHandler], + }); + + expect(createHandler).toThrowErrorMatchingInlineSnapshot( + `"Duplicate external token handler type 'static', each handler must have a unique type"`, + ); + }); + it('should fail if 2 custom handlers have the same type', async () => { + const createHandler = () => + ExternalAuthTokenHandler.create({ + ownPluginId: 'catalog', + logger: mockServices.logger.mock(), + config: mockServices.rootConfig(), + externalTokenHandlers: [ + createExternalTokenHandler({ + type: 'internal-custom', + initialize: jest.fn().mockResolvedValue(undefined), + verifyToken: jest.fn().mockResolvedValue({ + subject: 'sub', + }), + }), + createExternalTokenHandler({ + type: 'internal-custom', + initialize: jest.fn().mockResolvedValue(undefined), + verifyToken: jest.fn().mockResolvedValue({ + subject: 'sub', + }), + }), + ], + }); + + expect(createHandler).toThrowErrorMatchingInlineSnapshot( + `"Duplicate external token handler type 'internal-custom', each handler must have a unique type"`, + ); + }); + it('should fail if config contains types not declared', async () => { + const createHandler = () => + ExternalAuthTokenHandler.create({ + ownPluginId: 'catalog', + logger: mockServices.logger.mock(), + config: mockServices.rootConfig({ + data: { + backend: { + auth: { + externalAccess: [ + { + type: 'internal-custom', + options: { + issuer: 'my-company', + subject: 'internal-subject', + audience: 'backstage', + }, + accessRestrictions: [ + { plugin: 'catalog', permission: 'catalog.entity.read' }, + ], + }, + ], + }, + }, + }, + }), + }); + + expect(createHandler).toThrowErrorMatchingInlineSnapshot( + `"Unknown type 'internal-custom' in backend.auth.externalAccess, expected one of 'static', 'legacy', 'jwks'"`, + ); + }); + + it('should show valid custom types in errors', async () => { + const createHandler = () => + ExternalAuthTokenHandler.create({ + ownPluginId: 'catalog', + logger: mockServices.logger.mock(), + config: mockServices.rootConfig({ + data: { + backend: { + auth: { + externalAccess: [ + { + type: 'internal-custom-invalid', + options: { + issuer: 'my-company', + subject: 'internal-subject', + audience: 'backstage', + }, + accessRestrictions: [ + { plugin: 'catalog', permission: 'catalog.entity.read' }, + ], + }, + ], + }, + }, + }, + }), + externalTokenHandlers: [ + createExternalTokenHandler({ + type: 'internal-custom', + initialize: jest.fn().mockResolvedValue(undefined), + verifyToken: jest.fn().mockResolvedValue({ + subject: 'sub', + }), + }), + ], + }); + + expect(createHandler).toThrowErrorMatchingInlineSnapshot( + `"Unknown type 'internal-custom-invalid' in backend.auth.externalAccess, expected one of 'static', 'legacy', 'jwks', 'internal-custom'"`, + ); + }); +}); diff --git a/packages/backend-defaults/src/entrypoints/auth/external/ExternalAuthTokenHandler.ts b/packages/backend-defaults/src/entrypoints/auth/external/ExternalAuthTokenHandler.ts new file mode 100644 index 0000000000..e0a7680ce5 --- /dev/null +++ b/packages/backend-defaults/src/entrypoints/auth/external/ExternalAuthTokenHandler.ts @@ -0,0 +1,197 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + BackstagePrincipalAccessRestrictions, + createServiceRef, + LoggerService, + RootConfigService, +} from '@backstage/backend-plugin-api'; +import { NotAllowedError } from '@backstage/errors'; +import { legacyTokenHandler } from './legacy'; +import { staticTokenHandler } from './static'; +import { jwksTokenHandler } from './jwks'; +import { AccessRestrictionsMap, ExternalTokenHandler } from './types'; +import { readAccessRestrictionsFromConfig } from './helpers'; + +const NEW_CONFIG_KEY = 'backend.auth.externalAccess'; +const OLD_CONFIG_KEY = 'backend.auth.keys'; +let loggedDeprecationWarning = false; + +/** + * @public + * This service is used to add custom handlers for external token. + */ +export const externalTokenHandlersServiceRef = createServiceRef< + ExternalTokenHandler +>({ + id: 'core.auth.externalTokenHandlers', + multiton: true, +}); + +const defaultHandlers: Record> = { + static: staticTokenHandler, + legacy: legacyTokenHandler, + jwks: jwksTokenHandler, +}; + +type ContextMapEntry = { + context: T; + handler: ExternalTokenHandler; + allAccessRestrictions?: AccessRestrictionsMap; +}; + +/** + * Handles all types of external caller token types (i.e. not Backstage user + * tokens, nor Backstage backend plugin tokens). + * + * @internal + */ +export class ExternalAuthTokenHandler { + static create(options: { + ownPluginId: string; + config: RootConfigService; + logger: LoggerService; + externalTokenHandlers?: ExternalTokenHandler[]; + }): ExternalAuthTokenHandler { + const { + ownPluginId, + config, + externalTokenHandlers: customHandlers = [], + logger, + } = options; + + const handlersTypes = customHandlers.reduce< + Record> + >( + (acc, handler) => { + if (acc[handler.type]) { + throw new Error( + `Duplicate external token handler type '${handler.type}', each handler must have a unique type`, + ); + } + acc[handler.type] = handler; + return acc; + }, + { ...defaultHandlers }, + ); + + const handlerConfigs = config.getOptionalConfigArray(NEW_CONFIG_KEY) ?? []; + const contexts: ContextMapEntry[] = handlerConfigs.map( + handlerConfig => { + const type = handlerConfig.getString('type'); + + const handler = handlersTypes[type]; + if (!handler) { + const valid = Object.keys(handlersTypes) + .map(h => `'${h}'`) + .join(', '); + throw new Error( + `Unknown type '${type}' in ${NEW_CONFIG_KEY}, expected one of ${valid}`, + ); + } + return { + context: handler.initialize({ + options: handlerConfig.getConfig('options'), + }), + handler, + allAccessRestrictions: handlerConfig + ? readAccessRestrictionsFromConfig(handlerConfig) + : undefined, + }; + }, + ); + + // Load the old keys too + const legacyConfigs = config.getOptionalConfigArray(OLD_CONFIG_KEY) ?? []; + + if (legacyConfigs.length && !loggedDeprecationWarning) { + loggedDeprecationWarning = true; + + logger.warn( + `DEPRECATION WARNING: The ${OLD_CONFIG_KEY} config has been replaced by ${NEW_CONFIG_KEY}, see https://backstage.io/docs/auth/service-to-service-auth`, + ); + } + + for (const legacyConfig of legacyConfigs) { + contexts.push({ + context: legacyTokenHandler.initialize({ + legacy: true, + options: legacyConfig, + }), + handler: legacyTokenHandler, + allAccessRestrictions: readAccessRestrictionsFromConfig(legacyConfig), + }); + } + + return new ExternalAuthTokenHandler(ownPluginId, contexts); + } + + private readonly ownPluginId: string; + private readonly contexts: { + context: unknown; + handler: ExternalTokenHandler; + allAccessRestrictions?: AccessRestrictionsMap; + }[]; + + constructor( + ownPluginId: string, + contexts: { + context: unknown; + handler: ExternalTokenHandler; + allAccessRestrictions?: AccessRestrictionsMap; + }[], + ) { + this.ownPluginId = ownPluginId; + this.contexts = contexts; + } + + async verifyToken(token: string): Promise< + | { + subject: string; + accessRestrictions?: BackstagePrincipalAccessRestrictions; + } + | undefined + > { + for (const { handler, allAccessRestrictions, context } of this.contexts) { + const result = await handler.verifyToken(token, context); + if (result) { + if (allAccessRestrictions) { + const accessRestrictions = allAccessRestrictions.get( + this.ownPluginId, + ); + if (!accessRestrictions) { + const valid = [...allAccessRestrictions.keys()] + .map(k => `'${k}'`) + .join(', '); + throw new NotAllowedError( + `This token's access is restricted to plugin(s) ${valid}`, + ); + } + + return { + ...result, + accessRestrictions, + }; + } + + return result; + } + } + + return undefined; + } +} diff --git a/packages/backend-defaults/src/entrypoints/auth/external/ExternalTokenHandler.test.ts b/packages/backend-defaults/src/entrypoints/auth/external/ExternalTokenHandler.test.ts deleted file mode 100644 index 39b01c46b6..0000000000 --- a/packages/backend-defaults/src/entrypoints/auth/external/ExternalTokenHandler.test.ts +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Copyright 2024 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { BackstagePrincipalAccessRestrictions } from '@backstage/backend-plugin-api'; -import { ExternalTokenHandler } from './ExternalTokenHandler'; -import { TokenHandler } from './types'; -import { - mockServices, - registerMswTestHooks, -} from '@backstage/backend-test-utils'; -import { randomBytes } from 'crypto'; -import { SignJWT, exportJWK, generateKeyPair } from 'jose'; -import { DateTime } from 'luxon'; -import { v4 as uuid } from 'uuid'; -import { rest } from 'msw'; -import { setupServer } from 'msw/node'; - -// Simplified copy of TokenFactory in @backstage/plugin-auth-backend -interface AnyJWK extends Record { - use: 'sig'; - alg: string; - kid: string; - kty: string; -} -class FakeTokenFactory { - private readonly keys = new Array(); - - constructor( - private readonly options: { - issuer: string; - keyDurationSeconds: number; - }, - ) {} - - async issueToken(params: { - claims: { - sub: string; - ent?: string[]; - }; - }): Promise { - const pair = await generateKeyPair('RS256'); - const publicKey = await exportJWK(pair.publicKey); - const kid = uuid(); - publicKey.kid = kid; - this.keys.push(publicKey as AnyJWK); - - const iss = this.options.issuer; - const sub = params.claims.sub; - const ent = params.claims.ent; - const aud = 'backstage'; - const iat = Math.floor(Date.now() / 1000); - const exp = iat + this.options.keyDurationSeconds; - - return new SignJWT({ iss, sub, aud, iat, exp, ent, kid }) - .setProtectedHeader({ alg: 'RS256', ent: ent, kid: kid }) - .setIssuer(iss) - .setAudience(aud) - .setSubject(sub) - .setIssuedAt(iat) - .setExpirationTime(exp) - .sign(pair.privateKey); - } - - async listPublicKeys(): Promise<{ keys: AnyJWK[] }> { - return { keys: this.keys }; - } -} - -describe('ExternalTokenHandler', () => { - const server = setupServer(); - registerMswTestHooks(server); - - it('skips over inner handlers that do not match, and applies plugin restrictions', async () => { - const handler1: TokenHandler = { - add: jest.fn(), - verifyToken: jest.fn().mockResolvedValue(undefined), - }; - - const handler2: TokenHandler = { - add: jest.fn(), - verifyToken: jest.fn().mockResolvedValue({ - subject: 'sub', - allAccessRestrictions: new Map( - Object.entries({ - plugin1: { - permissionNames: ['do.it'], - } satisfies BackstagePrincipalAccessRestrictions, - }), - ), - }), - }; - - const plugin1 = new ExternalTokenHandler('plugin1', [handler1, handler2]); - const plugin2 = new ExternalTokenHandler('plugin2', [handler1, handler2]); - - await expect(plugin1.verifyToken('token')).resolves.toEqual({ - subject: 'sub', - accessRestrictions: { permissionNames: ['do.it'] }, - }); - await expect( - plugin2.verifyToken('token'), - ).rejects.toThrowErrorMatchingInlineSnapshot( - `"This token's access is restricted to plugin(s) 'plugin1'"`, - ); - }); - - it('successfully parses known methods', async () => { - const legacyKey = randomBytes(24); - - const factory = new FakeTokenFactory({ - issuer: 'blah', - keyDurationSeconds: 100, - }); - - server.use( - rest.get( - 'https://example.com/.well-known/jwks.json', - async (_, res, ctx) => { - const keys = await factory.listPublicKeys(); - return res(ctx.json(keys)); - }, - ), - ); - - const handler = ExternalTokenHandler.create({ - ownPluginId: 'catalog', - logger: mockServices.logger.mock(), - config: mockServices.rootConfig({ - data: { - backend: { - auth: { - externalAccess: [ - { - type: 'legacy', - options: { - secret: legacyKey.toString('base64'), - subject: 'legacy-subject', - }, - accessRestrictions: [ - { plugin: 'catalog', permission: 'catalog.entity.read' }, - ], - }, - { - type: 'static', - options: { - token: 'defdefdef', - subject: 'static-subject', - }, - accessRestrictions: [ - { plugin: 'catalog', permission: 'catalog.entity.read' }, - ], - }, - { - type: 'jwks', - options: { - url: 'https://example.com/.well-known/jwks.json', - algorithm: 'RS256', - issuer: 'blah', - audience: 'backstage', - subjectPrefix: 'custom-prefix', - }, - accessRestrictions: [ - { plugin: 'catalog', permission: 'catalog.entity.read' }, - ], - }, - ], - }, - }, - }, - }), - }); - - const legacyToken = await new SignJWT({ - sub: 'backstage-server', - exp: DateTime.now().plus({ minutes: 1 }).toUnixInteger(), - }) - .setProtectedHeader({ alg: 'HS256' }) - .sign(legacyKey); - - await expect(handler.verifyToken(legacyToken)).resolves.toEqual({ - subject: 'legacy-subject', - accessRestrictions: { permissionNames: ['catalog.entity.read'] }, - }); - - await expect(handler.verifyToken('defdefdef')).resolves.toEqual({ - subject: 'static-subject', - accessRestrictions: { permissionNames: ['catalog.entity.read'] }, - }); - - const jwksToken = await factory.issueToken({ - claims: { sub: 'jwks-subject' }, - }); - await expect(handler.verifyToken(jwksToken)).resolves.toEqual({ - subject: 'external:custom-prefix:jwks-subject', - accessRestrictions: { permissionNames: ['catalog.entity.read'] }, - }); - }); -}); diff --git a/packages/backend-defaults/src/entrypoints/auth/external/ExternalTokenHandler.ts b/packages/backend-defaults/src/entrypoints/auth/external/ExternalTokenHandler.ts deleted file mode 100644 index 72eeae6fcf..0000000000 --- a/packages/backend-defaults/src/entrypoints/auth/external/ExternalTokenHandler.ts +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright 2024 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { - BackstagePrincipalAccessRestrictions, - LoggerService, - RootConfigService, -} from '@backstage/backend-plugin-api'; -import { NotAllowedError } from '@backstage/errors'; -import { LegacyTokenHandler } from './legacy'; -import { StaticTokenHandler } from './static'; -import { JWKSHandler } from './jwks'; -import { TokenHandler } from './types'; - -const NEW_CONFIG_KEY = 'backend.auth.externalAccess'; -const OLD_CONFIG_KEY = 'backend.auth.keys'; -let loggedDeprecationWarning = false; - -/** - * Handles all types of external caller token types (i.e. not Backstage user - * tokens, nor Backstage backend plugin tokens). - * - * @internal - */ -export class ExternalTokenHandler { - static create(options: { - ownPluginId: string; - config: RootConfigService; - logger: LoggerService; - }): ExternalTokenHandler { - const { ownPluginId, config, logger } = options; - - const staticHandler = new StaticTokenHandler(); - const legacyHandler = new LegacyTokenHandler(); - const jwksHandler = new JWKSHandler(); - const handlers: Record = { - static: staticHandler, - legacy: legacyHandler, - jwks: jwksHandler, - }; - - // Load the new-style handlers - const handlerConfigs = config.getOptionalConfigArray(NEW_CONFIG_KEY) ?? []; - for (const handlerConfig of handlerConfigs) { - const type = handlerConfig.getString('type'); - const handler = handlers[type]; - if (!handler) { - const valid = Object.keys(handlers) - .map(k => `'${k}'`) - .join(', '); - throw new Error( - `Unknown type '${type}' in ${NEW_CONFIG_KEY}, expected one of ${valid}`, - ); - } - handler.add(handlerConfig); - } - - // Load the old keys too - const legacyConfigs = config.getOptionalConfigArray(OLD_CONFIG_KEY) ?? []; - if (legacyConfigs.length && !loggedDeprecationWarning) { - loggedDeprecationWarning = true; - logger.warn( - `DEPRECATION WARNING: The ${OLD_CONFIG_KEY} config has been replaced by ${NEW_CONFIG_KEY}, see https://backstage.io/docs/auth/service-to-service-auth`, - ); - } - for (const handlerConfig of legacyConfigs) { - legacyHandler.addOld(handlerConfig); - } - - return new ExternalTokenHandler(ownPluginId, Object.values(handlers)); - } - - constructor( - private readonly ownPluginId: string, - private readonly handlers: TokenHandler[], - ) {} - - async verifyToken(token: string): Promise< - | { - subject: string; - accessRestrictions?: BackstagePrincipalAccessRestrictions; - } - | undefined - > { - for (const handler of this.handlers) { - const result = await handler.verifyToken(token); - if (result) { - const { allAccessRestrictions, ...rest } = result; - if (allAccessRestrictions) { - const accessRestrictions = allAccessRestrictions.get( - this.ownPluginId, - ); - if (!accessRestrictions) { - const valid = [...allAccessRestrictions.keys()] - .map(k => `'${k}'`) - .join(', '); - throw new NotAllowedError( - `This token's access is restricted to plugin(s) ${valid}`, - ); - } - - return { - ...rest, - accessRestrictions, - }; - } - - return rest; - } - } - - return undefined; - } -} diff --git a/packages/backend-defaults/src/entrypoints/auth/external/helpers.ts b/packages/backend-defaults/src/entrypoints/auth/external/helpers.ts index d6aa0a01ff..94d289bd7c 100644 --- a/packages/backend-defaults/src/entrypoints/auth/external/helpers.ts +++ b/packages/backend-defaults/src/entrypoints/auth/external/helpers.ts @@ -15,7 +15,7 @@ */ import { Config } from '@backstage/config'; -import { AccessRestriptionsMap } from './types'; +import { AccessRestrictionsMap, ExternalTokenHandler } from './types'; /** * Parses and returns the `accessRestrictions` configuration from an @@ -25,12 +25,12 @@ import { AccessRestriptionsMap } from './types'; */ export function readAccessRestrictionsFromConfig( externalAccessEntryConfig: Config, -): AccessRestriptionsMap | undefined { +): AccessRestrictionsMap | undefined { const configs = externalAccessEntryConfig.getOptionalConfigArray('accessRestrictions') ?? []; - const result: AccessRestriptionsMap = new Map(); + const result: AccessRestrictionsMap = new Map(); for (const config of configs) { const validKeys = ['plugin', 'permission', 'permissionAttribute']; for (const key of config.keys()) { @@ -147,3 +147,37 @@ function readPermissionAttributes(externalAccessEntryConfig: Config) { return Object.keys(result).length ? result : undefined; } + +/** + * Creates an external token handler with the provided implementation. + * + * This helper function simplifies the creation of external token handlers by + * providing type safety and a consistent API. External token handlers are used + * to validate tokens from external systems that need to authenticate with Backstage. + * + * See {@link https://backstage.io/docs/auth/service-to-service-auth#adding-custom-externaltokenhandler | the service-to-service auth docs} + * for more information about implementing custom external token handlers. + * + * @public + * @param handler - The external token handler implementation with type, initialize, and verifyToken methods + * @returns The same handler instance, typed as ExternalTokenHandler + * + * @example + * ```ts + * const customHandler = createExternalTokenHandler({ + * type: 'custom', + * initialize({ options }) { + * return { apiKey: options.getString('apiKey') }; + * }, + * async verifyToken(token, context) { + * // Custom validation logic here + * return { subject: 'custom:user' }; + * }, + * }); + * ``` + */ +export function createExternalTokenHandler( + handler: ExternalTokenHandler, +): ExternalTokenHandler { + return handler; +} diff --git a/packages/backend-defaults/src/entrypoints/auth/external/jwks.test.ts b/packages/backend-defaults/src/entrypoints/auth/external/jwks.test.ts index 321e1823aa..327173fdee 100644 --- a/packages/backend-defaults/src/entrypoints/auth/external/jwks.test.ts +++ b/packages/backend-defaults/src/entrypoints/auth/external/jwks.test.ts @@ -20,7 +20,7 @@ import { SignJWT, exportJWK, generateKeyPair } from 'jose'; import { rest } from 'msw'; import { setupServer } from 'msw/node'; import { v4 as uuid } from 'uuid'; -import { JWKSHandler } from './jwks'; +import { jwksTokenHandler } from './jwks'; // Simplified copy of TokenFactory in @backstage/plugin-auth-backend interface AnyJWK extends Record { @@ -31,13 +31,14 @@ interface AnyJWK extends Record { } class FakeTokenFactory { private readonly keys = new Array(); + private readonly options: { + issuer: string; + keyDurationSeconds: number; + }; - constructor( - private readonly options: { - issuer: string; - keyDurationSeconds: number; - }, - ) {} + constructor(options: { issuer: string; keyDurationSeconds: number }) { + this.options = options; + } async issueToken(params: { claims: { @@ -101,114 +102,45 @@ describe('JWKSHandler', () => { it('verifies token with valid entry', async () => { const validEntry = { - options: { - url: `${mockBaseUrl}/.well-known/jwks.json`, - algorithm: 'RS256', - issuer: mockBaseUrl, - audience: 'backstage', - }, + url: `${mockBaseUrl}/.well-known/jwks.json`, + algorithm: 'RS256', + issuer: mockBaseUrl, + audience: 'backstage', }; - const jwksHandler = new JWKSHandler(); - - jwksHandler.add(new ConfigReader(validEntry)); + const context = jwksTokenHandler.initialize({ + options: new ConfigReader(validEntry), + }); const token = await factory.issueToken({ claims: { sub: mockSubject }, }); - const result = await jwksHandler.verifyToken(token); + const result = await jwksTokenHandler.verifyToken(token, context); expect(result).toEqual({ subject: `external:${mockSubject}` }); }); - it('skips invalid entry and continues verification', async () => { - const invalidEntry = { - options: { - url: `${mockBaseUrl}/.well-known/jwks.json`, - algorithm: 'RS256', - issuer: ['fakeIssuer'], - audience: ['fakeAud'], - }, - }; - - const validEntry = { - options: { - url: `${mockBaseUrl}/.well-known/jwks.json`, - algorithm: 'RS256', - issuer: ['multiple-issuers', mockBaseUrl], - audience: ['multiple-audiences', 'backstage'], - }, - }; - const jwksHandler = new JWKSHandler(); - - jwksHandler.add(new ConfigReader(invalidEntry)); - jwksHandler.add(new ConfigReader(validEntry)); - - const token = await factory.issueToken({ - claims: { sub: mockSubject }, - }); - - const result = await jwksHandler.verifyToken(token); - - expect(result).toEqual({ subject: `external:${mockSubject}` }); - }); - - it('returns undefined if no valid entry found', async () => { - const invalidEntry1 = { - options: { - url: `${mockBaseUrl}/.well-known/jwks.json`, - algorithm: 'RS256', - issuer: 'wrong', - }, - }; - - const invalidEntry2 = { - options: { - url: `${mockBaseUrl}/.well-known/jwks.json`, - algorithm: ['HS256'], - audience: 'wrong', - }, - }; - const jwksHandler = new JWKSHandler(); - - jwksHandler.add(new ConfigReader(invalidEntry1)); - jwksHandler.add(new ConfigReader(invalidEntry2)); - - const token = await factory.issueToken({ - claims: { sub: mockSubject }, - }); - - const result = await jwksHandler.verifyToken(token); - - expect(result).toBeUndefined(); - }); - it('rejects bad config', () => { - const jwksHandler = new JWKSHandler(); - expect(() => { - jwksHandler.add( - new ConfigReader({ - options: { - url: 'https://exampl e.com/jwks', - }, + return jwksTokenHandler.initialize({ + options: new ConfigReader({ + url: 'https://exampl e.com/jwks', }), - ); + }); }).toThrow('Illegal JWKS URL, must be a set of non-space characters'); expect(() => { - jwksHandler.add( - new ConfigReader({ - options: { - url: 'https://example.com/jwks\n', - }, + return jwksTokenHandler.initialize({ + options: new ConfigReader({ + url: 'https://example.com/jwks\n', }), - ); + }); }).toThrow('Illegal JWKS URL, must be a set of non-space characters'); }); it('gracefully handles no added tokens', async () => { - const handler = new JWKSHandler(); - await expect(handler.verifyToken('ghi')).resolves.toBeUndefined(); + await expect( + jwksTokenHandler.verifyToken('ghi', {} as any), + ).resolves.toBeUndefined(); }); it('uses custom subject prefix if provided', async () => { @@ -221,41 +153,18 @@ describe('JWKSHandler', () => { subjectPrefix: 'custom-prefix', }, }; - const jwksHandler = new JWKSHandler(); - - jwksHandler.add(new ConfigReader(validEntry)); + const context = jwksTokenHandler.initialize({ + options: new ConfigReader(validEntry.options), + }); const token = await factory.issueToken({ claims: { sub: mockSubject }, }); - const result = await jwksHandler.verifyToken(token); + const result = await jwksTokenHandler.verifyToken(token, context); expect(result).toEqual({ subject: `external:${validEntry.options.subjectPrefix}:${mockSubject}`, }); }); - - it('carries over access restrictions', async () => { - const jwksHandler = new JWKSHandler(); - jwksHandler.add( - new ConfigReader({ - options: { - url: `${mockBaseUrl}/.well-known/jwks.json`, - }, - accessRestrictions: [{ plugin: 'scaffolder', permission: 'do.it' }], - }), - ); - - const token = await factory.issueToken({ claims: { sub: mockSubject } }); - - await expect(jwksHandler.verifyToken(token)).resolves.toEqual({ - subject: `external:${mockSubject}`, - allAccessRestrictions: new Map( - Object.entries({ - scaffolder: { permissionNames: ['do.it'] }, - }), - ), - }); - }); }); diff --git a/packages/backend-defaults/src/entrypoints/auth/external/jwks.ts b/packages/backend-defaults/src/entrypoints/auth/external/jwks.ts index d88dc62a47..33b55df775 100644 --- a/packages/backend-defaults/src/entrypoints/auth/external/jwks.ts +++ b/packages/backend-defaults/src/entrypoints/auth/external/jwks.ts @@ -15,51 +15,40 @@ */ import { jwtVerify, createRemoteJWKSet, JWTVerifyGetKey } from 'jose'; -import { Config } from '@backstage/config'; import { + createExternalTokenHandler, readAccessRestrictionsFromConfig, readStringOrStringArrayFromConfig, } from './helpers'; -import { AccessRestriptionsMap, TokenHandler } from './types'; +import { AccessRestrictionsMap } from './types'; -/** - * Handles `type: jwks` access. - * - * @internal - */ -export class JWKSHandler implements TokenHandler { - #entries: Array<{ - algorithms?: string[]; - audiences?: string[]; - issuers?: string[]; - subjectPrefix?: string; - url: URL; - jwks: JWTVerifyGetKey; - allAccessRestrictions?: AccessRestriptionsMap; - }> = []; +type JWKSTokenContext = { + algorithms?: string[]; + audiences?: string[]; + issuers?: string[]; + subjectPrefix?: string; + url: URL; + jwks: JWTVerifyGetKey; + allAccessRestrictions?: AccessRestrictionsMap; +}; - add(config: Config) { - if (!config.getString('options.url').match(/^\S+$/)) { +export const jwksTokenHandler = createExternalTokenHandler({ + type: 'jwks', + initialize({ options }): JWKSTokenContext { + if (!options.getString('url').match(/^\S+$/)) { throw new Error( 'Illegal JWKS URL, must be a set of non-space characters', ); } - const algorithms = readStringOrStringArrayFromConfig( - config, - 'options.algorithm', - ); - const issuers = readStringOrStringArrayFromConfig(config, 'options.issuer'); - const audiences = readStringOrStringArrayFromConfig( - config, - 'options.audience', - ); - const subjectPrefix = config.getOptionalString('options.subjectPrefix'); - const url = new URL(config.getString('options.url')); + const algorithms = readStringOrStringArrayFromConfig(options, 'algorithm'); + const issuers = readStringOrStringArrayFromConfig(options, 'issuer'); + const audiences = readStringOrStringArrayFromConfig(options, 'audience'); + const subjectPrefix = options.getOptionalString('subjectPrefix'); + const url = new URL(options.getString('url')); const jwks = createRemoteJWKSet(url); - const allAccessRestrictions = readAccessRestrictionsFromConfig(config); - - this.#entries.push({ + const allAccessRestrictions = readAccessRestrictionsFromConfig(options); + return { algorithms, audiences, issuers, @@ -67,33 +56,30 @@ export class JWKSHandler implements TokenHandler { subjectPrefix, url, allAccessRestrictions, - }); - } + }; + }, - async verifyToken(token: string) { - for (const entry of this.#entries) { - try { - const { - payload: { sub }, - } = await jwtVerify(token, entry.jwks, { - algorithms: entry.algorithms, - issuer: entry.issuers, - audience: entry.audiences, - }); + async verifyToken(token: string, context: JWKSTokenContext) { + try { + const { + payload: { sub }, + } = await jwtVerify(token, context.jwks, { + algorithms: context.algorithms, + issuer: context.issuers, + audience: context.audiences, + }); - if (sub) { - const prefix = entry.subjectPrefix - ? `external:${entry.subjectPrefix}:` - : 'external:'; - return { - subject: `${prefix}${sub}`, - allAccessRestrictions: entry.allAccessRestrictions, - }; - } - } catch { - continue; + if (sub) { + const prefix = context.subjectPrefix + ? `external:${context.subjectPrefix}:` + : 'external:'; + return { + subject: `${prefix}${sub}`, + }; } + } catch { + return undefined; } return undefined; - } -} + }, +}); diff --git a/packages/backend-defaults/src/entrypoints/auth/external/legacy.test.ts b/packages/backend-defaults/src/entrypoints/auth/external/legacy.test.ts index c674e46ec9..48dbcc2418 100644 --- a/packages/backend-defaults/src/entrypoints/auth/external/legacy.test.ts +++ b/packages/backend-defaults/src/entrypoints/auth/external/legacy.test.ts @@ -18,49 +18,35 @@ import { ConfigReader } from '@backstage/config'; import { randomBytes } from 'crypto'; import { SignJWT, importJWK } from 'jose'; import { DateTime } from 'luxon'; -import { LegacyTokenHandler } from './legacy'; +import { legacyTokenHandler } from './legacy'; describe('LegacyTokenHandler', () => { - const tokenHandler = new LegacyTokenHandler(); const key1 = randomBytes(24); const key2 = randomBytes(24); const key3 = randomBytes(24); - const accessRestrictions1 = new Map( - Object.entries({ - scaffolder: {}, - }), - ); - const accessRestrictions2 = new Map( - Object.entries({ - catalog: { permissionNames: ['catalog.entity.read'] }, - }), - ); - tokenHandler.add( - new ConfigReader({ - options: { - secret: key1.toString('base64'), - subject: 'key1', - }, - accessRestrictions: [{ plugin: 'scaffolder' }], + const tokenHandler = legacyTokenHandler; + + const context1 = tokenHandler.initialize({ + options: new ConfigReader({ + secret: key1.toString('base64'), + subject: 'key1', }), - ); - tokenHandler.add( - new ConfigReader({ - options: { - secret: key2.toString('base64'), - subject: 'key2', - }, - accessRestrictions: [ - { plugin: 'catalog', permission: 'catalog.entity.read' }, - ], + }); + + const context2 = tokenHandler.initialize({ + options: new ConfigReader({ + secret: key2.toString('base64'), + subject: 'key2', }), - ); - tokenHandler.addOld( - new ConfigReader({ + }); + + const contextWithLegacy = tokenHandler.initialize({ + options: new ConfigReader({ secret: key3.toString('base64'), }), - ); + legacy: true, + }); it('should verify valid tokens', async () => { const token1 = await new SignJWT({ @@ -70,9 +56,8 @@ describe('LegacyTokenHandler', () => { .setProtectedHeader({ alg: 'HS256' }) .sign(key1); - await expect(tokenHandler.verifyToken(token1)).resolves.toEqual({ + await expect(tokenHandler.verifyToken(token1, context1)).resolves.toEqual({ subject: 'key1', - allAccessRestrictions: accessRestrictions1, }); const token2 = await new SignJWT({ @@ -82,9 +67,8 @@ describe('LegacyTokenHandler', () => { .setProtectedHeader({ alg: 'HS256' }) .sign(key2); - await expect(tokenHandler.verifyToken(token2)).resolves.toEqual({ + await expect(tokenHandler.verifyToken(token2, context2)).resolves.toEqual({ subject: 'key2', - allAccessRestrictions: accessRestrictions2, }); const token3 = await new SignJWT({ @@ -94,7 +78,9 @@ describe('LegacyTokenHandler', () => { .setProtectedHeader({ alg: 'HS256' }) .sign(key3); - await expect(tokenHandler.verifyToken(token3)).resolves.toEqual({ + await expect( + tokenHandler.verifyToken(token3, contextWithLegacy), + ).resolves.toEqual({ subject: 'external:backstage-plugin', }); }); @@ -107,10 +93,18 @@ describe('LegacyTokenHandler', () => { .setProtectedHeader({ alg: 'HS256' }) .sign(key1); - await expect(tokenHandler.verifyToken(validToken)).resolves.toBeUndefined(); + await expect( + tokenHandler.verifyToken(validToken, context1), + ).resolves.toBeUndefined(); + await expect( + tokenHandler.verifyToken(validToken, context2), + ).resolves.toBeUndefined(); await expect( - tokenHandler.verifyToken('statickeyblaaa'), + tokenHandler.verifyToken('statickeyblaaa', context1), + ).resolves.toBeUndefined(); + await expect( + tokenHandler.verifyToken('statickeyblaaa', context2), ).resolves.toBeUndefined(); const randomToken = await new SignJWT({ @@ -120,7 +114,10 @@ describe('LegacyTokenHandler', () => { .setProtectedHeader({ alg: 'HS256' }) .sign(randomBytes(24)); await expect( - tokenHandler.verifyToken(randomToken), + tokenHandler.verifyToken(randomToken, context1), + ).resolves.toBeUndefined(); + await expect( + tokenHandler.verifyToken(randomToken, context2), ).resolves.toBeUndefined(); const mockPublicKey = { @@ -144,7 +141,10 @@ describe('LegacyTokenHandler', () => { .sign(await importJWK(mockPrivateKey)); await expect( - tokenHandler.verifyToken(keyWithWrongAlg), + tokenHandler.verifyToken(keyWithWrongAlg, context1), + ).resolves.toBeUndefined(); + await expect( + tokenHandler.verifyToken(keyWithWrongAlg, context2), ).resolves.toBeUndefined(); }); @@ -157,130 +157,151 @@ describe('LegacyTokenHandler', () => { .setProtectedHeader({ alg: 'HS256' }) .sign(key1); - await expect(tokenHandler.verifyToken(keyWithWrongExp)).rejects.toThrow( - /\"exp\" claim must be a number/, - ); + await expect( + tokenHandler.verifyToken(keyWithWrongExp, context1), + ).rejects.toThrow(/\"exp\" claim must be a number/); }); it('rejects bad config', () => { - const handler = new LegacyTokenHandler(); + const handler = legacyTokenHandler; // new style add, bad secrets expect(() => - handler.add( - new ConfigReader({ options: { _missingsecret: true, subject: 'ok' } }), - ), - ).toThrowErrorMatchingInlineSnapshot( - `"Missing required config value at 'options.secret' in 'mock-config'"`, - ); - expect(() => - handler.add(new ConfigReader({ options: { secret: '', subject: 'ok' } })), - ).toThrowErrorMatchingInlineSnapshot( - `"Invalid type in config for key 'options.secret' in 'mock-config', got empty-string, wanted string"`, - ); - expect(() => - handler.add( - new ConfigReader({ options: { secret: 'has spaces', subject: 'ok' } }), - ), - ).toThrowErrorMatchingInlineSnapshot( - `"Illegal secret, must be a valid base64 string"`, - ); - expect(() => - handler.add( - new ConfigReader({ - options: { secret: 'hasnewline\n', subject: 'ok' }, + handler.initialize({ + options: new ConfigReader({ + _missingsecret: true, + subject: 'ok', }), - ), - ).toThrowErrorMatchingInlineSnapshot( - `"Illegal secret, must be a valid base64 string"`, - ); - expect(() => - handler.add(new ConfigReader({ options: { secret: 3, subject: 'ok' } })), - ).toThrowErrorMatchingInlineSnapshot( - `"Invalid type in config for key 'options.secret' in 'mock-config', got number, wanted string"`, - ); - - // new style add, bad subjects - expect(() => - handler.add( - new ConfigReader({ - options: { secret: 'b2s=', _missingsubject: true }, - }), - ), - ).toThrowErrorMatchingInlineSnapshot( - `"Missing required config value at 'options.subject' in 'mock-config'"`, - ); - expect(() => - handler.add( - new ConfigReader({ options: { secret: 'b2s=', subject: '' } }), - ), - ).toThrowErrorMatchingInlineSnapshot( - `"Invalid type in config for key 'options.subject' in 'mock-config', got empty-string, wanted string"`, - ); - expect(() => - handler.add( - new ConfigReader({ - options: { secret: 'b2s=', subject: 'has spaces' }, - }), - ), - ).toThrowErrorMatchingInlineSnapshot( - `"Illegal subject, must be a set of non-space characters"`, - ); - expect(() => - handler.add( - new ConfigReader({ - options: { secret: 'b2s=', subject: 'hasnewline\n' }, - }), - ), - ).toThrowErrorMatchingInlineSnapshot( - `"Illegal subject, must be a set of non-space characters"`, - ); - expect(() => - handler.add( - new ConfigReader({ options: { secret: 'b2s=', subject: 3 } }), - ), - ).toThrowErrorMatchingInlineSnapshot( - `"Invalid type in config for key 'options.subject' in 'mock-config', got number, wanted string"`, - ); - - // new style add, bad access restrictions - expect(() => - handler.add( - new ConfigReader({ - options: { secret: 'b2s=', subject: 'subject' }, - accessRestrictions: [{ plugin: ['a'] }], - }), - ), - ).toThrowErrorMatchingInlineSnapshot( - `"Invalid type in config for key 'accessRestrictions[0].plugin' in 'mock-config', got array, wanted string"`, - ); - - // old style add - expect(() => - handler.addOld(new ConfigReader({ secret: 'b2s=' })), - ).not.toThrow(); - expect(() => - handler.addOld(new ConfigReader({ _missingsecret: true })), + }), ).toThrowErrorMatchingInlineSnapshot( `"Missing required config value at 'secret' in 'mock-config'"`, ); expect(() => - handler.addOld(new ConfigReader({ secret: '' })), + handler.initialize({ + options: new ConfigReader({ secret: '', subject: 'ok' }), + }), ).toThrowErrorMatchingInlineSnapshot( `"Invalid type in config for key 'secret' in 'mock-config', got empty-string, wanted string"`, ); expect(() => - handler.addOld(new ConfigReader({ secret: 'has spaces' })), + handler.initialize({ + options: new ConfigReader({ + secret: 'has spaces', + subject: 'ok', + }), + }), ).toThrowErrorMatchingInlineSnapshot( `"Illegal secret, must be a valid base64 string"`, ); expect(() => - handler.addOld(new ConfigReader({ secret: 'hasnewline\n' })), + handler.initialize({ + options: new ConfigReader({ + secret: 'hasnewline\n', + subject: 'ok', + }), + }), ).toThrowErrorMatchingInlineSnapshot( `"Illegal secret, must be a valid base64 string"`, ); expect(() => - handler.addOld(new ConfigReader({ secret: 3 })), + handler.initialize({ + options: new ConfigReader({ secret: 3, subject: 'ok' }), + }), + ).toThrowErrorMatchingInlineSnapshot( + `"Invalid type in config for key 'secret' in 'mock-config', got number, wanted string"`, + ); + + // new style add, bad subjects + expect(() => + handler.initialize({ + options: new ConfigReader({ + secret: 'b2s=', + _missingsubject: true, + }), + }), + ).toThrowErrorMatchingInlineSnapshot( + `"Missing required config value at 'subject' in 'mock-config'"`, + ); + expect(() => + handler.initialize({ + options: new ConfigReader({ secret: 'b2s=', subject: '' }), + }), + ).toThrowErrorMatchingInlineSnapshot( + `"Invalid type in config for key 'subject' in 'mock-config', got empty-string, wanted string"`, + ); + expect(() => + handler.initialize({ + options: new ConfigReader({ + secret: 'b2s=', + subject: 'has spaces', + }), + }), + ).toThrowErrorMatchingInlineSnapshot( + `"Illegal subject, must be a set of non-space characters"`, + ); + expect(() => + handler.initialize({ + options: new ConfigReader({ + secret: 'b2s=', + subject: 'hasnewline\n', + }), + }), + ).toThrowErrorMatchingInlineSnapshot( + `"Illegal subject, must be a set of non-space characters"`, + ); + expect(() => + handler.initialize({ + options: new ConfigReader({ secret: 'b2s=', subject: 3 }), + }), + ).toThrowErrorMatchingInlineSnapshot( + `"Invalid type in config for key 'subject' in 'mock-config', got number, wanted string"`, + ); + + // old style add + expect(() => + handler.initialize({ + options: new ConfigReader({ secret: 'b2s=' }), + legacy: true, + }), + ).not.toThrow(); + + expect(() => + handler.initialize({ + options: new ConfigReader({ _missingsecret: true }), + legacy: true, + }), + ).toThrowErrorMatchingInlineSnapshot( + `"Missing required config value at 'secret' in 'mock-config'"`, + ); + expect(() => + handler.initialize({ + options: new ConfigReader({ secret: '' }), + legacy: true, + }), + ).toThrowErrorMatchingInlineSnapshot( + `"Invalid type in config for key 'secret' in 'mock-config', got empty-string, wanted string"`, + ); + expect(() => + handler.initialize({ + options: new ConfigReader({ secret: 'has spaces' }), + legacy: true, + }), + ).toThrowErrorMatchingInlineSnapshot( + `"Illegal secret, must be a valid base64 string"`, + ); + expect(() => + handler.initialize({ + options: new ConfigReader({ secret: 'hasnewline\n' }), + legacy: true, + }), + ).toThrowErrorMatchingInlineSnapshot( + `"Illegal secret, must be a valid base64 string"`, + ); + expect(() => + handler.initialize({ + options: new ConfigReader({ secret: 3 }), + legacy: true, + }), ).toThrowErrorMatchingInlineSnapshot( `"Invalid type in config for key 'secret' in 'mock-config', got number, wanted string"`, ); diff --git a/packages/backend-defaults/src/entrypoints/auth/external/legacy.ts b/packages/backend-defaults/src/entrypoints/auth/external/legacy.ts index 9c60e70707..2ab8c3a5ea 100644 --- a/packages/backend-defaults/src/entrypoints/auth/external/legacy.ts +++ b/packages/backend-defaults/src/entrypoints/auth/external/legacy.ts @@ -16,72 +16,56 @@ import { Config } from '@backstage/config'; import { base64url, decodeJwt, decodeProtectedHeader, jwtVerify } from 'jose'; -import { readAccessRestrictionsFromConfig } from './helpers'; -import { AccessRestriptionsMap, TokenHandler } from './types'; -/** - * Handles `type: legacy` access. - * - * @internal - */ -export class LegacyTokenHandler implements TokenHandler { - #entries = new Array<{ - key: Uint8Array; - result: { - subject: string; - allAccessRestrictions?: AccessRestriptionsMap; - }; - }>(); +import { ExternalTokenHandler } from './types'; - add(config: Config) { - const allAccessRestrictions = readAccessRestrictionsFromConfig(config); - this.#doAdd( - config.getString('options.secret'), - config.getString('options.subject'), - allAccessRestrictions, - ); - } +export type LegacyConfigWrapper = { + legacy: boolean; + config: Config; +}; - // used only for the old backend.auth.keys array - addOld(config: Config) { - // This choice of subject is for compatibility reasons - this.#doAdd(config.getString('secret'), 'external:backstage-plugin'); - } +type LegacyTokenHandlerContext = { + key: Uint8Array; + + subject: string; +}; + +type LegacyTokenHandlerOverloaded = + ExternalTokenHandler & { + initialize(ctx: { + options: Config; + legacy: true; + }): LegacyTokenHandlerContext; + }; + +export const legacyTokenHandler: LegacyTokenHandlerOverloaded = { + type: 'legacy', + initialize(ctx: { + options: Config; + legacy?: true; + }): LegacyTokenHandlerContext { + const secret = ctx.options.getString('secret'); + const subject = ctx.legacy + ? 'external:backstage-plugin' + : ctx.options.getString('subject'); - #doAdd( - secret: string, - subject: string, - allAccessRestrictions?: AccessRestriptionsMap, - ) { if (!secret.match(/^\S+$/)) { throw new Error('Illegal secret, must be a valid base64 string'); } else if (!subject.match(/^\S+$/)) { throw new Error('Illegal subject, must be a set of non-space characters'); } - let key: Uint8Array; try { - key = base64url.decode(secret); + return { + key: base64url.decode(secret), + subject, + }; } catch { throw new Error('Illegal secret, must be a valid base64 string'); } + }, - if (this.#entries.some(e => e.key === key)) { - throw new Error( - 'Legacy externalAccess token was declared more than once', - ); - } - - this.#entries.push({ - key, - result: { - subject, - allAccessRestrictions, - }, - }); - } - - async verifyToken(token: string) { + async verifyToken(token: string, context: LegacyTokenHandlerContext) { // First do a duck typing check to see if it remotely looks like a legacy token try { // We do a fair amount of checking upfront here. Since we aren't certain @@ -102,19 +86,18 @@ export class LegacyTokenHandler implements TokenHandler { return undefined; } - for (const { key, result } of this.#entries) { - try { - await jwtVerify(token, key); - return result; - } catch (e) { - if (e.code !== 'ERR_JWS_SIGNATURE_VERIFICATION_FAILED') { - throw e; - } - // Otherwise continue to try the next key + try { + await jwtVerify(token, context.key); + return { + subject: context.subject, + }; + } catch (error) { + if (error.code !== 'ERR_JWS_SIGNATURE_VERIFICATION_FAILED') { + throw error; } } // None of the signing keys matched return undefined; - } -} + }, +}; diff --git a/packages/backend-defaults/src/entrypoints/auth/external/static.test.ts b/packages/backend-defaults/src/entrypoints/auth/external/static.test.ts index a5945daba9..10e78d2dbf 100644 --- a/packages/backend-defaults/src/entrypoints/auth/external/static.test.ts +++ b/packages/backend-defaults/src/entrypoints/auth/external/static.test.ts @@ -15,137 +15,146 @@ */ import { ConfigReader } from '@backstage/config'; -import { StaticTokenHandler } from './static'; +import { staticTokenHandler } from './static'; describe('StaticTokenHandler', () => { it('accepts any of the added list of tokens', async () => { - const handler = new StaticTokenHandler(); - handler.add( - new ConfigReader({ - options: { token: 'abcabcabc', subject: 'one' }, - accessRestrictions: [{ plugin: 'scaffolder' }], + const context1 = staticTokenHandler.initialize({ + options: new ConfigReader({ + token: 'abcabcabc', + subject: 'one', }), - ); - handler.add( - new ConfigReader({ - options: { token: 'defdefdef', subject: 'two' }, - accessRestrictions: [ - { plugin: 'catalog', permission: 'catalog.entity.read' }, - ], + }); + const context2 = staticTokenHandler.initialize({ + options: new ConfigReader({ + token: 'defdefdef', + subject: 'two', }), - ); - const accessRestrictionsOne = new Map(Object.entries({ scaffolder: {} })); - const accessRestrictionsTwo = new Map( - Object.entries({ - catalog: { - permissionNames: ['catalog.entity.read'], - }, - }), - ); + }); - await expect(handler.verifyToken('abcabcabc')).resolves.toEqual({ + await expect( + staticTokenHandler.verifyToken('abcabcabc', context1), + ).resolves.toEqual({ subject: 'one', - allAccessRestrictions: accessRestrictionsOne, }); - await expect(handler.verifyToken('defdefdef')).resolves.toEqual({ + await expect( + staticTokenHandler.verifyToken('defdefdef', context2), + ).resolves.toEqual({ subject: 'two', - allAccessRestrictions: accessRestrictionsTwo, }); - await expect(handler.verifyToken('ghighighi')).resolves.toBeUndefined(); + await expect( + staticTokenHandler.verifyToken('ghighighi', context1), + ).resolves.toBeUndefined(); }); it('gracefully handles no added tokens', async () => { - const handler = new StaticTokenHandler(); - await expect(handler.verifyToken('ghi')).resolves.toBeUndefined(); + await expect( + staticTokenHandler.verifyToken('ghi', {} as any), + ).resolves.toBeUndefined(); }); it('rejects bad config', () => { - const handler = new StaticTokenHandler(); - expect(() => - handler.add( - new ConfigReader({ options: { _missingtoken: true, subject: 'ok' } }), - ), + staticTokenHandler.initialize({ + options: new ConfigReader({ _missingtoken: true, subject: 'ok' }), + }), ).toThrowErrorMatchingInlineSnapshot( - `"Missing required config value at 'options.token' in 'mock-config'"`, + `"Missing required config value at 'token' in 'mock-config'"`, ); expect(() => - handler.add(new ConfigReader({ options: { token: '', subject: 'ok' } })), + staticTokenHandler.initialize({ + options: new ConfigReader({ token: '', subject: 'ok' }), + }), ).toThrowErrorMatchingInlineSnapshot( - `"Invalid type in config for key 'options.token' in 'mock-config', got empty-string, wanted string"`, + `"Invalid type in config for key 'token' in 'mock-config', got empty-string, wanted string"`, ); expect(() => - handler.add( - new ConfigReader({ options: { token: 'has spaces', subject: 'ok' } }), - ), - ).toThrowErrorMatchingInlineSnapshot( - `"Illegal token, must be a set of non-space characters"`, - ); - expect(() => - handler.add( - new ConfigReader({ - options: { - token: 'hasnewlinebutislongenough\n', - subject: 'ok', - }, + staticTokenHandler.initialize({ + options: new ConfigReader({ + token: 'has spaces', + subject: 'ok', }), - ), + }), ).toThrowErrorMatchingInlineSnapshot( `"Illegal token, must be a set of non-space characters"`, ); expect(() => - handler.add( - new ConfigReader({ options: { token: 'short', subject: 'ok' } }), - ), + staticTokenHandler.initialize({ + options: new ConfigReader({ + token: 'hasnewlinebutislongenough\n', + subject: 'ok', + }), + }), + ).toThrowErrorMatchingInlineSnapshot( + `"Illegal token, must be a set of non-space characters"`, + ); + expect(() => + staticTokenHandler.initialize({ + options: new ConfigReader({ + token: 'short', + subject: 'ok', + }), + }), ).toThrowErrorMatchingInlineSnapshot( `"Illegal token, must be at least 8 characters length"`, ); expect(() => - handler.add(new ConfigReader({ options: { token: 3, subject: 'ok' } })), + staticTokenHandler.initialize({ + options: new ConfigReader({ token: 3, subject: 'ok' }), + }), ).toThrowErrorMatchingInlineSnapshot( - `"Invalid type in config for key 'options.token' in 'mock-config', got number, wanted string"`, + `"Invalid type in config for key 'token' in 'mock-config', got number, wanted string"`, ); expect(() => - handler.add( - new ConfigReader({ - options: { token: 'validtoken', _missingsubject: true }, + staticTokenHandler.initialize({ + options: new ConfigReader({ + token: 'validtoken', + _missingsubject: true, }), - ), + }), ).toThrowErrorMatchingInlineSnapshot( - `"Missing required config value at 'options.subject' in 'mock-config'"`, + `"Missing required config value at 'subject' in 'mock-config'"`, ); expect(() => - handler.add( - new ConfigReader({ options: { token: 'validtoken', subject: '' } }), - ), + staticTokenHandler.initialize({ + options: new ConfigReader({ + token: 'validtoken', + subject: '', + }), + }), ).toThrowErrorMatchingInlineSnapshot( - `"Invalid type in config for key 'options.subject' in 'mock-config', got empty-string, wanted string"`, + `"Invalid type in config for key 'subject' in 'mock-config', got empty-string, wanted string"`, ); expect(() => - handler.add( - new ConfigReader({ - options: { token: 'validtoken', subject: 'has spaces' }, + staticTokenHandler.initialize({ + options: new ConfigReader({ + token: 'validtoken', + subject: 'has spaces', }), - ), + }), ).toThrowErrorMatchingInlineSnapshot( `"Illegal subject, must be a set of non-space characters"`, ); expect(() => - handler.add( - new ConfigReader({ - options: { token: 'validtoken', subject: 'hasnewline\n' }, + staticTokenHandler.initialize({ + options: new ConfigReader({ + token: 'validtoken', + subject: 'hasnewline\n', }), - ), + }), ).toThrowErrorMatchingInlineSnapshot( `"Illegal subject, must be a set of non-space characters"`, ); expect(() => - handler.add( - new ConfigReader({ options: { token: 'validtoken', subject: 3 } }), - ), + staticTokenHandler.initialize({ + options: new ConfigReader({ + token: 'validtoken', + subject: 3, + }), + }), ).toThrowErrorMatchingInlineSnapshot( - `"Invalid type in config for key 'options.subject' in 'mock-config', got number, wanted string"`, + `"Invalid type in config for key 'subject' in 'mock-config', got number, wanted string"`, ); }); }); diff --git a/packages/backend-defaults/src/entrypoints/auth/external/static.ts b/packages/backend-defaults/src/entrypoints/auth/external/static.ts index 1e89d8c6a1..502150d5a8 100644 --- a/packages/backend-defaults/src/entrypoints/auth/external/static.ts +++ b/packages/backend-defaults/src/entrypoints/auth/external/static.ts @@ -15,29 +15,18 @@ */ import { Config } from '@backstage/config'; -import { readAccessRestrictionsFromConfig } from './helpers'; -import { AccessRestriptionsMap, TokenHandler } from './types'; +import { createExternalTokenHandler } from './helpers'; const MIN_TOKEN_LENGTH = 8; -/** - * Handles `type: static` access. - * - * @internal - */ -export class StaticTokenHandler implements TokenHandler { - #entries = new Map< - string, - { - subject: string; - allAccessRestrictions?: AccessRestriptionsMap; - } - >(); - - add(config: Config) { - const token = config.getString('options.token'); - const subject = config.getString('options.subject'); - const allAccessRestrictions = readAccessRestrictionsFromConfig(config); +export const staticTokenHandler = createExternalTokenHandler<{ + token: string; + subject: string; +}>({ + type: 'static', + initialize(ctx: { options: Config }): { token: string; subject: string } { + const token = ctx.options.getString('token'); + const subject = ctx.options.getString('subject'); if (!token.match(/^\S+$/)) { throw new Error('Illegal token, must be a set of non-space characters'); @@ -47,16 +36,17 @@ export class StaticTokenHandler implements TokenHandler { ); } else if (!subject.match(/^\S+$/)) { throw new Error('Illegal subject, must be a set of non-space characters'); - } else if (this.#entries.has(token)) { - throw new Error( - 'Static externalAccess token was declared more than once', - ); } - this.#entries.set(token, { subject, allAccessRestrictions }); - } - - async verifyToken(token: string) { - return this.#entries.get(token); - } -} + return { token, subject }; + }, + async verifyToken( + token: string, + context: { token: string; subject: string }, + ) { + if (token === context.token) { + return { subject: context.subject }; + } + return undefined; + }, +}); diff --git a/packages/backend-defaults/src/entrypoints/auth/external/types.ts b/packages/backend-defaults/src/entrypoints/auth/external/types.ts index 6a1fd084ed..01d5a26149 100644 --- a/packages/backend-defaults/src/entrypoints/auth/external/types.ts +++ b/packages/backend-defaults/src/entrypoints/auth/external/types.ts @@ -15,20 +15,23 @@ */ import { BackstagePrincipalAccessRestrictions } from '@backstage/backend-plugin-api'; -import { Config } from '@backstage/config'; +import type { Config } from '@backstage/config'; -export type AccessRestriptionsMap = Map< +export type AccessRestrictionsMap = Map< string, // plugin ID BackstagePrincipalAccessRestrictions >; -export interface TokenHandler { - add(options: Config): void; - verifyToken(token: string): Promise< - | { - subject: string; - allAccessRestrictions?: AccessRestriptionsMap; - } - | undefined - >; +/** + * @public + * This interface is used to handle external tokens. + * It is used by the auth service to verify tokens and extract the subject. + */ +export interface ExternalTokenHandler { + type: string; + initialize(ctx: { options: Config }): TContext; + verifyToken( + token: string, + ctx: TContext, + ): Promise<{ subject: string } | undefined>; } diff --git a/packages/backend-defaults/src/entrypoints/auth/index.ts b/packages/backend-defaults/src/entrypoints/auth/index.ts index e48926f0ce..69d41b0310 100644 --- a/packages/backend-defaults/src/entrypoints/auth/index.ts +++ b/packages/backend-defaults/src/entrypoints/auth/index.ts @@ -19,4 +19,10 @@ export { pluginTokenHandlerDecoratorServiceRef, } from './authServiceFactory'; +export { createExternalTokenHandler } from './external/helpers'; + +export { externalTokenHandlersServiceRef } from './external/ExternalAuthTokenHandler'; + +export type { ExternalTokenHandler } from './external/types'; + export type { PluginTokenHandler } from './plugin/PluginTokenHandler'; diff --git a/packages/backend-defaults/src/entrypoints/auth/plugin/PluginTokenHandler.ts b/packages/backend-defaults/src/entrypoints/auth/plugin/PluginTokenHandler.ts index 9aec52bc08..2bd797c929 100644 --- a/packages/backend-defaults/src/entrypoints/auth/plugin/PluginTokenHandler.ts +++ b/packages/backend-defaults/src/entrypoints/auth/plugin/PluginTokenHandler.ts @@ -46,7 +46,7 @@ type Options = { /** * @public - * Issues and verifies {@link https://backstage.iceio/docs/auth/service-to-service-auth | service-to-service tokens}. + * Issues and verifies {@link https://backstage.io/docs/auth/service-to-service-auth | service-to-service tokens}. */ export interface PluginTokenHandler { verifyToken( @@ -77,14 +77,28 @@ export class DefaultPluginTokenHandler implements PluginTokenHandler { ); } + private readonly logger: LoggerService; + private readonly ownPluginId: string; + private readonly keySource: PluginKeySource; + private readonly algorithm: string; + private readonly keyDurationSeconds: number; + private readonly discovery: DiscoveryService; + private constructor( - private readonly logger: LoggerService, - private readonly ownPluginId: string, - private readonly keySource: PluginKeySource, - private readonly algorithm: string, - private readonly keyDurationSeconds: number, - private readonly discovery: DiscoveryService, - ) {} + logger: LoggerService, + ownPluginId: string, + keySource: PluginKeySource, + algorithm: string, + keyDurationSeconds: number, + discovery: DiscoveryService, + ) { + this.logger = logger; + this.ownPluginId = ownPluginId; + this.keySource = keySource; + this.algorithm = algorithm; + this.keyDurationSeconds = keyDurationSeconds; + this.discovery = discovery; + } async verifyToken( token: string, diff --git a/packages/backend-defaults/src/entrypoints/auth/plugin/keys/DatabaseKeyStore.ts b/packages/backend-defaults/src/entrypoints/auth/plugin/keys/DatabaseKeyStore.ts index 9f5c278a9c..c34da745d1 100644 --- a/packages/backend-defaults/src/entrypoints/auth/plugin/keys/DatabaseKeyStore.ts +++ b/packages/backend-defaults/src/entrypoints/auth/plugin/keys/DatabaseKeyStore.ts @@ -62,10 +62,13 @@ export class DatabaseKeyStore implements KeyStore { return new DatabaseKeyStore(client, logger); } - private constructor( - private readonly client: Knex, - private readonly logger: LoggerService, - ) {} + private readonly client: Knex; + private readonly logger: LoggerService; + + private constructor(client: Knex, logger: LoggerService) { + this.client = client; + this.logger = logger; + } async addKey(options: { id: string; diff --git a/packages/backend-defaults/src/entrypoints/auth/plugin/keys/DatabasePluginKeySource.ts b/packages/backend-defaults/src/entrypoints/auth/plugin/keys/DatabasePluginKeySource.ts index 37cb1aeb5c..e6a7bbcfc6 100644 --- a/packages/backend-defaults/src/entrypoints/auth/plugin/keys/DatabasePluginKeySource.ts +++ b/packages/backend-defaults/src/entrypoints/auth/plugin/keys/DatabasePluginKeySource.ts @@ -33,13 +33,22 @@ const KEY_EXPIRATION_MARGIN_FACTOR = 3; export class DatabasePluginKeySource implements PluginKeySource { private privateKeyPromise?: Promise; private keyExpiry?: Date; + private readonly keyStore: KeyStore; + private readonly logger: LoggerService; + private readonly keyDurationSeconds: number; + private readonly algorithm: string; constructor( - private readonly keyStore: KeyStore, - private readonly logger: LoggerService, - private readonly keyDurationSeconds: number, - private readonly algorithm: string, - ) {} + keyStore: KeyStore, + logger: LoggerService, + keyDurationSeconds: number, + algorithm: string, + ) { + this.keyStore = keyStore; + this.logger = logger; + this.keyDurationSeconds = keyDurationSeconds; + this.algorithm = algorithm; + } public static async create(options: { logger: LoggerService; diff --git a/packages/backend-defaults/src/entrypoints/auth/plugin/keys/StaticConfigPluginKeySource.ts b/packages/backend-defaults/src/entrypoints/auth/plugin/keys/StaticConfigPluginKeySource.ts index eb863a807a..504f608d8d 100644 --- a/packages/backend-defaults/src/entrypoints/auth/plugin/keys/StaticConfigPluginKeySource.ts +++ b/packages/backend-defaults/src/entrypoints/auth/plugin/keys/StaticConfigPluginKeySource.ts @@ -67,10 +67,13 @@ const SECONDS_IN_MS = 1000; * private and public key paths in the `create` method. */ export class StaticConfigPluginKeySource implements PluginKeySource { - private constructor( - private readonly keyPairs: KeyPair[], - private readonly keyDurationSeconds: number, - ) {} + private readonly keyPairs: KeyPair[]; + private readonly keyDurationSeconds: number; + + private constructor(keyPairs: KeyPair[], keyDurationSeconds: number) { + this.keyPairs = keyPairs; + this.keyDurationSeconds = keyDurationSeconds; + } public static async create(options: { sourceConfig: Config; diff --git a/packages/backend-defaults/src/entrypoints/auth/user/UserTokenHandler.ts b/packages/backend-defaults/src/entrypoints/auth/user/UserTokenHandler.ts index d205fa6b82..a1b8654527 100644 --- a/packages/backend-defaults/src/entrypoints/auth/user/UserTokenHandler.ts +++ b/packages/backend-defaults/src/entrypoints/auth/user/UserTokenHandler.ts @@ -44,10 +44,13 @@ export class UserTokenHandler { return new UserTokenHandler(jwksClient, options.logger); } - constructor( - private readonly jwksClient: JwksClient, - private readonly logger: LoggerService, - ) {} + private readonly jwksClient: JwksClient; + private readonly logger: LoggerService; + + constructor(jwksClient: JwksClient, logger: LoggerService) { + this.jwksClient = jwksClient; + this.logger = logger; + } async verifyToken(token: string) { const verifyOpts = this.#getTokenVerificationOptions(token); diff --git a/packages/backend-defaults/src/entrypoints/database/DatabaseManager.ts b/packages/backend-defaults/src/entrypoints/database/DatabaseManager.ts index 5ca70cab86..96313e50af 100644 --- a/packages/backend-defaults/src/entrypoints/database/DatabaseManager.ts +++ b/packages/backend-defaults/src/entrypoints/database/DatabaseManager.ts @@ -52,16 +52,24 @@ export type DatabaseManagerOptions = { * Testable implementation class for {@link DatabaseManager} below. */ export class DatabaseManagerImpl { + private readonly config: Config; + private readonly connectors: Record; + private readonly options?: DatabaseManagerOptions; + private readonly databaseCache: Map>; + private readonly keepaliveIntervals: Map; + constructor( - private readonly config: Config, - private readonly connectors: Record, - private readonly options?: DatabaseManagerOptions, - private readonly databaseCache: Map> = new Map(), - private readonly keepaliveIntervals: Map< - string, - NodeJS.Timeout - > = new Map(), + config: Config, + connectors: Record, + options?: DatabaseManagerOptions, + databaseCache: Map> = new Map(), + keepaliveIntervals: Map = new Map(), ) { + this.config = config; + this.connectors = connectors; + this.options = options; + this.databaseCache = databaseCache; + this.keepaliveIntervals = keepaliveIntervals; // If a rootLifecycle service was provided, register a shutdown hook to // clean up any database connections. if (options?.rootLifecycle !== undefined) { @@ -262,7 +270,11 @@ export class DatabaseManager { ); } - private constructor(private readonly impl: DatabaseManagerImpl) {} + private readonly impl: DatabaseManagerImpl; + + private constructor(impl: DatabaseManagerImpl) { + this.impl = impl; + } /** * Generates a DatabaseService for consumption by plugins. diff --git a/packages/backend-defaults/src/entrypoints/database/connectors/mysql.ts b/packages/backend-defaults/src/entrypoints/database/connectors/mysql.ts index d5bd88deef..e44feed361 100644 --- a/packages/backend-defaults/src/entrypoints/database/connectors/mysql.ts +++ b/packages/backend-defaults/src/entrypoints/database/connectors/mysql.ts @@ -243,10 +243,13 @@ function normalizeConnection( } export class MysqlConnector implements Connector { - constructor( - private readonly config: Config, - private readonly prefix: string, - ) {} + private readonly config: Config; + private readonly prefix: string; + + constructor(config: Config, prefix: string) { + this.config = config; + this.prefix = prefix; + } async getClient( pluginId: string, diff --git a/packages/backend-defaults/src/entrypoints/database/connectors/postgres.ts b/packages/backend-defaults/src/entrypoints/database/connectors/postgres.ts index 4adf77a2f6..4a6893e9c1 100644 --- a/packages/backend-defaults/src/entrypoints/database/connectors/postgres.ts +++ b/packages/backend-defaults/src/entrypoints/database/connectors/postgres.ts @@ -294,10 +294,13 @@ function normalizeConnection( } export class PgConnector implements Connector { - constructor( - private readonly config: Config, - private readonly prefix: string, - ) {} + private readonly config: Config; + private readonly prefix: string; + + constructor(config: Config, prefix: string) { + this.config = config; + this.prefix = prefix; + } async getClient( pluginId: string, diff --git a/packages/backend-defaults/src/entrypoints/database/connectors/sqlite3.ts b/packages/backend-defaults/src/entrypoints/database/connectors/sqlite3.ts index 67dd9d975f..081169c700 100644 --- a/packages/backend-defaults/src/entrypoints/database/connectors/sqlite3.ts +++ b/packages/backend-defaults/src/entrypoints/database/connectors/sqlite3.ts @@ -174,7 +174,11 @@ function normalizeConnection( } export class Sqlite3Connector implements Connector { - constructor(private readonly config: Config) {} + private readonly config: Config; + + constructor(config: Config) { + this.config = config; + } async getClient( pluginId: string, diff --git a/packages/backend-defaults/src/entrypoints/lifecycle/lifecycleServiceFactory.ts b/packages/backend-defaults/src/entrypoints/lifecycle/lifecycleServiceFactory.ts index 0ba812f5b4..92c4f599a4 100644 --- a/packages/backend-defaults/src/entrypoints/lifecycle/lifecycleServiceFactory.ts +++ b/packages/backend-defaults/src/entrypoints/lifecycle/lifecycleServiceFactory.ts @@ -28,10 +28,13 @@ import { /** @internal */ export class BackendPluginLifecycleImpl implements LifecycleService { - constructor( - private readonly logger: LoggerService, - private readonly pluginMetadata: PluginMetadataService, - ) {} + private readonly logger: LoggerService; + private readonly pluginMetadata: PluginMetadataService; + + constructor(logger: LoggerService, pluginMetadata: PluginMetadataService) { + this.logger = logger; + this.pluginMetadata = pluginMetadata; + } #hasStarted = false; #hasShutdown = false; diff --git a/packages/backend-defaults/src/entrypoints/rootHealth/rootHealthServiceFactory.ts b/packages/backend-defaults/src/entrypoints/rootHealth/rootHealthServiceFactory.ts index b201079b4f..e7255610bc 100644 --- a/packages/backend-defaults/src/entrypoints/rootHealth/rootHealthServiceFactory.ts +++ b/packages/backend-defaults/src/entrypoints/rootHealth/rootHealthServiceFactory.ts @@ -24,8 +24,10 @@ import { /** @internal */ export class DefaultRootHealthService implements RootHealthService { #state: 'init' | 'up' | 'down' = 'init'; + readonly options: { lifecycle: RootLifecycleService }; - constructor(readonly options: { lifecycle: RootLifecycleService }) { + constructor(options: { lifecycle: RootLifecycleService }) { + this.options = options; options.lifecycle.addStartupHook(() => { this.#state = 'up'; }); diff --git a/packages/backend-defaults/src/entrypoints/rootLifecycle/rootLifecycleServiceFactory.ts b/packages/backend-defaults/src/entrypoints/rootLifecycle/rootLifecycleServiceFactory.ts index abba4beece..a77a2e1308 100644 --- a/packages/backend-defaults/src/entrypoints/rootLifecycle/rootLifecycleServiceFactory.ts +++ b/packages/backend-defaults/src/entrypoints/rootLifecycle/rootLifecycleServiceFactory.ts @@ -27,7 +27,11 @@ import { /** @internal */ export class BackendLifecycleImpl implements RootLifecycleService { - constructor(private readonly logger: LoggerService) {} + private readonly logger: LoggerService; + + constructor(logger: LoggerService) { + this.logger = logger; + } #hasStarted = false; #startupTasks: Array<{ diff --git a/packages/backend-defaults/src/entrypoints/scheduler/lib/LocalTaskWorker.ts b/packages/backend-defaults/src/entrypoints/scheduler/lib/LocalTaskWorker.ts index a063f4145e..efecb1c0ed 100644 --- a/packages/backend-defaults/src/entrypoints/scheduler/lib/LocalTaskWorker.ts +++ b/packages/backend-defaults/src/entrypoints/scheduler/lib/LocalTaskWorker.ts @@ -36,15 +36,23 @@ export class LocalTaskWorker { status: 'idle', }; + private readonly taskId: string; + private readonly fn: SchedulerServiceTaskFunction; + private readonly logger: LoggerService; + constructor( - private readonly taskId: string, - private readonly fn: SchedulerServiceTaskFunction, - private readonly logger: LoggerService, - ) {} + taskId: string, + fn: SchedulerServiceTaskFunction, + logger: LoggerService, + ) { + this.taskId = taskId; + this.fn = fn; + this.logger = logger; + } start(settings: TaskSettingsV2, options: { signal: AbortSignal }) { this.logger.info( - `Task worker starting: ${this.taskId}, ${JSON.stringify(settings)}`, + `Registered scheduled task: ${this.taskId}, ${JSON.stringify(settings)}`, ); (async () => { diff --git a/packages/backend-defaults/src/entrypoints/scheduler/lib/PluginTaskSchedulerImpl.test.ts b/packages/backend-defaults/src/entrypoints/scheduler/lib/PluginTaskSchedulerImpl.test.ts index c0339a7a2d..5872ae793e 100644 --- a/packages/backend-defaults/src/entrypoints/scheduler/lib/PluginTaskSchedulerImpl.test.ts +++ b/packages/backend-defaults/src/entrypoints/scheduler/lib/PluginTaskSchedulerImpl.test.ts @@ -33,7 +33,7 @@ jest.setTimeout(60_000); describe('PluginTaskManagerImpl', () => { const addShutdownHook = jest.fn(); const databases = TestDatabases.create({ - ids: ['POSTGRES_17', 'POSTGRES_13', 'SQLITE_3'], + ids: ['POSTGRES_18', 'POSTGRES_14', 'SQLITE_3'], }); beforeAll(async () => { diff --git a/packages/backend-defaults/src/entrypoints/scheduler/lib/PluginTaskSchedulerImpl.ts b/packages/backend-defaults/src/entrypoints/scheduler/lib/PluginTaskSchedulerImpl.ts index b7a946ab80..90f5d08e4a 100644 --- a/packages/backend-defaults/src/entrypoints/scheduler/lib/PluginTaskSchedulerImpl.ts +++ b/packages/backend-defaults/src/entrypoints/scheduler/lib/PluginTaskSchedulerImpl.ts @@ -50,12 +50,19 @@ export class PluginTaskSchedulerImpl implements SchedulerService { private readonly lastStarted: Gauge; private readonly lastCompleted: Gauge; + private readonly pluginId: string; + private readonly databaseFactory: () => Promise; + private readonly logger: LoggerService; + constructor( - private readonly pluginId: string, - private readonly databaseFactory: () => Promise, - private readonly logger: LoggerService, + pluginId: string, + databaseFactory: () => Promise, + logger: LoggerService, rootLifecycle: RootLifecycleService, ) { + this.pluginId = pluginId; + this.databaseFactory = databaseFactory; + this.logger = logger; const meter = metrics.getMeter('default'); this.counter = meter.createCounter('backend_tasks.task.runs.count', { description: 'Total number of times a task has been run', diff --git a/packages/backend-defaults/src/entrypoints/scheduler/lib/PluginTaskSchedulerJanitor.test.ts b/packages/backend-defaults/src/entrypoints/scheduler/lib/PluginTaskSchedulerJanitor.test.ts index bfba35ac30..8c2b54f69f 100644 --- a/packages/backend-defaults/src/entrypoints/scheduler/lib/PluginTaskSchedulerJanitor.test.ts +++ b/packages/backend-defaults/src/entrypoints/scheduler/lib/PluginTaskSchedulerJanitor.test.ts @@ -39,8 +39,8 @@ describe('PluginTaskSchedulerJanitor', () => { const databases = TestDatabases.create({ ids: [ /* 'MYSQL_8' not supported yet */ - 'POSTGRES_17', - 'POSTGRES_13', + 'POSTGRES_18', + 'POSTGRES_14', 'SQLITE_3', 'MYSQL_8', ], diff --git a/packages/backend-defaults/src/entrypoints/scheduler/lib/TaskWorker.ts b/packages/backend-defaults/src/entrypoints/scheduler/lib/TaskWorker.ts index 32e7a5ab4e..eb040ef415 100644 --- a/packages/backend-defaults/src/entrypoints/scheduler/lib/TaskWorker.ts +++ b/packages/backend-defaults/src/entrypoints/scheduler/lib/TaskWorker.ts @@ -46,14 +46,25 @@ export class TaskWorker { #workerState: TaskApiTasksResponse['workerState'] = { status: 'idle', }; + private readonly taskId: string; + private readonly fn: SchedulerServiceTaskFunction; + private readonly knex: Knex; + private readonly logger: LoggerService; + private readonly workCheckFrequency: Duration; constructor( - private readonly taskId: string, - private readonly fn: SchedulerServiceTaskFunction, - private readonly knex: Knex, - private readonly logger: LoggerService, - private readonly workCheckFrequency: Duration = DEFAULT_WORK_CHECK_FREQUENCY, - ) {} + taskId: string, + fn: SchedulerServiceTaskFunction, + knex: Knex, + logger: LoggerService, + workCheckFrequency: Duration = DEFAULT_WORK_CHECK_FREQUENCY, + ) { + this.taskId = taskId; + this.fn = fn; + this.knex = knex; + this.logger = logger; + this.workCheckFrequency = workCheckFrequency; + } async start(settings: TaskSettingsV2, options: { signal: AbortSignal }) { try { @@ -63,7 +74,7 @@ export class TaskWorker { } this.logger.info( - `Task worker starting: ${this.taskId}, ${JSON.stringify(settings)}`, + `Registered scheduled task: ${this.taskId}, ${JSON.stringify(settings)}`, ); let workCheckFrequency = this.workCheckFrequency; diff --git a/packages/backend-defaults/src/entrypoints/urlReader/lib/AwsCodeCommitUrlReader.ts b/packages/backend-defaults/src/entrypoints/urlReader/lib/AwsCodeCommitUrlReader.ts index 7cffe6a28b..06af036891 100644 --- a/packages/backend-defaults/src/entrypoints/urlReader/lib/AwsCodeCommitUrlReader.ts +++ b/packages/backend-defaults/src/entrypoints/urlReader/lib/AwsCodeCommitUrlReader.ts @@ -142,13 +142,23 @@ export class AwsCodeCommitUrlReader implements UrlReaderService { }); }; + private readonly credsManager: AwsCredentialsManager; + private readonly integration: AwsCodeCommitIntegration; + private readonly deps: { + treeResponseFactory: ReadTreeResponseFactory; + }; + constructor( - private readonly credsManager: AwsCredentialsManager, - private readonly integration: AwsCodeCommitIntegration, - private readonly deps: { + credsManager: AwsCredentialsManager, + integration: AwsCodeCommitIntegration, + deps: { treeResponseFactory: ReadTreeResponseFactory; }, - ) {} + ) { + this.credsManager = credsManager; + this.integration = integration; + this.deps = deps; + } /** * If accessKeyId and secretAccessKey are missing, the standard credentials provider chain will be used: diff --git a/packages/backend-defaults/src/entrypoints/urlReader/lib/AwsS3UrlReader.ts b/packages/backend-defaults/src/entrypoints/urlReader/lib/AwsS3UrlReader.ts index 5060f6c632..87563a86ba 100644 --- a/packages/backend-defaults/src/entrypoints/urlReader/lib/AwsS3UrlReader.ts +++ b/packages/backend-defaults/src/entrypoints/urlReader/lib/AwsS3UrlReader.ts @@ -151,13 +151,23 @@ export class AwsS3UrlReader implements UrlReaderService { }); }; + private readonly credsManager: AwsCredentialsManager; + private readonly integration: AwsS3Integration; + private readonly deps: { + treeResponseFactory: ReadTreeResponseFactory; + }; + constructor( - private readonly credsManager: AwsCredentialsManager, - private readonly integration: AwsS3Integration, - private readonly deps: { + credsManager: AwsCredentialsManager, + integration: AwsS3Integration, + deps: { treeResponseFactory: ReadTreeResponseFactory; }, - ) {} + ) { + this.credsManager = credsManager; + this.integration = integration; + this.deps = deps; + } /** * If accessKeyId and secretAccessKey are missing, the standard credentials provider chain will be used: diff --git a/packages/backend-defaults/src/entrypoints/urlReader/lib/AzureBlobStorageUrlReader.ts b/packages/backend-defaults/src/entrypoints/urlReader/lib/AzureBlobStorageUrlReader.ts index 1953beabe9..ca5f49405f 100644 --- a/packages/backend-defaults/src/entrypoints/urlReader/lib/AzureBlobStorageUrlReader.ts +++ b/packages/backend-defaults/src/entrypoints/urlReader/lib/AzureBlobStorageUrlReader.ts @@ -91,13 +91,23 @@ export class AzureBlobStorageUrlReader implements UrlReaderService { // private readonly blobServiceClient: BlobServiceClient; + private readonly credsManager: AzureCredentialsManager; + private readonly integration: AzureBlobStorageIntergation; + private readonly deps: { + treeResponseFactory: ReadTreeResponseFactory; + }; + constructor( - private readonly credsManager: AzureCredentialsManager, - private readonly integration: AzureBlobStorageIntergation, - private readonly deps: { + credsManager: AzureCredentialsManager, + integration: AzureBlobStorageIntergation, + deps: { treeResponseFactory: ReadTreeResponseFactory; }, - ) {} + ) { + this.credsManager = credsManager; + this.integration = integration; + this.deps = deps; + } private async createContainerClient( containerName: string, diff --git a/packages/backend-defaults/src/entrypoints/urlReader/lib/AzureUrlReader.test.ts b/packages/backend-defaults/src/entrypoints/urlReader/lib/AzureUrlReader.test.ts index 12a7ae8093..ee0eac6b4e 100644 --- a/packages/backend-defaults/src/entrypoints/urlReader/lib/AzureUrlReader.test.ts +++ b/packages/backend-defaults/src/entrypoints/urlReader/lib/AzureUrlReader.test.ts @@ -214,7 +214,7 @@ describe('AzureUrlReader', () => { res( ctx.status(200), ctx.set('Content-Type', 'application/zip'), - ctx.body(repoBuffer), + ctx.body(new Uint8Array(repoBuffer)), ), ), rest.get( @@ -324,7 +324,7 @@ describe('AzureUrlReader', () => { res( ctx.status(200), ctx.set('Content-Type', 'application/zip'), - ctx.body(repoBuffer), + ctx.body(new Uint8Array(repoBuffer)), ), ), rest.get( diff --git a/packages/backend-defaults/src/entrypoints/urlReader/lib/AzureUrlReader.ts b/packages/backend-defaults/src/entrypoints/urlReader/lib/AzureUrlReader.ts index 465ec2326c..66fd1cc95a 100644 --- a/packages/backend-defaults/src/entrypoints/urlReader/lib/AzureUrlReader.ts +++ b/packages/backend-defaults/src/entrypoints/urlReader/lib/AzureUrlReader.ts @@ -62,13 +62,22 @@ export class AzureUrlReader implements UrlReaderService { }); }; + private readonly integration: AzureIntegration; + private readonly deps: { + treeResponseFactory: ReadTreeResponseFactory; + credentialsProvider: AzureDevOpsCredentialsProvider; + }; + constructor( - private readonly integration: AzureIntegration, - private readonly deps: { + integration: AzureIntegration, + deps: { treeResponseFactory: ReadTreeResponseFactory; credentialsProvider: AzureDevOpsCredentialsProvider; }, - ) {} + ) { + this.integration = integration; + this.deps = deps; + } async read(url: string): Promise { const response = await this.readUrl(url); diff --git a/packages/backend-defaults/src/entrypoints/urlReader/lib/BitbucketCloudUrlReader.test.ts b/packages/backend-defaults/src/entrypoints/urlReader/lib/BitbucketCloudUrlReader.test.ts index d2d0d28d76..89ff98ba27 100644 --- a/packages/backend-defaults/src/entrypoints/urlReader/lib/BitbucketCloudUrlReader.test.ts +++ b/packages/backend-defaults/src/entrypoints/urlReader/lib/BitbucketCloudUrlReader.test.ts @@ -271,7 +271,7 @@ describe('BitbucketCloudUrlReader', () => { 'content-disposition', 'attachment; filename=backstage-mock-12ab34cd56ef.tar.gz', ), - ctx.body(repoBuffer), + ctx.body(new Uint8Array(repoBuffer)), ), ), rest.get( @@ -399,7 +399,7 @@ describe('BitbucketCloudUrlReader', () => { 'content-disposition', 'attachment; filename=backstage-mock-12ab34cd56ef.tar.gz', ), - ctx.body(repoBuffer), + ctx.body(new Uint8Array(repoBuffer)), ), ), rest.get( diff --git a/packages/backend-defaults/src/entrypoints/urlReader/lib/BitbucketCloudUrlReader.ts b/packages/backend-defaults/src/entrypoints/urlReader/lib/BitbucketCloudUrlReader.ts index c968420a5e..3a228f1e9c 100644 --- a/packages/backend-defaults/src/entrypoints/urlReader/lib/BitbucketCloudUrlReader.ts +++ b/packages/backend-defaults/src/entrypoints/urlReader/lib/BitbucketCloudUrlReader.ts @@ -59,10 +59,15 @@ export class BitbucketCloudUrlReader implements UrlReaderService { }); }; + private readonly integration: BitbucketCloudIntegration; + private readonly deps: { treeResponseFactory: ReadTreeResponseFactory }; + constructor( - private readonly integration: BitbucketCloudIntegration, - private readonly deps: { treeResponseFactory: ReadTreeResponseFactory }, + integration: BitbucketCloudIntegration, + deps: { treeResponseFactory: ReadTreeResponseFactory }, ) { + this.integration = integration; + this.deps = deps; const { host, username, appPassword } = integration.config; if (username && !appPassword) { diff --git a/packages/backend-defaults/src/entrypoints/urlReader/lib/BitbucketServerUrlReader.test.ts b/packages/backend-defaults/src/entrypoints/urlReader/lib/BitbucketServerUrlReader.test.ts index 5e82246298..1ab6342225 100644 --- a/packages/backend-defaults/src/entrypoints/urlReader/lib/BitbucketServerUrlReader.test.ts +++ b/packages/backend-defaults/src/entrypoints/urlReader/lib/BitbucketServerUrlReader.test.ts @@ -71,7 +71,7 @@ describe('BitbucketServerUrlReader', () => { 'content-disposition', 'attachment; filename=backstage-mock.tgz', ), - ctx.body(repoBuffer), + ctx.body(new Uint8Array(repoBuffer)), ), ), rest.get( @@ -157,7 +157,7 @@ describe('BitbucketServerUrlReader', () => { 'content-disposition', 'attachment; filename=backstage-mock.tgz', ), - ctx.body(repoBuffer), + ctx.body(new Uint8Array(repoBuffer)), ), ), rest.get( @@ -216,7 +216,7 @@ describe('BitbucketServerUrlReader', () => { 'content-disposition', 'attachment; filename=backstage-mock.tgz', ), - ctx.body(repoBuffer), + ctx.body(new Uint8Array(repoBuffer)), ), ), rest.get( diff --git a/packages/backend-defaults/src/entrypoints/urlReader/lib/BitbucketServerUrlReader.ts b/packages/backend-defaults/src/entrypoints/urlReader/lib/BitbucketServerUrlReader.ts index 3482bdf4bd..aba28b4fd7 100644 --- a/packages/backend-defaults/src/entrypoints/urlReader/lib/BitbucketServerUrlReader.ts +++ b/packages/backend-defaults/src/entrypoints/urlReader/lib/BitbucketServerUrlReader.ts @@ -58,10 +58,16 @@ export class BitbucketServerUrlReader implements UrlReaderService { }); }; + private readonly integration: BitbucketServerIntegration; + private readonly deps: { treeResponseFactory: ReadTreeResponseFactory }; + constructor( - private readonly integration: BitbucketServerIntegration, - private readonly deps: { treeResponseFactory: ReadTreeResponseFactory }, - ) {} + integration: BitbucketServerIntegration, + deps: { treeResponseFactory: ReadTreeResponseFactory }, + ) { + this.integration = integration; + this.deps = deps; + } async read(url: string): Promise { const response = await this.readUrl(url); diff --git a/packages/backend-defaults/src/entrypoints/urlReader/lib/BitbucketUrlReader.test.ts b/packages/backend-defaults/src/entrypoints/urlReader/lib/BitbucketUrlReader.test.ts index 29217ea1f5..bd1a2d5912 100644 --- a/packages/backend-defaults/src/entrypoints/urlReader/lib/BitbucketUrlReader.test.ts +++ b/packages/backend-defaults/src/entrypoints/urlReader/lib/BitbucketUrlReader.test.ts @@ -130,6 +130,27 @@ describe('BitbucketUrlReader', () => { expect(buffer.toString()).toBe('foo'); }); + it('should be able to readUrl using provided token', async () => { + worker.use( + rest.get( + 'https://api.bitbucket.org/2.0/repositories/backstage-verification/test-template/src/master/template.yaml', + (req, res, ctx) => { + expect(req.headers.get('authorization')).toBe( + 'Bearer manual-token', + ); + return res(ctx.status(200), ctx.body('foo')); + }, + ), + ); + + const result = await bitbucketProcessor.readUrl( + 'https://bitbucket.org/backstage-verification/test-template/src/master/template.yaml', + { token: 'manual-token' }, + ); + const buffer = await result.buffer(); + expect(buffer.toString()).toBe('foo'); + }); + it('should be able to readUrl via stream without ETag', async () => { worker.use( rest.get( @@ -324,7 +345,7 @@ describe('BitbucketUrlReader', () => { 'content-disposition', 'attachment; filename=backstage-mock-12ab34cd56ef.tar.gz', ), - ctx.body(repoBuffer), + ctx.body(new Uint8Array(repoBuffer)), ), ), rest.get( @@ -347,7 +368,7 @@ describe('BitbucketUrlReader', () => { 'content-disposition', 'attachment; filename=backstage-mock.tgz', ), - ctx.body(privateBitbucketRepoBuffer), + ctx.body(new Uint8Array(privateBitbucketRepoBuffer)), ), ), rest.get( @@ -491,7 +512,7 @@ describe('BitbucketUrlReader', () => { 'content-disposition', 'attachment; filename=backstage-mock-12ab34cd56ef.tar.gz', ), - ctx.body(repoBuffer), + ctx.body(new Uint8Array(repoBuffer)), ), ), rest.get( @@ -562,7 +583,7 @@ describe('BitbucketUrlReader', () => { 'content-disposition', 'attachment; filename=backstage-mock.tgz', ), - ctx.body(privateBitbucketRepoBuffer), + ctx.body(new Uint8Array(privateBitbucketRepoBuffer)), ), ), rest.get( diff --git a/packages/backend-defaults/src/entrypoints/urlReader/lib/BitbucketUrlReader.ts b/packages/backend-defaults/src/entrypoints/urlReader/lib/BitbucketUrlReader.ts index 4c6de31aa4..8c8843d7e9 100644 --- a/packages/backend-defaults/src/entrypoints/urlReader/lib/BitbucketUrlReader.ts +++ b/packages/backend-defaults/src/entrypoints/urlReader/lib/BitbucketUrlReader.ts @@ -69,11 +69,16 @@ export class BitbucketUrlReader implements UrlReaderService { }); }; + private readonly integration: BitbucketIntegration; + private readonly deps: { treeResponseFactory: ReadTreeResponseFactory }; + constructor( - private readonly integration: BitbucketIntegration, + integration: BitbucketIntegration, logger: LoggerService, - private readonly deps: { treeResponseFactory: ReadTreeResponseFactory }, + deps: { treeResponseFactory: ReadTreeResponseFactory }, ) { + this.integration = integration; + this.deps = deps; const { host, token, username, appPassword } = integration.config; const replacement = host === 'bitbucket.org' ? 'bitbucketCloud' : 'bitbucketServer'; @@ -93,13 +98,27 @@ export class BitbucketUrlReader implements UrlReaderService { return response.buffer(); } + private getCredentials = async (options?: { + token?: string; + }): Promise<{ headers: Record }> => { + if (options?.token) { + return { + headers: { + Authorization: `Bearer ${options.token}`, + }, + }; + } + + return await getBitbucketRequestOptions(this.integration.config); + }; + async readUrl( url: string, options?: UrlReaderServiceReadUrlOptions, ): Promise { const { etag, lastModifiedAfter, signal } = options ?? {}; const bitbucketUrl = getBitbucketFileFetchUrl(url, this.integration.config); - const requestOptions = getBitbucketRequestOptions(this.integration.config); + const requestOptions = await this.getCredentials(options); let response: Response; try { diff --git a/packages/backend-defaults/src/entrypoints/urlReader/lib/GerritUrlReader.test.ts b/packages/backend-defaults/src/entrypoints/urlReader/lib/GerritUrlReader.test.ts index 246c07631b..69c834201c 100644 --- a/packages/backend-defaults/src/entrypoints/urlReader/lib/GerritUrlReader.test.ts +++ b/packages/backend-defaults/src/entrypoints/urlReader/lib/GerritUrlReader.test.ts @@ -291,7 +291,7 @@ describe.skip('GerritUrlReader', () => { 'content-disposition', 'attachment; filename=web-refs/heads/master.tar.gz', ), - ctx.body(repoArchiveBuffer), + ctx.body(new Uint8Array(repoArchiveBuffer)), ), ), rest.get( @@ -304,7 +304,7 @@ describe.skip('GerritUrlReader', () => { 'content-disposition', 'attachment; filename=web-refs/heads/master-docs.tar.gz', ), - ctx.body(repoArchiveDocsBuffer), + ctx.body(new Uint8Array(repoArchiveDocsBuffer)), ), ), rest.get( @@ -317,7 +317,7 @@ describe.skip('GerritUrlReader', () => { 'content-disposition', 'attachment; filename=web-refs/heads/master.tar.gz', ), - ctx.body(repoArchiveBuffer), + ctx.body(new Uint8Array(repoArchiveBuffer)), ), ), ); @@ -330,7 +330,10 @@ describe.skip('GerritUrlReader', () => { it('reads the wanted files correctly using gitiles.', async () => { worker.use( rest.get(branchAPIUrl, (_, res, ctx) => { - return res(ctx.status(200), ctx.body(branchAPIresponse)); + return res( + ctx.status(200), + ctx.body(new Uint8Array(branchAPIresponse)), + ); }), ); @@ -353,7 +356,10 @@ describe.skip('GerritUrlReader', () => { it('throws NotModifiedError for matching etags.', async () => { worker.use( rest.get(branchAPIUrl, (_, res, ctx) => { - return res(ctx.status(200), ctx.body(branchAPIresponse)); + return res( + ctx.status(200), + ctx.body(new Uint8Array(branchAPIresponse)), + ); }), ); @@ -389,7 +395,10 @@ describe.skip('GerritUrlReader', () => { it('should returns wanted files with a subpath using gitiles', async () => { worker.use( rest.get(branchAPIUrl, (_, res, ctx) => { - return res(ctx.status(200), ctx.body(branchAPIresponse)); + return res( + ctx.status(200), + ctx.body(new Uint8Array(branchAPIresponse)), + ); }), ); @@ -462,7 +471,7 @@ describe.skip('GerritUrlReader', () => { ctx.status(200), ctx.set('Content-Type', 'application/json'), ctx.set('content-disposition', 'attachment'), - ctx.body(treeRecursiveResponse), + ctx.body(new Uint8Array(treeRecursiveResponse)), ); } @@ -522,7 +531,10 @@ describe.skip('GerritUrlReader', () => { it('reads the wanted files correctly using gitiles.', async () => { worker.use( rest.get(branchAPIUrl, (_, res, ctx) => { - return res(ctx.status(200), ctx.body(branchAPIresponse)); + return res( + ctx.status(200), + ctx.body(new Uint8Array(branchAPIresponse)), + ); }), ); @@ -549,7 +561,10 @@ describe.skip('GerritUrlReader', () => { it('throws NotModifiedError for matching etags.', async () => { worker.use( rest.get(branchAPIUrl, (_, res, ctx) => { - return res(ctx.status(200), ctx.body(branchAPIresponse)); + return res( + ctx.status(200), + ctx.body(new Uint8Array(branchAPIresponse)), + ); }), ); diff --git a/packages/backend-defaults/src/entrypoints/urlReader/lib/GerritUrlReader.ts b/packages/backend-defaults/src/entrypoints/urlReader/lib/GerritUrlReader.ts index bec062ee8b..9facb613d4 100644 --- a/packages/backend-defaults/src/entrypoints/urlReader/lib/GerritUrlReader.ts +++ b/packages/backend-defaults/src/entrypoints/urlReader/lib/GerritUrlReader.ts @@ -84,10 +84,16 @@ export class GerritUrlReader implements UrlReaderService { }); }; + private readonly integration: GerritIntegration; + private readonly deps: { treeResponseFactory: ReadTreeResponseFactory }; + constructor( - private readonly integration: GerritIntegration, - private readonly deps: { treeResponseFactory: ReadTreeResponseFactory }, - ) {} + integration: GerritIntegration, + deps: { treeResponseFactory: ReadTreeResponseFactory }, + ) { + this.integration = integration; + this.deps = deps; + } async read(url: string): Promise { const response = await this.readUrl(url); diff --git a/packages/backend-defaults/src/entrypoints/urlReader/lib/GiteaUrlReader.test.ts b/packages/backend-defaults/src/entrypoints/urlReader/lib/GiteaUrlReader.test.ts index 0adbc3381f..9242cd2f9b 100644 --- a/packages/backend-defaults/src/entrypoints/urlReader/lib/GiteaUrlReader.test.ts +++ b/packages/backend-defaults/src/entrypoints/urlReader/lib/GiteaUrlReader.test.ts @@ -279,7 +279,7 @@ describe('GiteaUrlReader', () => { 'content-disposition', 'attachment; filename=backstage-mock.tar.gz', ), - ctx.body(repoBuffer), + ctx.body(new Uint8Array(repoBuffer)), ); }, ), diff --git a/packages/backend-defaults/src/entrypoints/urlReader/lib/GiteaUrlReader.ts b/packages/backend-defaults/src/entrypoints/urlReader/lib/GiteaUrlReader.ts index bce033c2ae..b1b4b79c34 100644 --- a/packages/backend-defaults/src/entrypoints/urlReader/lib/GiteaUrlReader.ts +++ b/packages/backend-defaults/src/entrypoints/urlReader/lib/GiteaUrlReader.ts @@ -62,12 +62,20 @@ export class GiteaUrlReader implements UrlReaderService { }); }; + private readonly integration: GiteaIntegration; + private readonly deps: { + treeResponseFactory: ReadTreeResponseFactory; + }; + constructor( - private readonly integration: GiteaIntegration, - private readonly deps: { + integration: GiteaIntegration, + deps: { treeResponseFactory: ReadTreeResponseFactory; }, - ) {} + ) { + this.integration = integration; + this.deps = deps; + } async read(url: string): Promise { const response = await this.readUrl(url); diff --git a/packages/backend-defaults/src/entrypoints/urlReader/lib/GithubUrlReader.test.ts b/packages/backend-defaults/src/entrypoints/urlReader/lib/GithubUrlReader.test.ts index 861b3ad7cb..fe832e124f 100644 --- a/packages/backend-defaults/src/entrypoints/urlReader/lib/GithubUrlReader.test.ts +++ b/packages/backend-defaults/src/entrypoints/urlReader/lib/GithubUrlReader.test.ts @@ -373,7 +373,7 @@ describe('GithubUrlReader', () => { 'content-disposition', 'attachment; filename=backstage-mock-etag123.tar.gz', ), - ctx.body(repoBuffer), + ctx.body(new Uint8Array(repoBuffer)), ), ), rest.get( @@ -390,7 +390,7 @@ describe('GithubUrlReader', () => { 'content-disposition', 'attachment; filename=backstage-mock-etag123.tar.gz', ), - ctx.body(repoBuffer), + ctx.body(new Uint8Array(repoBuffer)), ), ), rest.get( @@ -480,7 +480,7 @@ describe('GithubUrlReader', () => { 'content-disposition', 'attachment; filename=backstage-mock-etag123.tar.gz', ), - ctx.body(repoBuffer), + ctx.body(new Uint8Array(repoBuffer)), ); }, ), @@ -531,7 +531,7 @@ describe('GithubUrlReader', () => { 'content-disposition', 'attachment; filename=backstage-mock-etag123.tar.gz', ), - ctx.body(repoBuffer), + ctx.body(new Uint8Array(repoBuffer)), ); }, ), @@ -720,7 +720,7 @@ describe('GithubUrlReader', () => { 'content-disposition', 'attachment; filename=backstage-mock-etag123.tar.gz', ), - ctx.body(repoBuffer), + ctx.body(new Uint8Array(repoBuffer)), ), ), rest.get( @@ -733,7 +733,7 @@ describe('GithubUrlReader', () => { 'content-disposition', 'attachment; filename=backstage-mock-etag123.tar.gz', ), - ctx.body(repoBuffer), + ctx.body(new Uint8Array(repoBuffer)), ), ), ); diff --git a/packages/backend-defaults/src/entrypoints/urlReader/lib/GithubUrlReader.ts b/packages/backend-defaults/src/entrypoints/urlReader/lib/GithubUrlReader.ts index fe0bd08fec..7846262da3 100644 --- a/packages/backend-defaults/src/entrypoints/urlReader/lib/GithubUrlReader.ts +++ b/packages/backend-defaults/src/entrypoints/urlReader/lib/GithubUrlReader.ts @@ -77,13 +77,21 @@ export class GithubUrlReader implements UrlReaderService { }); }; + private readonly integration: GithubIntegration; + private readonly deps: { + treeResponseFactory: ReadTreeResponseFactory; + credentialsProvider: GithubCredentialsProvider; + }; + constructor( - private readonly integration: GithubIntegration, - private readonly deps: { + integration: GithubIntegration, + deps: { treeResponseFactory: ReadTreeResponseFactory; credentialsProvider: GithubCredentialsProvider; }, ) { + this.integration = integration; + this.deps = deps; if (!integration.config.apiBaseUrl && !integration.config.rawBaseUrl) { throw new Error( `GitHub integration '${integration.title}' must configure an explicit apiBaseUrl or rawBaseUrl`, diff --git a/packages/backend-defaults/src/entrypoints/urlReader/lib/GitlabUrlReader.test.ts b/packages/backend-defaults/src/entrypoints/urlReader/lib/GitlabUrlReader.test.ts index 337325f6f8..c5ce323c54 100644 --- a/packages/backend-defaults/src/entrypoints/urlReader/lib/GitlabUrlReader.test.ts +++ b/packages/backend-defaults/src/entrypoints/urlReader/lib/GitlabUrlReader.test.ts @@ -310,7 +310,7 @@ describe('GitlabUrlReader', () => { 'content-disposition', 'attachment; filename="mock-main-sha123abc.zip"', ), - ctx.body(archiveBuffer), + ctx.body(new Uint8Array(archiveBuffer)), ), ), rest.get( @@ -388,7 +388,7 @@ describe('GitlabUrlReader', () => { 'content-disposition', 'attachment; filename="mock-main-sha123abc.zip"', ), - ctx.body(archiveBuffer), + ctx.body(new Uint8Array(archiveBuffer)), ), ), ); @@ -437,7 +437,7 @@ describe('GitlabUrlReader', () => { 'content-disposition', 'attachment; filename="mock-main-sha123abc.zip"', ), - ctx.body(archiveBuffer), + ctx.body(new Uint8Array(archiveBuffer)), ), ), ); @@ -632,7 +632,7 @@ describe('GitlabUrlReader', () => { 'content-disposition', `attachment; filename="${filename}"`, ), - ctx.body(body), + ctx.body(new Uint8Array(body)), ); }, ), @@ -670,6 +670,17 @@ describe('GitlabUrlReader', () => { ); }); + it('works when there are multiple globs', async () => { + const result = await gitlabProcessor.search( + 'https://gitlab.com/backstage/mock/tree/main/**/docs/**/index.*', + ); + expect(result.etag).toBe('sha123abc'); + expect(result.files.length).toBe(1); + expect(result.files[0].url).toBe( + 'https://gitlab.com/backstage/mock/tree/main/docs/index.md', + ); + }); + it('works for the naive case', async () => { const result = await gitlabProcessor.search( 'https://gitlab.com/backstage/mock/tree/main/**/index.*', diff --git a/packages/backend-defaults/src/entrypoints/urlReader/lib/GitlabUrlReader.ts b/packages/backend-defaults/src/entrypoints/urlReader/lib/GitlabUrlReader.ts index 11746fef0d..ee290e31e6 100644 --- a/packages/backend-defaults/src/entrypoints/urlReader/lib/GitlabUrlReader.ts +++ b/packages/backend-defaults/src/entrypoints/urlReader/lib/GitlabUrlReader.ts @@ -63,10 +63,16 @@ export class GitlabUrlReader implements UrlReaderService { }); }; + private readonly integration: GitLabIntegration; + private readonly deps: { treeResponseFactory: ReadTreeResponseFactory }; + constructor( - private readonly integration: GitLabIntegration, - private readonly deps: { treeResponseFactory: ReadTreeResponseFactory }, - ) {} + integration: GitLabIntegration, + deps: { treeResponseFactory: ReadTreeResponseFactory }, + ) { + this.integration = integration; + this.deps = deps; + } async read(url: string): Promise { const response = await this.readUrl(url); @@ -313,14 +319,10 @@ export class GitlabUrlReader implements UrlReaderService { */ private getStaticPart(globPattern: string) { const segments = globPattern.split('/'); - let i = segments.length; - while ( - i > 0 && - new Minimatch(segments.slice(0, i).join('/')).match(globPattern) - ) { - i--; - } - return segments.slice(0, i).join('/'); + const globIndex = segments.findIndex(segment => segment.match(/[*?]/)); + return globIndex === -1 + ? globPattern + : segments.slice(0, globIndex).join('/'); } toString() { diff --git a/packages/backend-defaults/src/entrypoints/urlReader/lib/GoogleGcsUrlReader.ts b/packages/backend-defaults/src/entrypoints/urlReader/lib/GoogleGcsUrlReader.ts index 36f2e183c7..0bc5add43f 100644 --- a/packages/backend-defaults/src/entrypoints/urlReader/lib/GoogleGcsUrlReader.ts +++ b/packages/backend-defaults/src/entrypoints/urlReader/lib/GoogleGcsUrlReader.ts @@ -88,10 +88,16 @@ export class GoogleGcsUrlReader implements UrlReaderService { return [{ reader, predicate }]; }; + private readonly integration: GoogleGcsIntegrationConfig; + private readonly storage: GoogleCloud.Storage; + constructor( - private readonly integration: GoogleGcsIntegrationConfig, - private readonly storage: GoogleCloud.Storage, - ) {} + integration: GoogleGcsIntegrationConfig, + storage: GoogleCloud.Storage, + ) { + this.integration = integration; + this.storage = storage; + } private readStreamFromUrl(url: string): Readable { const { bucket, key } = parseURL(url); diff --git a/packages/backend-defaults/src/entrypoints/urlReader/lib/HarnessUrlReader.test.ts b/packages/backend-defaults/src/entrypoints/urlReader/lib/HarnessUrlReader.test.ts index 37dab25883..33a0e1a088 100644 --- a/packages/backend-defaults/src/entrypoints/urlReader/lib/HarnessUrlReader.test.ts +++ b/packages/backend-defaults/src/entrypoints/urlReader/lib/HarnessUrlReader.test.ts @@ -215,7 +215,7 @@ describe('HarnessUrlReader', () => { 'content-disposition', 'attachment; filename=backstage-mock.zip', ), - ctx.body(repoBuffer), + ctx.body(new Uint8Array(repoBuffer)), ); }, ), diff --git a/packages/backend-defaults/src/entrypoints/urlReader/lib/HarnessUrlReader.ts b/packages/backend-defaults/src/entrypoints/urlReader/lib/HarnessUrlReader.ts index bc2e0dab11..ebc9b7be83 100644 --- a/packages/backend-defaults/src/entrypoints/urlReader/lib/HarnessUrlReader.ts +++ b/packages/backend-defaults/src/entrypoints/urlReader/lib/HarnessUrlReader.ts @@ -64,12 +64,20 @@ export class HarnessUrlReader implements UrlReaderService { }); }; + private readonly integration: HarnessIntegration; + private readonly deps: { + treeResponseFactory: ReadTreeResponseFactory; + }; + constructor( - private readonly integration: HarnessIntegration, - private readonly deps: { + integration: HarnessIntegration, + deps: { treeResponseFactory: ReadTreeResponseFactory; }, - ) {} + ) { + this.integration = integration; + this.deps = deps; + } async read(url: string): Promise { const response = await this.readUrl(url); return response.buffer(); diff --git a/packages/backend-defaults/src/entrypoints/urlReader/lib/tree/ReadTreeResponseFactory.ts b/packages/backend-defaults/src/entrypoints/urlReader/lib/tree/ReadTreeResponseFactory.ts index a8f187d7b8..816b734b28 100644 --- a/packages/backend-defaults/src/entrypoints/urlReader/lib/tree/ReadTreeResponseFactory.ts +++ b/packages/backend-defaults/src/entrypoints/urlReader/lib/tree/ReadTreeResponseFactory.ts @@ -36,7 +36,11 @@ export class DefaultReadTreeResponseFactory implements ReadTreeResponseFactory { ); } - constructor(private readonly workDir: string) {} + private readonly workDir: string; + + constructor(workDir: string) { + this.workDir = workDir; + } async fromTarArchive( options: ReadTreeResponseFactoryOptions & { diff --git a/packages/backend-defaults/src/entrypoints/urlReader/lib/tree/ReadableArrayResponse.ts b/packages/backend-defaults/src/entrypoints/urlReader/lib/tree/ReadableArrayResponse.ts index 6b39672ddd..095b58edb1 100644 --- a/packages/backend-defaults/src/entrypoints/urlReader/lib/tree/ReadableArrayResponse.ts +++ b/packages/backend-defaults/src/entrypoints/urlReader/lib/tree/ReadableArrayResponse.ts @@ -35,12 +35,13 @@ const pipeline = promisify(pipelineCb); */ export class ReadableArrayResponse implements UrlReaderServiceReadTreeResponse { private read = false; + private readonly stream: FromReadableArrayOptions; + private readonly workDir: string; + public readonly etag: string; - constructor( - private readonly stream: FromReadableArrayOptions, - private readonly workDir: string, - public readonly etag: string, - ) { + constructor(stream: FromReadableArrayOptions, workDir: string, etag: string) { + this.stream = stream; + this.workDir = workDir; this.etag = etag; } diff --git a/packages/backend-defaults/src/entrypoints/urlReader/lib/tree/TarArchiveResponse.ts b/packages/backend-defaults/src/entrypoints/urlReader/lib/tree/TarArchiveResponse.ts index 9fb1d3fe7e..3102137f64 100644 --- a/packages/backend-defaults/src/entrypoints/urlReader/lib/tree/TarArchiveResponse.ts +++ b/packages/backend-defaults/src/entrypoints/urlReader/lib/tree/TarArchiveResponse.ts @@ -37,15 +37,27 @@ const pipeline = promisify(pipelineCb); */ export class TarArchiveResponse implements UrlReaderServiceReadTreeResponse { private read = false; + private readonly stream: Readable; + private readonly subPath: string; + private readonly workDir: string; + public readonly etag: string; + private readonly filter?: (path: string, info: { size: number }) => boolean; + private readonly stripFirstDirectory: boolean; constructor( - private readonly stream: Readable, - private readonly subPath: string, - private readonly workDir: string, - public readonly etag: string, - private readonly filter?: (path: string, info: { size: number }) => boolean, - private readonly stripFirstDirectory: boolean = true, + stream: Readable, + subPath: string, + workDir: string, + etag: string, + filter?: (path: string, info: { size: number }) => boolean, + stripFirstDirectory: boolean = true, ) { + this.stream = stream; + this.subPath = subPath; + this.workDir = workDir; + this.etag = etag; + this.filter = filter; + this.stripFirstDirectory = stripFirstDirectory; if (subPath) { if (!subPath.endsWith('/')) { this.subPath += '/'; diff --git a/packages/backend-defaults/src/entrypoints/urlReader/lib/tree/ZipArchiveResponse.ts b/packages/backend-defaults/src/entrypoints/urlReader/lib/tree/ZipArchiveResponse.ts index 5c0b6f77bc..2d5a743172 100644 --- a/packages/backend-defaults/src/entrypoints/urlReader/lib/tree/ZipArchiveResponse.ts +++ b/packages/backend-defaults/src/entrypoints/urlReader/lib/tree/ZipArchiveResponse.ts @@ -32,14 +32,24 @@ import { resolveSafeChildPath } from '@backstage/backend-plugin-api'; */ export class ZipArchiveResponse implements UrlReaderServiceReadTreeResponse { private read = false; + private readonly stream: Readable; + private readonly subPath: string; + private readonly workDir: string; + public readonly etag: string; + private readonly filter?: (path: string, info: { size: number }) => boolean; constructor( - private readonly stream: Readable, - private readonly subPath: string, - private readonly workDir: string, - public readonly etag: string, - private readonly filter?: (path: string, info: { size: number }) => boolean, + stream: Readable, + subPath: string, + workDir: string, + etag: string, + filter?: (path: string, info: { size: number }) => boolean, ) { + this.stream = stream; + this.subPath = subPath; + this.workDir = workDir; + this.etag = etag; + this.filter = filter; if (subPath) { if (!subPath.endsWith('/')) { this.subPath += '/'; diff --git a/packages/backend-defaults/src/setupTests.ts b/packages/backend-defaults/src/setupTests.ts index c9745b280d..7d47eea453 100644 --- a/packages/backend-defaults/src/setupTests.ts +++ b/packages/backend-defaults/src/setupTests.ts @@ -21,5 +21,5 @@ import { Settings } from 'luxon'; Settings.throwOnInvalid = true; TestDatabases.setDefaults({ - ids: ['MYSQL_8', 'POSTGRES_17', 'POSTGRES_13', 'SQLITE_3'], + ids: ['MYSQL_8', 'POSTGRES_18', 'POSTGRES_14', 'SQLITE_3'], }); diff --git a/packages/backend-dynamic-feature-service/CHANGELOG.md b/packages/backend-dynamic-feature-service/CHANGELOG.md index a15b315a62..6e5ff62fd1 100644 --- a/packages/backend-dynamic-feature-service/CHANGELOG.md +++ b/packages/backend-dynamic-feature-service/CHANGELOG.md @@ -1,5 +1,70 @@ # @backstage/backend-dynamic-feature-service +## 0.7.6-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@3.2.0-next.1 + - @backstage/backend-defaults@0.13.1-next.1 + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-permission-common@0.9.3-next.1 + - @backstage/backend-openapi-utils@0.6.3-next.1 + - @backstage/plugin-app-node@0.1.39-next.1 + - @backstage/plugin-auth-node@0.6.9-next.1 + - @backstage/plugin-events-backend@0.5.8-next.1 + - @backstage/plugin-events-node@0.4.17-next.1 + - @backstage/plugin-permission-node@0.10.6-next.1 + - @backstage/plugin-scaffolder-node@0.12.1-next.1 + - @backstage/plugin-search-backend-node@1.3.17-next.1 + +## 0.7.6-next.0 + +### Patch Changes + +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/backend-defaults@0.13.1-next.0 + - @backstage/plugin-catalog-backend@3.1.3-next.0 + - @backstage/plugin-search-backend-node@1.3.17-next.0 + - @backstage/plugin-events-backend@0.5.8-next.0 + - @backstage/plugin-events-node@0.4.17-next.0 + - @backstage/plugin-auth-node@0.6.9-next.0 + - @backstage/config-loader@1.10.6-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/cli-node@0.2.15-next.0 + - @backstage/plugin-scaffolder-node@0.12.1-next.0 + - @backstage/plugin-permission-node@0.10.6-next.0 + - @backstage/backend-openapi-utils@0.6.3-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/cli-common@0.1.15 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-app-node@0.1.39-next.0 + - @backstage/plugin-permission-common@0.9.3-next.0 + - @backstage/plugin-search-common@1.2.21-next.0 + +## 0.7.5 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@3.1.2 + - @backstage/backend-defaults@0.13.0 + - @backstage/plugin-scaffolder-node@0.12.0 + - @backstage/config-loader@1.10.5 + - @backstage/config@1.3.5 + - @backstage/backend-openapi-utils@0.6.2 + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-app-node@0.1.38 + - @backstage/plugin-auth-node@0.6.8 + - @backstage/plugin-events-backend@0.5.7 + - @backstage/plugin-events-node@0.4.16 + - @backstage/plugin-permission-common@0.9.2 + - @backstage/plugin-permission-node@0.10.5 + - @backstage/plugin-search-backend-node@1.3.16 + - @backstage/plugin-search-common@1.2.20 + ## 0.7.5-next.1 ### Patch Changes diff --git a/packages/backend-dynamic-feature-service/package.json b/packages/backend-dynamic-feature-service/package.json index 78e881a6bc..c96ad18d94 100644 --- a/packages/backend-dynamic-feature-service/package.json +++ b/packages/backend-dynamic-feature-service/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/backend-dynamic-feature-service", - "version": "0.7.5-next.1", + "version": "0.7.6-next.1", "description": "Backstage dynamic feature service", "backstage": { "role": "node-library" diff --git a/packages/backend-dynamic-feature-service/src/loader/CommonJSModuleLoader.ts b/packages/backend-dynamic-feature-service/src/loader/CommonJSModuleLoader.ts index 28203a2019..b960e5bd38 100644 --- a/packages/backend-dynamic-feature-service/src/loader/CommonJSModuleLoader.ts +++ b/packages/backend-dynamic-feature-service/src/loader/CommonJSModuleLoader.ts @@ -36,8 +36,10 @@ export type CommonJSModuleLoaderOptions = { */ export class CommonJSModuleLoader implements ModuleLoader { private module: any; + public readonly options: CommonJSModuleLoaderOptions; - constructor(public readonly options: CommonJSModuleLoaderOptions) { + constructor(options: CommonJSModuleLoaderOptions) { + this.options = options; this.module = require('node:module'); } diff --git a/packages/backend-dynamic-feature-service/src/manager/plugin-manager.ts b/packages/backend-dynamic-feature-service/src/manager/plugin-manager.ts index 0162cbef2d..02ddd6168b 100644 --- a/packages/backend-dynamic-feature-service/src/manager/plugin-manager.ts +++ b/packages/backend-dynamic-feature-service/src/manager/plugin-manager.ts @@ -99,11 +99,18 @@ export class DynamicPluginManager implements DynamicPluginProvider { private readonly _plugins: DynamicPlugin[]; private _availablePackages: ScannedPluginPackage[]; + private readonly logger: LoggerService; + private readonly packages: ScannedPluginPackage[]; + private readonly moduleLoader: ModuleLoader; + private constructor( - private readonly logger: LoggerService, - private readonly packages: ScannedPluginPackage[], - private readonly moduleLoader: ModuleLoader, + logger: LoggerService, + packages: ScannedPluginPackage[], + moduleLoader: ModuleLoader, ) { + this.logger = logger; + this.packages = packages; + this.moduleLoader = moduleLoader; this._plugins = []; this._availablePackages = packages; } @@ -304,7 +311,11 @@ export const dynamicPluginsServiceFactory = Object.assign( ); class DynamicPluginsEnabledFeatureDiscoveryService { - constructor(private readonly dynamicPlugins: DynamicPluginProvider) {} + private readonly dynamicPlugins: DynamicPluginProvider; + + constructor(dynamicPlugins: DynamicPluginProvider) { + this.dynamicPlugins = dynamicPlugins; + } async getBackendFeatures(): Promise<{ features: Array }> { return { diff --git a/packages/backend-dynamic-feature-service/src/scanner/plugin-scanner.ts b/packages/backend-dynamic-feature-service/src/scanner/plugin-scanner.ts index 5b254908b2..a468a382d5 100644 --- a/packages/backend-dynamic-feature-service/src/scanner/plugin-scanner.ts +++ b/packages/backend-dynamic-feature-service/src/scanner/plugin-scanner.ts @@ -42,13 +42,22 @@ export class PluginScanner { private configUnsubscribe?: () => void; private rootDirectoryWatcher?: chokidar.FSWatcher; private subscribers: (() => void)[] = []; + private readonly config: Config; + private readonly logger: LoggerService; + private readonly backstageRoot: string; + private readonly preferAlpha: boolean; private constructor( - private readonly config: Config, - private readonly logger: LoggerService, - private readonly backstageRoot: string, - private readonly preferAlpha: boolean, - ) {} + config: Config, + logger: LoggerService, + backstageRoot: string, + preferAlpha: boolean, + ) { + this.config = config; + this.logger = logger; + this.backstageRoot = backstageRoot; + this.preferAlpha = preferAlpha; + } static create(options: DynamicPluginScannerOptions): PluginScanner { const scanner = new PluginScanner( diff --git a/packages/backend-openapi-utils/CHANGELOG.md b/packages/backend-openapi-utils/CHANGELOG.md index 61815004c3..109df60032 100644 --- a/packages/backend-openapi-utils/CHANGELOG.md +++ b/packages/backend-openapi-utils/CHANGELOG.md @@ -1,5 +1,28 @@ # @backstage/backend-openapi-utils +## 0.6.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + +## 0.6.3-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + +## 0.6.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.4 + ## 0.6.2-next.0 ### Patch Changes diff --git a/packages/backend-openapi-utils/package.json b/packages/backend-openapi-utils/package.json index 48211fa851..d88bfb2a91 100644 --- a/packages/backend-openapi-utils/package.json +++ b/packages/backend-openapi-utils/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/backend-openapi-utils", - "version": "0.6.2-next.0", + "version": "0.6.3-next.1", "description": "OpenAPI typescript support.", "backstage": { "role": "node-library" diff --git a/packages/backend-plugin-api/CHANGELOG.md b/packages/backend-plugin-api/CHANGELOG.md index 44931c21e1..a12734cc98 100644 --- a/packages/backend-plugin-api/CHANGELOG.md +++ b/packages/backend-plugin-api/CHANGELOG.md @@ -1,5 +1,47 @@ # @backstage/backend-plugin-api +## 1.5.0-next.2 + +### Minor Changes + +- 62fc2de: Explicitly mark `coreServices.rootInstanceMetadata` as a root service. + +## 1.5.0-next.1 + +### Minor Changes + +- a17d9df: Promote `instanceMetadata` service to main entrypoint. + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-common@0.9.3-next.1 + - @backstage/plugin-auth-node@0.6.9-next.1 + - @backstage/plugin-permission-node@0.10.6-next.1 + +## 1.4.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.6.9-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/plugin-permission-node@0.10.6-next.0 + - @backstage/cli-common@0.1.15 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-permission-common@0.9.3-next.0 + +## 1.4.4 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.5 + - @backstage/plugin-auth-node@0.6.8 + - @backstage/plugin-permission-common@0.9.2 + - @backstage/plugin-permission-node@0.10.5 + ## 1.4.4-next.0 ### Patch Changes diff --git a/packages/backend-plugin-api/package.json b/packages/backend-plugin-api/package.json index fcdbd15107..90d3959658 100644 --- a/packages/backend-plugin-api/package.json +++ b/packages/backend-plugin-api/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/backend-plugin-api", - "version": "1.4.4-next.0", + "version": "1.5.0-next.2", "description": "Core API used by Backstage backend plugins", "backstage": { "role": "node-library" diff --git a/packages/backend-plugin-api/report.api.md b/packages/backend-plugin-api/report.api.md index 52843dec96..c46ed254c8 100644 --- a/packages/backend-plugin-api/report.api.md +++ b/packages/backend-plugin-api/report.api.md @@ -232,6 +232,11 @@ export namespace coreServices { const rootLogger: ServiceRef; const scheduler: ServiceRef; const urlReader: ServiceRef; + const rootInstanceMetadata: ServiceRef< + RootInstanceMetadataService, + 'root', + 'singleton' + >; } // @public @@ -578,6 +583,24 @@ export interface RootHttpRouterService { use(path: string, handler: Handler): void; } +// @public (undocumented) +export interface RootInstanceMetadataService { + // (undocumented) + getInstalledPlugins: () => Promise< + ReadonlyArray + >; +} + +// @public (undocumented) +export interface RootInstanceMetadataServicePluginInfo { + // (undocumented) + readonly modules: ReadonlyArray<{ + moduleId: string; + }>; + // (undocumented) + readonly pluginId: string; +} + // @public export interface RootLifecycleService extends LifecycleService { // (undocumented) diff --git a/packages/backend-plugin-api/src/alpha/InstanceMetadataService.ts b/packages/backend-plugin-api/src/alpha/InstanceMetadataService.ts index 869d361343..a82a261d9d 100644 --- a/packages/backend-plugin-api/src/alpha/InstanceMetadataService.ts +++ b/packages/backend-plugin-api/src/alpha/InstanceMetadataService.ts @@ -1,5 +1,5 @@ /* - * Copyright 2024 The Backstage Authors + * Copyright 2025 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/ui/src/components/Icon/context.tsx b/packages/backend-plugin-api/src/services/definitions/RootInstanceMetadataService.ts similarity index 67% rename from packages/ui/src/components/Icon/context.tsx rename to packages/backend-plugin-api/src/services/definitions/RootInstanceMetadataService.ts index 4d1974d081..c324fed331 100644 --- a/packages/ui/src/components/Icon/context.tsx +++ b/packages/backend-plugin-api/src/services/definitions/RootInstanceMetadataService.ts @@ -14,14 +14,17 @@ * limitations under the License. */ -import { createContext, useContext } from 'react'; -import { icons } from './icons'; -import type { IconContextProps } from './types'; +/** @public */ +export interface RootInstanceMetadataServicePluginInfo { + readonly pluginId: string; + readonly modules: ReadonlyArray<{ + moduleId: string; + }>; +} /** @public */ -export const IconContext = createContext({ - icons, -}); - -/** @public */ -export const useIcons = () => useContext(IconContext); +export interface RootInstanceMetadataService { + getInstalledPlugins: () => Promise< + ReadonlyArray + >; +} diff --git a/packages/backend-plugin-api/src/services/definitions/coreServices.ts b/packages/backend-plugin-api/src/services/definitions/coreServices.ts index 8c8c6c0f82..887b05ee4e 100644 --- a/packages/backend-plugin-api/src/services/definitions/coreServices.ts +++ b/packages/backend-plugin-api/src/services/definitions/coreServices.ts @@ -277,4 +277,16 @@ export namespace coreServices { export const urlReader = createServiceRef< import('./UrlReaderService').UrlReaderService >({ id: 'core.urlReader' }); + + /** + * Information about the current Backstage instance. + * + * @public + */ + export const rootInstanceMetadata = createServiceRef< + import('./RootInstanceMetadataService').RootInstanceMetadataService + >({ + id: 'core.rootInstanceMetadata', + scope: 'root', + }); } diff --git a/packages/backend-plugin-api/src/services/definitions/index.ts b/packages/backend-plugin-api/src/services/definitions/index.ts index c811a513a8..6bcee4b043 100644 --- a/packages/backend-plugin-api/src/services/definitions/index.ts +++ b/packages/backend-plugin-api/src/services/definitions/index.ts @@ -85,4 +85,8 @@ export type { UrlReaderServiceSearchResponseFile, } from './UrlReaderService'; export type { BackstageUserInfo, UserInfoService } from './UserInfoService'; +export type { + RootInstanceMetadataService, + RootInstanceMetadataServicePluginInfo, +} from './RootInstanceMetadataService'; export { coreServices } from './coreServices'; diff --git a/packages/backend-test-utils/CHANGELOG.md b/packages/backend-test-utils/CHANGELOG.md index 536d595d34..a30fc1fa68 100644 --- a/packages/backend-test-utils/CHANGELOG.md +++ b/packages/backend-test-utils/CHANGELOG.md @@ -1,5 +1,60 @@ # @backstage/backend-test-utils +## 1.10.0-next.2 + +### Patch Changes + +- f3001fd: Tweak some of the mock services to have more precise types +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.2 + +## 1.10.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.13.1-next.1 + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/backend-app-api@1.3.0-next.1 + - @backstage/plugin-permission-common@0.9.3-next.1 + - @backstage/plugin-auth-node@0.6.9-next.1 + - @backstage/plugin-events-node@0.4.17-next.1 + +## 1.10.0-next.0 + +### Minor Changes + +- d57b13b: Added support for Postgres 18 to the available `TestDatabases`. + + Note that the set of _default_ databases to test against for users of the `TestDatabases` class was also updated to include Postgres 14 and 18, instead of 13 and 17. If you need to override this, you can pass in an explicit `ids` argument, for example `ids: ['POSTGRES_17', 'POSTGRES_13', 'SQLITE_3']`. + +### Patch Changes + +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/backend-defaults@0.13.1-next.0 + - @backstage/plugin-events-node@0.4.17-next.0 + - @backstage/plugin-auth-node@0.6.9-next.0 + - @backstage/backend-app-api@1.2.9-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/plugin-permission-common@0.9.3-next.0 + +## 1.9.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.13.0 + - @backstage/backend-app-api@1.2.8 + - @backstage/config@1.3.5 + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-auth-node@0.6.8 + - @backstage/plugin-events-node@0.4.16 + - @backstage/plugin-permission-common@0.9.2 + ## 1.9.1-next.1 ### Patch Changes diff --git a/packages/backend-test-utils/package.json b/packages/backend-test-utils/package.json index 3ee214b345..ea0c2d0c85 100644 --- a/packages/backend-test-utils/package.json +++ b/packages/backend-test-utils/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/backend-test-utils", - "version": "1.9.1-next.1", + "version": "1.10.0-next.2", "description": "Test helpers library for Backstage backends", "backstage": { "role": "node-library" diff --git a/packages/backend-test-utils/report.api.md b/packages/backend-test-utils/report.api.md index 02fa9d8587..da5002275b 100644 --- a/packages/backend-test-utils/report.api.md +++ b/packages/backend-test-utils/report.api.md @@ -35,6 +35,7 @@ import { PermissionsService } from '@backstage/backend-plugin-api'; import { RootConfigService } from '@backstage/backend-plugin-api'; import { RootHealthService } from '@backstage/backend-plugin-api'; import { RootHttpRouterService } from '@backstage/backend-plugin-api'; +import { RootInstanceMetadataService } from '@backstage/backend-plugin-api'; import { RootLifecycleService } from '@backstage/backend-plugin-api'; import { RootLoggerService } from '@backstage/backend-plugin-api'; import { SchedulerService } from '@backstage/backend-plugin-api'; @@ -229,11 +230,7 @@ export namespace mockServices { export function events(): EventsService; // (undocumented) export namespace events { - const factory: () => ServiceFactory< - EventsService, - 'plugin', - 'singleton' | 'multiton' - >; + const factory: () => ServiceFactory; const mock: ( partialImpl?: Partial | undefined, ) => ServiceMock; @@ -319,7 +316,7 @@ export namespace mockServices { const // (undocumented) factory: ( options?: Options | undefined, - ) => ServiceFactory; + ) => ServiceFactory; const // (undocumented) mock: ( partialImpl?: Partial | undefined, @@ -344,6 +341,21 @@ export namespace mockServices { ) => ServiceMock; } // (undocumented) + export function rootInstanceMetadata(): RootInstanceMetadataService; + // (undocumented) + export namespace rootInstanceMetadata { + const // (undocumented) + mock: ( + partialImpl?: Partial | undefined, + ) => ServiceMock; + const // (undocumented) + factory: () => ServiceFactory< + RootInstanceMetadataService, + 'root', + 'singleton' + >; + } + // (undocumented) export namespace rootLifecycle { const // (undocumented) factory: () => ServiceFactory; @@ -353,7 +365,7 @@ export namespace mockServices { ) => ServiceMock; } // (undocumented) - export function rootLogger(options?: rootLogger.Options): LoggerService; + export function rootLogger(options?: rootLogger.Options): RootLoggerService; // (undocumented) export namespace rootLogger { // (undocumented) @@ -363,7 +375,7 @@ export namespace mockServices { const // (undocumented) factory: ( options?: Options | undefined, - ) => ServiceFactory; + ) => ServiceFactory; const // (undocumented) mock: ( partialImpl?: Partial | undefined, @@ -510,6 +522,7 @@ export class TestCaches { // @public export type TestDatabaseId = + | 'POSTGRES_18' | 'POSTGRES_17' | 'POSTGRES_16' | 'POSTGRES_15' diff --git a/packages/backend-test-utils/src/alpha/services/MockActionsRegistry.ts b/packages/backend-test-utils/src/alpha/services/MockActionsRegistry.ts index 49c80b7a3d..94e5987afd 100644 --- a/packages/backend-test-utils/src/alpha/services/MockActionsRegistry.ts +++ b/packages/backend-test-utils/src/alpha/services/MockActionsRegistry.ts @@ -65,7 +65,11 @@ import { export class MockActionsRegistry implements ActionsRegistryService, ActionsService { - private constructor(private readonly logger: LoggerService) {} + private readonly logger: LoggerService; + + private constructor(logger: LoggerService) { + this.logger = logger; + } static create(opts: { logger: LoggerService }) { return new MockActionsRegistry(opts.logger); diff --git a/packages/backend-test-utils/src/database/TestDatabases.ts b/packages/backend-test-utils/src/database/TestDatabases.ts index cd20e2997b..8baac04193 100644 --- a/packages/backend-test-utils/src/database/TestDatabases.ts +++ b/packages/backend-test-utils/src/database/TestDatabases.ts @@ -131,7 +131,7 @@ export class TestDatabases { * Returns a fresh, unique, empty logical database on an instance of the * given database ID platform. * - * @param id - The ID of the database platform to use, e.g. 'POSTGRES_13' + * @param id - The ID of the database platform to use, e.g. 'POSTGRES_14' * @returns A `Knex` connection object */ async init(id: TestDatabaseId): Promise { diff --git a/packages/backend-test-utils/src/database/types.ts b/packages/backend-test-utils/src/database/types.ts index e1ef61a93b..30e7e2f905 100644 --- a/packages/backend-test-utils/src/database/types.ts +++ b/packages/backend-test-utils/src/database/types.ts @@ -28,6 +28,7 @@ export interface Engine { * @public */ export type TestDatabaseId = + | 'POSTGRES_18' | 'POSTGRES_17' | 'POSTGRES_16' | 'POSTGRES_15' @@ -48,6 +49,13 @@ export type TestDatabaseProperties = { export const allDatabases: Record = Object.freeze({ + POSTGRES_18: { + name: 'Postgres 18.x', + driver: 'pg', + dockerImageName: getDockerImageForName('postgres:18'), + connectionStringEnvironmentVariableName: + 'BACKSTAGE_TEST_DATABASE_POSTGRES18_CONNECTION_STRING', + }, POSTGRES_17: { name: 'Postgres 17.x', driver: 'pg', diff --git a/packages/backend-test-utils/src/services/mockServices.ts b/packages/backend-test-utils/src/services/mockServices.ts index f7d57d3fb1..9e941db3bb 100644 --- a/packages/backend-test-utils/src/services/mockServices.ts +++ b/packages/backend-test-utils/src/services/mockServices.ts @@ -34,7 +34,7 @@ import { DatabaseService, DiscoveryService, HttpAuthService, - LoggerService, + RootInstanceMetadataService, PermissionsService, RootConfigService, SchedulerService, @@ -43,6 +43,7 @@ import { UserInfoService, coreServices, createServiceFactory, + RootLoggerService, } from '@backstage/backend-plugin-api'; import { ConfigReader } from '@backstage/config'; import { EventsService, eventsServiceRef } from '@backstage/plugin-events-node'; @@ -76,14 +77,15 @@ function createLoggerMock() { function simpleFactoryWithOptions< TService, TScope extends 'root' | 'plugin', + TInstances extends 'singleton' | 'multiton', TOptions extends [options?: object] = [], >( - ref: ServiceRef, + ref: ServiceRef, factory: (...options: TOptions) => TService, -): (...options: TOptions) => ServiceFactory { +): (...options: TOptions) => ServiceFactory { const factoryWithOptions = (...options: TOptions) => createServiceFactory({ - service: ref as ServiceRef, + service: ref as ServiceRef, deps: {}, async factory() { return factory(...options); @@ -92,8 +94,8 @@ function simpleFactoryWithOptions< return Object.assign( factoryWithOptions, factoryWithOptions(...([undefined] as unknown as TOptions)), - ) as ServiceFactory & - ((...options: TOptions) => ServiceFactory); + ) as ServiceFactory & + ((...options: TOptions) => ServiceFactory); } /** @@ -177,7 +179,7 @@ export namespace mockServices { })); } - export function rootLogger(options?: rootLogger.Options): LoggerService { + export function rootLogger(options?: rootLogger.Options): RootLoggerService { return MockRootLoggerService.create(options); } export namespace rootLogger { @@ -556,4 +558,19 @@ export namespace mockServices { subscribe: jest.fn(), })); } + + export function rootInstanceMetadata(): RootInstanceMetadataService { + return { + getInstalledPlugins: () => Promise.resolve([]), + }; + } + export namespace rootInstanceMetadata { + export const mock = simpleMock(coreServices.rootInstanceMetadata, () => ({ + getInstalledPlugins: jest.fn(), + })); + export const factory = simpleFactoryWithOptions( + coreServices.rootInstanceMetadata, + rootInstanceMetadata, + ); + } } diff --git a/packages/backend/CHANGELOG.md b/packages/backend/CHANGELOG.md index f8c601b473..cb84bcbc4c 100644 --- a/packages/backend/CHANGELOG.md +++ b/packages/backend/CHANGELOG.md @@ -1,5 +1,142 @@ # example-backend +## 0.0.44-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-notifications-backend@0.6.0-next.2 + - @backstage/backend-plugin-api@1.5.0-next.2 + - @backstage/plugin-kubernetes-backend@0.20.4-next.2 + - @backstage/plugin-scaffolder-backend-module-notifications@0.1.16-next.2 + - @backstage/plugin-catalog-backend@3.2.0-next.1 + - @backstage/plugin-events-backend@0.5.8-next.1 + - @backstage/plugin-scaffolder-backend@3.0.1-next.1 + - @backstage/plugin-search-backend@2.0.8-next.1 + +## 0.0.44-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@3.2.0-next.1 + - @backstage/plugin-search-backend-module-elasticsearch@1.7.8-next.1 + - @backstage/plugin-kubernetes-backend@0.20.4-next.1 + - @backstage/plugin-techdocs-backend@2.1.2-next.1 + - @backstage/plugin-signals-backend@0.3.10-next.1 + - @backstage/backend-defaults@0.13.1-next.1 + - @backstage/plugin-notifications-backend@0.5.12-next.1 + - @backstage/plugin-catalog-backend-module-openapi@0.2.16-next.1 + - @backstage/backend-plugin-api@1.5.0-next.1 + - @backstage/plugin-permission-backend@0.7.6-next.1 + - @backstage/plugin-permission-common@0.9.3-next.1 + - @backstage/plugin-search-backend@2.0.8-next.1 + - @backstage/plugin-auth-backend@0.25.6-next.1 + - @backstage/plugin-catalog-backend-module-backstage-openapi@0.5.8-next.1 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.2.14-next.1 + - @backstage/plugin-catalog-backend-module-unprocessed@0.6.6-next.1 + - @backstage/plugin-mcp-actions-backend@0.1.5-next.1 + - @backstage/plugin-scaffolder-backend@3.0.1-next.1 + - @backstage/plugin-scaffolder-backend-module-github@0.9.2-next.1 + - @backstage/plugin-search-backend-module-catalog@0.3.10-next.1 + - @backstage/plugin-search-backend-module-techdocs@0.4.8-next.1 + - @backstage/plugin-app-backend@0.5.8-next.1 + - @backstage/plugin-auth-backend-module-github-provider@0.3.9-next.1 + - @backstage/plugin-auth-backend-module-guest-provider@0.2.14-next.1 + - @backstage/plugin-auth-backend-module-openshift-provider@0.1.2-next.1 + - @backstage/plugin-auth-node@0.6.9-next.1 + - @backstage/plugin-devtools-backend@0.5.11-next.1 + - @backstage/plugin-events-backend@0.5.8-next.1 + - @backstage/plugin-events-backend-module-google-pubsub@0.1.6-next.1 + - @backstage/plugin-permission-backend-module-allow-all-policy@0.2.14-next.1 + - @backstage/plugin-permission-node@0.10.6-next.1 + - @backstage/plugin-proxy-backend@0.6.8-next.1 + - @backstage/plugin-scaffolder-backend-module-notifications@0.1.16-next.1 + - @backstage/plugin-search-backend-module-explore@0.3.9-next.1 + - @backstage/plugin-search-backend-node@1.3.17-next.1 + +## 0.0.44-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.13.1-next.0 + - @backstage/plugin-catalog-backend@3.1.3-next.0 + - @backstage/plugin-notifications-backend@0.5.12-next.0 + - @backstage/plugin-scaffolder-backend@3.0.1-next.0 + - @backstage/plugin-search-backend-module-elasticsearch@1.7.8-next.0 + - @backstage/plugin-search-backend@2.0.8-next.0 + - @backstage/plugin-search-backend-node@1.3.17-next.0 + - @backstage/plugin-kubernetes-backend@0.20.4-next.0 + - @backstage/plugin-events-backend@0.5.8-next.0 + - @backstage/plugin-catalog-backend-module-backstage-openapi@0.5.8-next.0 + - @backstage/plugin-auth-backend@0.25.6-next.0 + - @backstage/plugin-devtools-backend@0.5.11-next.0 + - @backstage/plugin-catalog-backend-module-unprocessed@0.6.6-next.0 + - @backstage/plugin-auth-node@0.6.9-next.0 + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/plugin-mcp-actions-backend@0.1.5-next.0 + - @backstage/plugin-app-backend@0.5.8-next.0 + - @backstage/plugin-auth-backend-module-github-provider@0.3.9-next.0 + - @backstage/plugin-auth-backend-module-openshift-provider@0.1.2-next.0 + - @backstage/plugin-events-backend-module-google-pubsub@0.1.6-next.0 + - @backstage/plugin-permission-backend@0.7.6-next.0 + - @backstage/plugin-permission-node@0.10.6-next.0 + - @backstage/plugin-proxy-backend@0.6.8-next.0 + - @backstage/plugin-signals-backend@0.3.10-next.0 + - @backstage/plugin-techdocs-backend@2.1.2-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/plugin-auth-backend-module-guest-provider@0.2.14-next.0 + - @backstage/plugin-catalog-backend-module-openapi@0.2.16-next.0 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.2.14-next.0 + - @backstage/plugin-permission-backend-module-allow-all-policy@0.2.14-next.0 + - @backstage/plugin-permission-common@0.9.3-next.0 + - @backstage/plugin-scaffolder-backend-module-github@0.9.2-next.0 + - @backstage/plugin-scaffolder-backend-module-notifications@0.1.16-next.0 + - @backstage/plugin-search-backend-module-catalog@0.3.10-next.0 + - @backstage/plugin-search-backend-module-explore@0.3.9-next.0 + - @backstage/plugin-search-backend-module-techdocs@0.4.8-next.0 + +## 0.0.43 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@3.1.2 + - @backstage/backend-defaults@0.13.0 + - @backstage/plugin-scaffolder-backend-module-notifications@0.1.15 + - @backstage/plugin-scaffolder-backend-module-github@0.9.1 + - @backstage/plugin-scaffolder-backend@3.0.0 + - @backstage/plugin-notifications-backend@0.5.11 + - @backstage/backend-plugin-api@1.4.4 + - @backstage/plugin-app-backend@0.5.7 + - @backstage/plugin-auth-backend@0.25.5 + - @backstage/plugin-auth-backend-module-github-provider@0.3.8 + - @backstage/plugin-auth-backend-module-guest-provider@0.2.13 + - @backstage/plugin-auth-backend-module-openshift-provider@0.1.1 + - @backstage/plugin-auth-node@0.6.8 + - @backstage/plugin-catalog-backend-module-backstage-openapi@0.5.7 + - @backstage/plugin-catalog-backend-module-openapi@0.2.15 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.2.13 + - @backstage/plugin-catalog-backend-module-unprocessed@0.6.5 + - @backstage/plugin-devtools-backend@0.5.10 + - @backstage/plugin-events-backend@0.5.7 + - @backstage/plugin-events-backend-module-google-pubsub@0.1.5 + - @backstage/plugin-kubernetes-backend@0.20.3 + - @backstage/plugin-mcp-actions-backend@0.1.4 + - @backstage/plugin-permission-backend@0.7.5 + - @backstage/plugin-permission-backend-module-allow-all-policy@0.2.13 + - @backstage/plugin-permission-common@0.9.2 + - @backstage/plugin-permission-node@0.10.5 + - @backstage/plugin-proxy-backend@0.6.7 + - @backstage/plugin-search-backend@2.0.7 + - @backstage/plugin-search-backend-module-catalog@0.3.9 + - @backstage/plugin-search-backend-module-explore@0.3.8 + - @backstage/plugin-search-backend-module-techdocs@0.4.7 + - @backstage/plugin-search-backend-node@1.3.16 + - @backstage/plugin-signals-backend@0.3.9 + - @backstage/plugin-techdocs-backend@2.1.1 + ## 0.0.43-next.1 ### Patch Changes diff --git a/packages/backend/package.json b/packages/backend/package.json index c698a424a5..199304ae5a 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -1,6 +1,6 @@ { "name": "example-backend", - "version": "0.0.43-next.1", + "version": "0.0.44-next.2", "backstage": { "role": "backend" }, @@ -60,6 +60,7 @@ "@backstage/plugin-scaffolder-backend-module-notifications": "workspace:^", "@backstage/plugin-search-backend": "workspace:^", "@backstage/plugin-search-backend-module-catalog": "workspace:^", + "@backstage/plugin-search-backend-module-elasticsearch": "workspace:^", "@backstage/plugin-search-backend-module-explore": "workspace:^", "@backstage/plugin-search-backend-module-techdocs": "workspace:^", "@backstage/plugin-search-backend-node": "workspace:^", diff --git a/packages/backend/src/index.ts b/packages/backend/src/index.ts index e32d3148b3..541c3bd791 100644 --- a/packages/backend/src/index.ts +++ b/packages/backend/src/index.ts @@ -15,18 +15,27 @@ */ import { createBackend } from '@backstage/backend-defaults'; -import { createBackendFeatureLoader } from '@backstage/backend-plugin-api'; +import { + coreServices, + createBackendFeatureLoader, +} from '@backstage/backend-plugin-api'; const backend = createBackend(); // An example of how to group together and load multiple features. You can also // access root-scoped services by adding `deps`. const searchLoader = createBackendFeatureLoader({ - *loader() { + deps: { + config: coreServices.rootConfig, + }, + *loader({ config }) { yield import('@backstage/plugin-search-backend'); yield import('@backstage/plugin-search-backend-module-catalog'); yield import('@backstage/plugin-search-backend-module-explore'); yield import('@backstage/plugin-search-backend-module-techdocs'); + if (config.has('search.elasticsearch')) { + yield import('@backstage/plugin-search-backend-module-elasticsearch'); + } }, }); diff --git a/packages/backend/src/instanceMetadata.ts b/packages/backend/src/instanceMetadata.ts index 5f835f3117..026fc6fd02 100644 --- a/packages/backend/src/instanceMetadata.ts +++ b/packages/backend/src/instanceMetadata.ts @@ -17,21 +17,21 @@ import { coreServices, createBackendPlugin, } from '@backstage/backend-plugin-api'; -import { instanceMetadataServiceRef } from '@backstage/backend-plugin-api/alpha'; -// Example usage of the instance metadata service to log the installed features. +// Example usage of the instance metadata service to log the installed plugins. export default createBackendPlugin({ pluginId: 'instance-metadata-logging', register(env) { env.registerInit({ deps: { - instanceMetadata: instanceMetadataServiceRef, + instanceMetadata: coreServices.rootInstanceMetadata, logger: coreServices.logger, }, async init({ instanceMetadata, logger }) { + const plugins = await instanceMetadata.getInstalledPlugins(); logger.info( - `Installed features on this instance: ${instanceMetadata - .getInstalledFeatures() + `Installed plugins on this instance: ${plugins + .map(e => e.pluginId) .join(', ')}`, ); }, diff --git a/packages/catalog-client/CHANGELOG.md b/packages/catalog-client/CHANGELOG.md index a7d254702d..5eb97934f9 100644 --- a/packages/catalog-client/CHANGELOG.md +++ b/packages/catalog-client/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/catalog-client +## 1.12.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/errors@1.2.7 + ## 1.12.0 ### Minor Changes diff --git a/packages/catalog-client/package.json b/packages/catalog-client/package.json index d3c13d9e6f..e5c63b1e0d 100644 --- a/packages/catalog-client/package.json +++ b/packages/catalog-client/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/catalog-client", - "version": "1.12.0", + "version": "1.12.1-next.0", "description": "An isomorphic client for the catalog backend", "backstage": { "role": "common-library" diff --git a/packages/catalog-model/CHANGELOG.md b/packages/catalog-model/CHANGELOG.md index 7d1b62ae0a..9d42e9d675 100644 --- a/packages/catalog-model/CHANGELOG.md +++ b/packages/catalog-model/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/catalog-model +## 1.7.6-next.0 + +### Patch Changes + +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + ## 1.7.5 ### Patch Changes diff --git a/packages/catalog-model/package.json b/packages/catalog-model/package.json index 5f37b5d57b..93a13b7edf 100644 --- a/packages/catalog-model/package.json +++ b/packages/catalog-model/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/catalog-model", - "version": "1.7.5", + "version": "1.7.6-next.0", "description": "Types and validators that help describe the model of a Backstage Catalog", "backstage": { "role": "common-library" diff --git a/packages/catalog-model/src/EntityPolicies.ts b/packages/catalog-model/src/EntityPolicies.ts index e5aaeb0c20..feadee906c 100644 --- a/packages/catalog-model/src/EntityPolicies.ts +++ b/packages/catalog-model/src/EntityPolicies.ts @@ -19,7 +19,11 @@ import { Entity, EntityPolicy } from './entity'; // Helper that requires that all of a set of policies can be successfully // applied class AllEntityPolicies implements EntityPolicy { - constructor(private readonly policies: EntityPolicy[]) {} + private readonly policies: EntityPolicy[]; + + constructor(policies: EntityPolicy[]) { + this.policies = policies; + } async enforce(entity: Entity): Promise { let result = entity; @@ -39,7 +43,11 @@ class AllEntityPolicies implements EntityPolicy { // Helper that requires that at least one of a set of policies can be // successfully applied class AnyEntityPolicy implements EntityPolicy { - constructor(private readonly policies: EntityPolicy[]) {} + private readonly policies: EntityPolicy[]; + + constructor(policies: EntityPolicy[]) { + this.policies = policies; + } async enforce(entity: Entity): Promise { for (const policy of this.policies) { diff --git a/packages/cli-node/CHANGELOG.md b/packages/cli-node/CHANGELOG.md index d684791e38..7921933482 100644 --- a/packages/cli-node/CHANGELOG.md +++ b/packages/cli-node/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/cli-node +## 0.2.15-next.0 + +### Patch Changes + +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/cli-common@0.1.15 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + ## 0.2.14 ### Patch Changes diff --git a/packages/cli-node/package.json b/packages/cli-node/package.json index ed266b031f..abd7c959a7 100644 --- a/packages/cli-node/package.json +++ b/packages/cli-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/cli-node", - "version": "0.2.14", + "version": "0.2.15-next.0", "description": "Node.js library for Backstage CLIs", "backstage": { "role": "node-library" diff --git a/packages/cli-node/src/monorepo/Lockfile.ts b/packages/cli-node/src/monorepo/Lockfile.ts index d7a6f5f0b6..ff624191af 100644 --- a/packages/cli-node/src/monorepo/Lockfile.ts +++ b/packages/cli-node/src/monorepo/Lockfile.ts @@ -133,10 +133,16 @@ export class Lockfile { return new Lockfile(packages, data); } + private readonly packages: Map; + private readonly data: LockfileData; + private constructor( - private readonly packages: Map, - private readonly data: LockfileData, - ) {} + packages: Map, + data: LockfileData, + ) { + this.packages = packages; + this.data = data; + } /** Returns the name of all packages available in the lockfile */ get(name: string): LockfileQueryEntry[] | undefined { diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index 28b2293982..8f0c1a8a21 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,5 +1,116 @@ # @backstage/cli +## 0.34.5-next.1 + +### Patch Changes + +- da19cb5: Fix inconsistent behavior in the `new` command for the `@internal` scope: it now consistently defaults to the `backstage-plugin-` infix whether the `--scope` option is not set or it's set to `internal`. +- b2bef92: Convert all enums to erasable-syntax compliant patterns + +## 0.34.5-next.0 + +### Patch Changes + +- fc7cbfc: The templates executed with the `yarn new` command now supports templating filenames. +- fc7cbfc: Added a template for the `yarn new` command to create an catalog entity provider. To add this template to an explicit list in the root `package.json`, use `@backstage/cli/templates/catalog-provider-module`. +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/eslint-plugin@0.2.0-next.0 + - @backstage/config-loader@1.10.6-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/cli-node@0.2.15-next.0 + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/integration@1.18.2-next.0 + - @backstage/cli-common@0.1.15 + - @backstage/errors@1.2.7 + - @backstage/release-manifests@0.0.13 + - @backstage/types@1.2.2 + +## 0.34.4 + +### Patch Changes + +- f2cf564: Removed the script transform cache from the default Jest configuration. The script cache provided a moderate performance boost, but it is incompatible with Jest 30. +- 33faad2: Allow using custom manifest location in the yarn plugin and version bump. + + The Backstage yarn plugin and version bump allows two new environment variables to configure custom manifest location: + + - `BACKSTAGE_VERSIONS_BASE_URL`: The base URL for fetching the Backstage version + manifest. Defaults to `https://versions.backstage.io/v1/releases/VERSION/manifest.json`. + Useful for running the plugin in environment without direct access to the internet, + for example by using a mirror of the versions API or a proxy. + Note that the environment variable is just the host name, and the path is appended by + the plugin. If you are using the yarn plugin, bump version command will also try + to fetch the new version of the yarn plugin from the same base URL (defaults to + `https://versions.backstage.io/v1/releases/RELEASE/yarn-plugin`) + - `BACKSTAGE_MANIFEST_FILE`: Path to a local manifest file. If set, the plugin + will not attempt to fetch the manifest from the network. Useful for running + the plugin in environment without internet access and without mirror of the + versions API. + +- 6ebc1ea: Fixed module federation config by only setting `import: false` on shared libraries for remote. +- ab96bb7: Added a new `--entrypoint` option to the `package start` command, which allows you to specify a custom entry directory/file for development applications. This is particularly useful when maintaining separate dev apps for different versions of your plugin (e.g., stable and alpha). + + **Example usage:** + + Consider the following plugin dev folder structure: + + ``` + dev/ + index.tsx + alpha/ + index.ts + ``` + + - The default `yarn package start` command uses the `dev/` folder as the entry point and executes `dev/index.tsx` file; + - Running `yarn package start --entrypoint dev/alpha` will instead use `dev/alpha/` as the entry point and execute `dev/alpha/index.ts` file. + +- 024645e: Remove unused @octokit modules from cli package + + - @octokit/graphql + - @octokit/graphql-schema + - @octokit/oauth-app + +- d14ef24: Added automatic detection and support for the Backstage Yarn plugin when generating new packages with `yarn new`. When the plugin is installed, new packages will automatically use `backstage:^` ranges for `@backstage/*` dependencies. +- Updated dependencies + - @backstage/eslint-plugin@0.1.12 + - @backstage/integration@1.18.1 + - @backstage/config-loader@1.10.5 + - @backstage/config@1.3.5 + +## 0.34.4-next.3 + +### Patch Changes + +- f2cf564: Removed the script transform cache from the default Jest configuration. The script cache provided a moderate performance boost, but it is incompatible with Jest 30. +- 024645e: Remove unused @octokit modules from cli package + + - @octokit/graphql + - @octokit/graphql-schema + - @octokit/oauth-app + +- d14ef24: Added automatic detection and support for the Backstage Yarn plugin when generating new packages with `yarn new`. When the plugin is installed, new packages will automatically use `backstage:^` ranges for `@backstage/*` dependencies. + +## 0.34.4-next.2 + +### Patch Changes + +- ab96bb7: Added a new `--entrypoint` option to the `package start` command, which allows you to specify a custom entry directory/file for development applications. This is particularly useful when maintaining separate dev apps for different versions of your plugin (e.g., stable and alpha). + + **Example usage:** + + Consider the following plugin dev folder structure: + + ``` + dev/ + index.tsx + alpha/ + index.ts + ``` + + - The default `yarn package start` command uses the `dev/` folder as the entry point and executes `dev/index.tsx` file; + - Running `yarn package start --entrypoint dev/alpha` will instead use `dev/alpha/` as the entry point and execute `dev/alpha/index.ts` file. + ## 0.34.4-next.1 ### Patch Changes diff --git a/packages/cli/cli-report.md b/packages/cli/cli-report.md index e2f5bb9cc6..a8df5242ad 100644 --- a/packages/cli/cli-report.md +++ b/packages/cli/cli-report.md @@ -312,6 +312,7 @@ Usage: program [options] Options: --check --config + --entrypoint --inspect [host] --inspect-brk [host] --link diff --git a/packages/cli/config/jestCachingModuleLoader.js b/packages/cli/config/jestCachingModuleLoader.js index b2b6a4f8ae..a3fdf106e9 100644 --- a/packages/cli/config/jestCachingModuleLoader.js +++ b/packages/cli/config/jestCachingModuleLoader.js @@ -16,29 +16,12 @@ const { default: JestRuntime } = require('jest-runtime'); -const scriptTransformCache = new Map(); - module.exports = class CachingJestRuntime extends JestRuntime { constructor(config, ...restArgs) { super(config, ...restArgs); this.allowLoadAsEsm = config.extensionsToTreatAsEsm.includes('.mts'); } - // This may or may not be a good idea. Theoretically I don't know why this would impact - // test correctness and flakiness, but it seems like it may introduce flakiness and strange failures. - // It does seem to speed up test execution by a fair amount though. - createScriptFromCode(scriptSource, filename) { - let script = scriptTransformCache.get(scriptSource); - if (!script) { - script = super.createScriptFromCode(scriptSource, filename); - // Tried to store the script object in a WeakRef here. It starts out at - // about 90% hit rate, but eventually drops all the way to 20%, and overall - // it seemed to increase memory usage by 20% or so. - scriptTransformCache.set(scriptSource, script); - } - return script; - } - // Unfortunately we need to use this unstable API to make sure that .js files // are only loaded as modules where ESM is supported, i.e. Node.js packages. unstable_shouldLoadAsEsm(path, ...restArgs) { diff --git a/packages/cli/package.json b/packages/cli/package.json index 3c12b383ad..bdd8a120c3 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/cli", - "version": "0.34.4-next.1", + "version": "0.34.5-next.1", "description": "CLI for developing Backstage plugins and apps", "backstage": { "role": "cli" @@ -59,9 +59,6 @@ "@backstage/types": "workspace:^", "@manypkg/get-packages": "^1.1.3", "@module-federation/enhanced": "^0.9.0", - "@octokit/graphql": "^5.0.0", - "@octokit/graphql-schema": "^13.7.0", - "@octokit/oauth-app": "^4.2.0", "@octokit/request": "^8.0.0", "@rollup/plugin-commonjs": "^26.0.0", "@rollup/plugin-json": "^6.0.0", diff --git a/packages/cli/src/lib/version.test.ts b/packages/cli/src/lib/version.test.ts index 744874d7e9..d582c397c3 100644 --- a/packages/cli/src/lib/version.test.ts +++ b/packages/cli/src/lib/version.test.ts @@ -79,4 +79,110 @@ describe('createPackageVersionProvider', () => { `^${corePluginApiPkg.version}`, ); }); + + describe('with backstage protocol options', () => { + it('should return backstage:^ for @backstage packages when preferBackstageProtocol is true', async () => { + mockDir.setContent({ + 'yarn.lock': `${HEADER} +"@backstage/core-plugin-api@^1.0.0": + version "1.0.0" +`, + }); + + const lockfilePath = mockDir.resolve('yarn.lock'); + const lockfile = await Lockfile.load(lockfilePath); + const provider = createPackageVersionProvider(lockfile, { + preferBackstageProtocol: true, + }); + + expect(provider('@backstage/core-plugin-api')).toBe('backstage:^'); + expect(provider('@backstage/cli')).toBe('backstage:^'); + }); + + it('should not return backstage:^ for non-@backstage packages even when preferBackstageProtocol is true', async () => { + mockDir.setContent({ + 'yarn.lock': `${HEADER} +"react@^18.0.0": + version "18.0.0" + +"@backstage/core-plugin-api@^1.0.0": + version "1.0.0" + +"@internal/library@workspace:packages/internal": + version "0.0.0-use.local" +`, + }); + + const lockfilePath = mockDir.resolve('yarn.lock'); + const lockfile = await Lockfile.load(lockfilePath); + const provider = createPackageVersionProvider(lockfile, { + preferBackstageProtocol: true, + }); + + expect(provider('react', '18.0.0')).toBe('^18.0.0'); + expect(provider('@backstage/core-plugin-api')).toBe('backstage:^'); + expect(provider('@internal/library')).toBe('workspace:^'); + }); + + it('should prefer workspace ranges over backstage protocol', async () => { + mockDir.setContent({ + 'yarn.lock': `${HEADER} +"react@workspace:packages/internal": + version "0.0.0-use.local" + +"@backstage/core-plugin-api@workspace:packages/internal": + version "0.0.0-use.local" + +"@internal/library@workspace:packages/internal": + version "0.0.0-use.local" +`, + }); + + const lockfilePath = mockDir.resolve('yarn.lock'); + const lockfile = await Lockfile.load(lockfilePath); + const provider = createPackageVersionProvider(lockfile, { + preferBackstageProtocol: true, + }); + + expect(provider('react')).toBe('workspace:^'); + expect(provider('@backstage/core-plugin-api')).toBe('workspace:^'); + expect(provider('@internal/library')).toBe('workspace:^'); + }); + + // skipping this as it's broken in VP right now, and need a release. + // eslint-disable-next-line jest/no-disabled-tests + it.skip('should not use backstage protocol when preferBackstageProtocol is false', async () => { + mockDir.setContent({ + 'yarn.lock': `${HEADER} +"@backstage/core-plugin-api@*": + version "1.0.0" +`, + }); + + const lockfilePath = mockDir.resolve('yarn.lock'); + const lockfile = await Lockfile.load(lockfilePath); + const provider = createPackageVersionProvider(lockfile, { + preferBackstageProtocol: false, + }); + + expect(provider('@backstage/core-plugin-api')).toBe('*'); + }); + + // skipping this as it's broken in VP right now, and need a release. + // eslint-disable-next-line jest/no-disabled-tests + it.skip('should not use backstage protocol when options are not provided', async () => { + mockDir.setContent({ + 'yarn.lock': `${HEADER} +"@backstage/core-plugin-api@*": + version "1.0.0" +`, + }); + + const lockfilePath = mockDir.resolve('yarn.lock'); + const lockfile = await Lockfile.load(lockfilePath); + const provider = createPackageVersionProvider(lockfile); + + expect(provider('@backstage/core-plugin-api')).toBe('*'); + }); + }); }); diff --git a/packages/cli/src/lib/version.ts b/packages/cli/src/lib/version.ts index 5a2a8c7838..a7a7ca1bdb 100644 --- a/packages/cli/src/lib/version.ts +++ b/packages/cli/src/lib/version.ts @@ -89,23 +89,35 @@ export function findVersion() { export const version = findVersion(); export const isDev = fs.pathExistsSync(paths.resolveOwn('src')); -export function createPackageVersionProvider(lockfile?: Lockfile) { +export function createPackageVersionProvider( + lockfile?: Lockfile, + options?: { + preferBackstageProtocol?: boolean; + }, +) { return (name: string, versionHint?: string): string => { const packageVersion = packageVersions[name]; + + // 1) workspace precedence (existing logic) - check this first + const lockfileEntries = lockfile?.get(name); + const lockfileEntry = lockfileEntries?.find(entry => + entry.range.startsWith('workspace:'), + ); + if (lockfileEntry) { + return 'workspace:^'; + } + + // 2) backstage:^ when plugin is present and allowed + if (options?.preferBackstageProtocol && name.startsWith('@backstage/')) { + return 'backstage:^'; + } + + // 3) fallback to current npm resolution const targetVersion = versionHint || packageVersion; if (!targetVersion) { throw new Error(`No version available for package ${name}`); } - const lockfileEntries = lockfile?.get(name); - - for (const specifier of ['^', '~', '*']) { - const range = `workspace:${specifier}`; - if (lockfileEntries?.some(entry => entry.range === range)) { - return range; - } - } - const validRanges = lockfileEntries?.filter(entry => semver.satisfies(targetVersion, entry.range), ); diff --git a/packages/cli/src/lib/versioning/Lockfile.ts b/packages/cli/src/lib/versioning/Lockfile.ts index 047cf5aefc..7a9a2c801a 100644 --- a/packages/cli/src/lib/versioning/Lockfile.ts +++ b/packages/cli/src/lib/versioning/Lockfile.ts @@ -106,11 +106,19 @@ export class Lockfile { return new Lockfile(packages, data, legacy); } + private readonly packages: Map; + private readonly data: LockfileData; + private readonly legacy: boolean; + private constructor( - private readonly packages: Map, - private readonly data: LockfileData, - private readonly legacy: boolean = false, - ) {} + packages: Map, + data: LockfileData, + legacy: boolean = false, + ) { + this.packages = packages; + this.data = data; + this.legacy = legacy; + } /** Get the entries for a single package in the lockfile */ get(name: string): LockfileQueryEntry[] | undefined { diff --git a/packages/cli/src/lib/yarnPlugin.test.ts b/packages/cli/src/lib/yarnPlugin.test.ts new file mode 100644 index 0000000000..d5c865e1c4 --- /dev/null +++ b/packages/cli/src/lib/yarnPlugin.test.ts @@ -0,0 +1,131 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { createMockDirectory } from '@backstage/backend-test-utils'; +import { getHasYarnPlugin } from './yarnPlugin'; + +const mockDir = createMockDirectory(); + +jest.mock('./paths', () => ({ + paths: { + resolveTargetRoot(filename: string) { + return mockDir.resolve(filename); + }, + }, +})); + +describe('getHasYarnPlugin', () => { + beforeEach(() => { + mockDir.clear(); + }); + + it('should return false when .yarnrc.yml does not exist', async () => { + mockDir.setContent({}); + + const result = await getHasYarnPlugin(); + expect(result).toBe(false); + }); + + it('should return false when .yarnrc.yml is empty', async () => { + mockDir.setContent({ + '.yarnrc.yml': '', + }); + + const result = await getHasYarnPlugin(); + expect(result).toBe(false); + }); + + it('should return false when plugins array is empty', async () => { + mockDir.setContent({ + '.yarnrc.yml': 'plugins: []', + }); + + const result = await getHasYarnPlugin(); + expect(result).toBe(false); + }); + + it('should return false when plugins array does not contain backstage plugin', async () => { + mockDir.setContent({ + '.yarnrc.yml': ` +plugins: + - path: .yarn/plugins/@yarnpkg/plugin-typescript.cjs + - path: .yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs +`, + }); + + const result = await getHasYarnPlugin(); + expect(result).toBe(false); + }); + + it('should return true when backstage plugin is present', async () => { + mockDir.setContent({ + '.yarnrc.yml': ` +plugins: + - path: .yarn/plugins/@yarnpkg/plugin-typescript.cjs + - path: .yarn/plugins/@yarnpkg/plugin-backstage.cjs + - path: .yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs +`, + }); + + const result = await getHasYarnPlugin(); + expect(result).toBe(true); + }); + + it('should return true when backstage plugin is the only plugin', async () => { + mockDir.setContent({ + '.yarnrc.yml': ` +plugins: + - path: .yarn/plugins/@yarnpkg/plugin-backstage.cjs +`, + }); + + const result = await getHasYarnPlugin(); + expect(result).toBe(true); + }); + + it('should throw error when .yarnrc.yml has invalid content', async () => { + mockDir.setContent({ + '.yarnrc.yml': 'invalid: yaml: content: [', + }); + + await expect(getHasYarnPlugin()).rejects.toThrow(); + }); + + it('should throw error when .yarnrc.yml has unexpected structure', async () => { + mockDir.setContent({ + '.yarnrc.yml': ` +plugins: "not an array" +`, + }); + + await expect(getHasYarnPlugin()).rejects.toThrow( + 'Unexpected content in .yarnrc.yml', + ); + }); + + it('should handle plugins with different structure', async () => { + mockDir.setContent({ + '.yarnrc.yml': ` +plugins: + - path: .yarn/plugins/@yarnpkg/plugin-backstage.cjs + - path: .yarn/plugins/@yarnpkg/plugin-typescript.cjs +`, + }); + + const result = await getHasYarnPlugin(); + expect(result).toBe(true); + }); +}); diff --git a/packages/cli/src/lib/yarnPlugin.ts b/packages/cli/src/lib/yarnPlugin.ts new file mode 100644 index 0000000000..5ad49b2524 --- /dev/null +++ b/packages/cli/src/lib/yarnPlugin.ts @@ -0,0 +1,66 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import fs from 'fs-extra'; +import yaml from 'yaml'; +import z from 'zod'; +import { paths } from './paths'; + +const yarnRcSchema = z.object({ + plugins: z + .array( + z.object({ + path: z.string(), + }), + ) + .optional(), +}); + +/** + * Detects whether the Backstage Yarn plugin is installed in the target repository. + * + * @returns Promise - true if the plugin is installed, false otherwise + */ +export async function getHasYarnPlugin(): Promise { + const yarnRcPath = paths.resolveTargetRoot('.yarnrc.yml'); + const yarnRcContent = await fs.readFile(yarnRcPath, 'utf-8').catch(e => { + if (e.code === 'ENOENT') { + // gracefully continue in case the file doesn't exist + return ''; + } + throw e; + }); + + if (!yarnRcContent) { + return false; + } + + const parseResult = yarnRcSchema.safeParse(yaml.parse(yarnRcContent)); + + if (!parseResult.success) { + throw new Error( + `Unexpected content in .yarnrc.yml: ${parseResult.error.toString()}`, + ); + } + + const yarnRc = parseResult.data; + + const backstagePlugin = yarnRc.plugins?.some( + plugin => plugin.path === '.yarn/plugins/@yarnpkg/plugin-backstage.cjs', + ); + + return Boolean(backstagePlugin); +} diff --git a/packages/cli/src/modules/build/commands/package/start/command.ts b/packages/cli/src/modules/build/commands/package/start/command.ts index 05fc668356..b38957f488 100644 --- a/packages/cli/src/modules/build/commands/package/start/command.ts +++ b/packages/cli/src/modules/build/commands/package/start/command.ts @@ -23,6 +23,7 @@ import { paths } from '../../../../../lib/paths'; export async function command(opts: OptionValues): Promise { await startPackage({ role: await findRoleFromCommand(opts), + entrypoint: opts.entrypoint, targetDir: paths.targetDir, configPaths: opts.config as string[], checksEnabled: Boolean(opts.check), diff --git a/packages/cli/src/modules/build/commands/package/start/startPackage.test.ts b/packages/cli/src/modules/build/commands/package/start/startPackage.test.ts new file mode 100644 index 0000000000..ca153adb22 --- /dev/null +++ b/packages/cli/src/modules/build/commands/package/start/startPackage.test.ts @@ -0,0 +1,89 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { createMockDirectory } from '@backstage/backend-test-utils'; +import { resolveEntryPath } from './startPackage'; + +describe('resolveEntryPath', () => { + const mockDir = createMockDirectory(); + + afterEach(() => { + mockDir.clear(); + }); + + it('should remove file extensions', () => { + mockDir.setContent({ + 'dev/custom.tsx': '// dev app code', + }); + + const result = resolveEntryPath('dev/custom.tsx', mockDir.path); + + expect(result).toBe('dev/custom'); + }); + + it('should remove trailing slashes', () => { + mockDir.setContent({ + 'dev/alpha.ts': '// dev app code', + }); + + const result = resolveEntryPath('dev/alpha/', mockDir.path); + + expect(result).toBe('dev/alpha'); + }); + + it('should handle multiple dots in filename', () => { + mockDir.setContent({ + 'index.alpha.ts': 'export const data = {};', + }); + + const result = resolveEntryPath('index.alpha.ts', mockDir.path); + + expect(result).toBe('index.alpha'); + }); + + it('should handle simple directory names', () => { + mockDir.setContent({ + 'dev/index.ts': '// dev app code', + }); + + const result = resolveEntryPath('dev', mockDir.path); + + expect(result).toBe('dev/index'); + }); + + it('should handle nested directory paths', () => { + mockDir.setContent({ + 'dev/alpha/index.ts': '// dev app code', + }); + + const result = resolveEntryPath('dev/alpha', mockDir.path); + + expect(result).toBe('dev/alpha/index'); + }); + + it('should return the file when there is a directory with the same name', () => { + mockDir.setContent({ + 'dev/alpha.ts': '// dev app code', + 'dev/app-config.yaml': '// dev app config', + 'dev/alpha/index.ts': '// dev app code', + 'dev/alpha/app-config.yaml': '// dev app config', + }); + + const result = resolveEntryPath('dev/alpha', mockDir.path); + + expect(result).toBe('dev/alpha'); + }); +}); diff --git a/packages/cli/src/modules/build/commands/package/start/startPackage.ts b/packages/cli/src/modules/build/commands/package/start/startPackage.ts index 10f705ba4d..b49fd482e5 100644 --- a/packages/cli/src/modules/build/commands/package/start/startPackage.ts +++ b/packages/cli/src/modules/build/commands/package/start/startPackage.ts @@ -17,9 +17,24 @@ import { PackageRole } from '@backstage/cli-node'; import { startBackend, startBackendPlugin } from './startBackend'; import { startFrontend } from './startFrontend'; +import { parse, resolve, join } from 'path'; +import { glob } from 'glob'; + +export function resolveEntryPath( + entrypoint: string = 'dev', + targetDir: string, +): string { + const { dir: entryDir, name: entryName } = parse(entrypoint); + const [entryFile] = glob.sync(`${resolve(targetDir, entryDir, entryName)}.*`); + if (entryFile) { + return join(entryDir, entryName); + } + return join(entryDir, entryName, 'index'); +} export async function startPackage(options: { role: PackageRole; + entrypoint?: string; targetDir: string; configPaths: string[]; checksEnabled: boolean; @@ -45,8 +60,8 @@ export async function startPackage(options: { case 'frontend-plugin': case 'frontend-plugin-module': return startFrontend({ - entry: 'dev/index', ...options, + entry: resolveEntryPath(options.entrypoint, options.targetDir), }); case 'frontend-dynamic-container' as PackageRole: // experimental return startFrontend({ diff --git a/packages/cli/src/modules/build/index.ts b/packages/cli/src/modules/build/index.ts index fd31a6c782..5382adfec9 100644 --- a/packages/cli/src/modules/build/index.ts +++ b/packages/cli/src/modules/build/index.ts @@ -137,6 +137,10 @@ export const buildPlugin = createCliPlugin({ '--link ', 'Link an external workspace for module resolution', ) + .option( + '--entrypoint ', + 'The entrypoint to start from, relative to the package root. Can point to either a file (without extension) or a directory (in which case the index file in that directory is used). Defaults to "dev"', + ) .action(lazy(() => import('./commands/package/start'), 'command')); await defaultCommand.parseAsync(args, { from: 'user' }); diff --git a/packages/cli/src/modules/build/lib/builder/types.ts b/packages/cli/src/modules/build/lib/builder/types.ts index 937bd157c9..f80d0d1306 100644 --- a/packages/cli/src/modules/build/lib/builder/types.ts +++ b/packages/cli/src/modules/build/lib/builder/types.ts @@ -13,13 +13,29 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +/* We want to maintain the same information as an enum, so we disable the redeclaration warning */ +/* eslint-disable @typescript-eslint/no-redeclare */ import { BackstagePackage, BackstagePackageJson } from '@backstage/cli-node'; -export enum Output { - esm, - cjs, - types, +export const Output = { + esm: 0, + cjs: 1, + types: 2, +} as const; + +/** + * @public + */ +export type Output = (typeof Output)[keyof typeof Output]; + +/** + * @public + */ +export namespace Output { + export type esm = typeof Output.esm; + export type cjs = typeof Output.cjs; + export type types = typeof Output.types; } export type BuildOptions = { diff --git a/packages/cli/src/modules/create-github-app/commands/create-github-app/GithubCreateAppServer.ts b/packages/cli/src/modules/create-github-app/commands/create-github-app/GithubCreateAppServer.ts index b1825ad996..9bfe14174d 100644 --- a/packages/cli/src/modules/create-github-app/commands/create-github-app/GithubCreateAppServer.ts +++ b/packages/cli/src/modules/create-github-app/commands/create-github-app/GithubCreateAppServer.ts @@ -58,10 +58,12 @@ export class GithubCreateAppServer { return server.start(); } - private constructor( - private readonly actionUrl: string, - private readonly permissions: string[], - ) { + private readonly actionUrl: string; + private readonly permissions: string[]; + + private constructor(actionUrl: string, permissions: string[]) { + this.actionUrl = actionUrl; + this.permissions = permissions; const webhookId = crypto .randomBytes(15) .toString('base64') diff --git a/packages/cli/src/modules/migrate/commands/versions/bump.test.ts b/packages/cli/src/modules/migrate/commands/versions/bump.test.ts index 1fb28d0289..738610d09c 100644 --- a/packages/cli/src/modules/migrate/commands/versions/bump.test.ts +++ b/packages/cli/src/modules/migrate/commands/versions/bump.test.ts @@ -1,5 +1,5 @@ /* - * Copyright 2020 The Backstage Authors + * Copyright 2025 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - import fs from 'fs-extra'; import { Command } from 'commander'; import * as runObj from '../../../../lib/run'; @@ -24,8 +23,8 @@ import { setupServer } from 'msw/node'; import { rest } from 'msw'; import { NotFoundError } from '@backstage/errors'; import { - MockDirectory, createMockDirectory, + MockDirectory, } from '@backstage/backend-test-utils'; // Avoid mutating the global agents used in other tests @@ -1056,3 +1055,310 @@ describe('createVersionFinder', () => { ); }); }); + +describe('environment variables', () => { + const worker = setupServer(); + registerMswTestHooks(worker); + + beforeEach(() => { + delete process.env.BACKSTAGE_MANIFEST_FILE; + process.env.BACKSTAGE_VERSIONS_BASE_URL = 'https://custom.example.com'; + }); + + afterEach(() => { + jest.resetAllMocks(); + }); + + afterAll(() => { + delete process.env.BACKSTAGE_MANIFEST_FILE; + delete process.env.BACKSTAGE_VERSIONS_BASE_URL; + }); + + it('should use custom base URL when BACKSTAGE_VERSIONS_BASE_URL is set', async () => { + mockDir.setContent({ + 'yarn.lock': lockfileMock, + 'package.json': JSON.stringify({ + workspaces: { + packages: ['packages/*'], + }, + }), + packages: { + a: { + 'package.json': JSON.stringify({ + name: 'a', + dependencies: { + '@backstage/core': '^1.0.5', + }, + }), + }, + }, + }); + + jest.spyOn(runObj, 'run').mockResolvedValue(undefined); + worker.use( + rest.get( + 'https://custom.example.com/v1/tags/main/manifest.json', + (_, res, ctx) => + res( + ctx.status(200), + ctx.json({ + releaseVersion: '1.5.0', + packages: [ + { + name: '@backstage/core', + version: '1.5.0', + }, + ], + }), + ), + ), + ); + + const { log: logs } = await withLogCollector(['log', 'warn'], async () => { + await bump({ pattern: null, release: 'main' } as unknown as Command); + }); + + expectLogsToMatch(logs, [ + 'Using default pattern glob @backstage/*', + 'Checking for updates of @backstage/core', + 'Some packages are outdated, updating', + 'bumping @backstage/core in a to ^1.5.0', + 'Your project is now at version 1.5.0, which has been written to backstage.json', + 'Running yarn install to install new versions', + 'Checking for moved packages to the @backstage-community namespace...', + 'Version bump complete!', + ]); + + expect(runObj.run).toHaveBeenCalledTimes(1); + expect(runObj.run).toHaveBeenCalledWith( + 'yarn', + ['install'], + expect.any(Object), + ); + + const packageA = await fs.readJson( + mockDir.resolve('packages/a/package.json'), + ); + expect(packageA).toEqual({ + name: 'a', + dependencies: { + '@backstage/core': '^1.5.0', + }, + }); + }); + + it('should use custom manifest file when BACKSTAGE_MANIFEST_FILE is set', async () => { + const manifestPath = mockDir.resolve('custom-manifest.json'); + process.env.BACKSTAGE_MANIFEST_FILE = manifestPath; + + const customManifest = { + releaseVersion: '2.0.0', + packages: [ + { + name: '@backstage/core', + version: '2.0.0', + }, + { + name: '@backstage/theme', + version: '3.0.0', + }, + ], + }; + + mockDir.setContent({ + 'custom-manifest.json': JSON.stringify(customManifest), + 'yarn.lock': lockfileMock, + 'package.json': JSON.stringify({ + workspaces: { + packages: ['packages/*'], + }, + }), + packages: { + a: { + 'package.json': JSON.stringify({ + name: 'a', + dependencies: { + '@backstage/core': '^1.0.5', + '@backstage/theme': '^1.0.0', + }, + }), + }, + }, + }); + + jest.spyOn(runObj, 'run').mockResolvedValue(undefined); + + const { log: logs } = await withLogCollector(['log', 'warn'], async () => { + await bump({ pattern: null, release: 'main' } as unknown as Command); + }); + + expectLogsToMatch(logs, [ + 'Using default pattern glob @backstage/*', + 'Checking for updates of @backstage/core', + 'Checking for updates of @backstage/theme', + 'Some packages are outdated, updating', + 'bumping @backstage/core in a to ^2.0.0', + 'bumping @backstage/theme in a to ^3.0.0', + 'Your project is now at version 2.0.0, which has been written to backstage.json', + 'Running yarn install to install new versions', + 'Checking for moved packages to the @backstage-community namespace...', + '⚠️ The following packages may have breaking changes:', + ' @backstage/core : 1.0.6 ~> 2.0.0', + ' https://github.com/backstage/backstage/blob/master/packages/core/CHANGELOG.md', + ' @backstage/theme : 1.0.0 ~> 3.0.0', + ' https://github.com/backstage/backstage/blob/master/packages/theme/CHANGELOG.md', + 'Version bump complete!', + ]); + + // Should not make any HTTP requests since using local manifest + expect(mockFetchPackageInfo).not.toHaveBeenCalled(); + + expect(runObj.run).toHaveBeenCalledTimes(1); + expect(runObj.run).toHaveBeenCalledWith( + 'yarn', + ['install'], + expect.any(Object), + ); + + const packageA = await fs.readJson( + mockDir.resolve('packages/a/package.json'), + ); + expect(packageA).toEqual({ + name: 'a', + dependencies: { + '@backstage/core': '^2.0.0', + '@backstage/theme': '^3.0.0', + }, + }); + }); + + it('should use custom base URL for yarn plugin when BACKSTAGE_VERSIONS_BASE_URL is set with yarn plugin', async () => { + mockDir.setContent({ + '.yarnrc.yml': yarnRcMock, + 'yarn.lock': lockfileMock, + 'package.json': JSON.stringify({ + workspaces: { + packages: ['packages/*'], + }, + }), + packages: { + a: { + 'package.json': JSON.stringify({ + name: 'a', + dependencies: { + '@backstage/core': '^1.0.5', + }, + }), + }, + }, + }); + + jest.spyOn(runObj, 'run').mockResolvedValue(undefined); + worker.use( + rest.get( + 'https://custom.example.com/v1/tags/main/manifest.json', + (_, res, ctx) => + res( + ctx.status(200), + ctx.json({ + releaseVersion: '1.5.0', + packages: [ + { + name: '@backstage/core', + version: '1.5.0', + }, + ], + }), + ), + ), + ); + + const { log: logs } = await withLogCollector(['log', 'warn'], async () => { + await bump({ pattern: null, release: 'main' } as unknown as Command); + }); + + expectLogsToMatch(logs, [ + 'Using default pattern glob @backstage/*', + 'Checking for updates of @backstage/core', + 'NOTE: this bump used backstage:^ versions in package.json files, since the Backstage yarn plugin was detected in the repository. To migrate back to explicit npm versions, remove the plugin by running "yarn plugin remove @yarnpkg/plugin-backstage", then repeat this command.', + 'Some packages are outdated, updating', + 'bumping @backstage/core in a to ^1.5.0', + 'Updating yarn plugin to v1.5.0...', + 'Your project is now at version 1.5.0, which has been written to backstage.json', + 'Running yarn install to install new versions', + 'Checking for moved packages to the @backstage-community namespace...', + 'Version bump complete!', + ]); + + expect(runObj.run).toHaveBeenCalledTimes(2); + expect(runObj.run).toHaveBeenCalledWith('yarn', [ + 'plugin', + 'import', + 'https://custom.example.com/v1/releases/1.5.0/yarn-plugin', + ]); + expect(runObj.run).toHaveBeenCalledWith( + 'yarn', + ['install'], + expect.any(Object), + ); + }); + + it('should handle missing manifest file when BACKSTAGE_MANIFEST_FILE is set', async () => { + process.env.BACKSTAGE_MANIFEST_FILE = '/nonexistent/manifest.json'; + + mockDir.setContent({ + 'yarn.lock': lockfileMock, + 'package.json': JSON.stringify({ + workspaces: { + packages: ['packages/*'], + }, + }), + packages: { + a: { + 'package.json': JSON.stringify({ + name: 'a', + dependencies: { + '@backstage/core': '^1.0.5', + }, + }), + }, + }, + }); + + await expect( + bump({ pattern: null, release: 'main' } as unknown as Command), + ).rejects.toThrow(); + }); + + it('should handle network errors when using custom base URL', async () => { + mockDir.setContent({ + 'yarn.lock': lockfileMock, + 'package.json': JSON.stringify({ + workspaces: { + packages: ['packages/*'], + }, + }), + packages: { + a: { + 'package.json': JSON.stringify({ + name: 'a', + dependencies: { + '@backstage/core': '^1.0.5', + }, + }), + }, + }, + }); + + worker.use( + rest.get( + 'https://custom.example.com/v1/tags/main/manifest.json', + (_, res, ctx) => res(ctx.status(500), ctx.json({})), + ), + ); + + await expect( + bump({ pattern: null, release: 'main' } as unknown as Command), + ).rejects.toThrow(); + }); +}); diff --git a/packages/cli/src/modules/migrate/commands/versions/bump.ts b/packages/cli/src/modules/migrate/commands/versions/bump.ts index 391ccf8f22..0d5e2f2f35 100644 --- a/packages/cli/src/modules/migrate/commands/versions/bump.ts +++ b/packages/cli/src/modules/migrate/commands/versions/bump.ts @@ -16,20 +16,20 @@ maybeBootstrapProxy(); +import { env } from 'process'; import fs from 'fs-extra'; import chalk from 'chalk'; import { minimatch } from 'minimatch'; import semver from 'semver'; import { OptionValues } from 'commander'; -import yaml from 'yaml'; -import z from 'zod'; import { isError, NotFoundError } from '@backstage/errors'; import { resolve as resolvePath } from 'path'; import { paths } from '../../../../lib/paths'; +import { getHasYarnPlugin } from '../../../../lib/yarnPlugin'; import { - mapDependencies, fetchPackageInfo, Lockfile, + mapDependencies, YarnInfoInspectData, } from '../../../../lib/versioning'; import { BACKSTAGE_JSON } from '@backstage/cli-common'; @@ -105,8 +105,14 @@ export default async (opts: OptionValues) => { let findTargetVersion: (name: string) => Promise; let releaseManifest: ReleaseManifest; - // Specific release specified. Be strict when resolving versions - if (semver.valid(opts.release)) { + if (env.BACKSTAGE_MANIFEST_FILE) { + // Use specific manifest file if provided + releaseManifest = await fs.readJson(env.BACKSTAGE_MANIFEST_FILE); + findTargetVersion = createStrictVersionFinder({ + releaseManifest, + }); + } else if (semver.valid(opts.release)) { + // Specific release specified. Be strict when resolving versions releaseManifest = await getManifestByVersion({ version: opts.release }); findTargetVersion = createStrictVersionFinder({ releaseManifest, @@ -116,9 +122,11 @@ export default async (opts: OptionValues) => { if (opts.release === 'next') { const next = await getManifestByReleaseLine({ releaseLine: 'next', + versionsBaseUrl: env.BACKSTAGE_VERSIONS_BASE_URL, }); const main = await getManifestByReleaseLine({ releaseLine: 'main', + versionsBaseUrl: env.BACKSTAGE_VERSIONS_BASE_URL, }); // Prefer manifest with the latest release version releaseManifest = semver.gt(next.releaseVersion, main.releaseVersion) @@ -127,6 +135,7 @@ export default async (opts: OptionValues) => { } else { releaseManifest = await getManifestByReleaseLine({ releaseLine: opts.release, + versionsBaseUrl: env.BACKSTAGE_VERSIONS_BASE_URL, }); } findTargetVersion = createVersionFinder({ @@ -141,11 +150,12 @@ export default async (opts: OptionValues) => { `Updating yarn plugin to v${releaseManifest.releaseVersion}...`, ); console.log(); - await run('yarn', [ - 'plugin', - 'import', - `https://versions.backstage.io/v1/releases/${releaseManifest.releaseVersion}/yarn-plugin`, - ]); + + const yarnPluginUrl = env.BACKSTAGE_VERSIONS_BASE_URL + ? `${env.BACKSTAGE_VERSIONS_BASE_URL}/v1/releases/${releaseManifest.releaseVersion}/yarn-plugin` + : `https://versions.backstage.io/v1/releases/${releaseManifest.releaseVersion}/yarn-plugin`; + + await run('yarn', ['plugin', 'import', yarnPluginUrl]); console.log(); } @@ -497,42 +507,3 @@ async function asLockfileVersion(version: string) { return version; } - -const yarnRcSchema = z.object({ - plugins: z - .array( - z.object({ - path: z.string(), - }), - ) - .optional(), -}); - -async function getHasYarnPlugin() { - const yarnRcPath = paths.resolveTargetRoot('.yarnrc.yml'); - const yarnRcContent = await fs.readFile(yarnRcPath, 'utf-8').catch(e => { - if (e.code === 'ENOENT') { - // gracefully continue in case the file doesn't exist - return ''; - } - throw e; - }); - - if (!yarnRcContent) { - return false; - } - - const parseResult = yarnRcSchema.safeParse(yaml.parse(yarnRcContent)); - - if (!parseResult.success) { - throw new Error( - `Unexpected content in .yarnrc.yml: ${parseResult.error.toString()}`, - ); - } - - const yarnRc = parseResult.data; - - return yarnRc.plugins?.some( - plugin => plugin.path === '.yarn/plugins/@yarnpkg/plugin-backstage.cjs', - ); -} diff --git a/packages/cli/src/modules/new/lib/defaultTemplates.ts b/packages/cli/src/modules/new/lib/defaultTemplates.ts index 1a2f7a89d4..9d1543c452 100644 --- a/packages/cli/src/modules/new/lib/defaultTemplates.ts +++ b/packages/cli/src/modules/new/lib/defaultTemplates.ts @@ -23,5 +23,6 @@ export const defaultTemplates = [ '@backstage/cli/templates/plugin-common-library', '@backstage/cli/templates/web-library', '@backstage/cli/templates/node-library', + '@backstage/cli/templates/catalog-provider-module', '@backstage/cli/templates/scaffolder-backend-module', ]; diff --git a/packages/cli/src/modules/new/lib/execution/PortableTemplater.ts b/packages/cli/src/modules/new/lib/execution/PortableTemplater.ts index 519f7c6dbc..9dfd60baba 100644 --- a/packages/cli/src/modules/new/lib/execution/PortableTemplater.ts +++ b/packages/cli/src/modules/new/lib/execution/PortableTemplater.ts @@ -27,6 +27,7 @@ import lowerFirst from 'lodash/lowerFirst'; import { Lockfile } from '../../../../lib/versioning'; import { paths } from '../../../../lib/paths'; import { createPackageVersionProvider } from '../../../../lib/version'; +import { getHasYarnPlugin } from '../../../../lib/yarnPlugin'; const builtInHelpers = { camelCase, @@ -53,7 +54,10 @@ export class PortableTemplater { /* ignored */ } - const versionProvider = createPackageVersionProvider(lockfile); + const hasYarnPlugin = await getHasYarnPlugin(); + const versionProvider = createPackageVersionProvider(lockfile, { + preferBackstageProtocol: hasYarnPlugin, + }); const templater = new PortableTemplater( { diff --git a/packages/cli/src/modules/new/lib/execution/writeTemplateContents.ts b/packages/cli/src/modules/new/lib/execution/writeTemplateContents.ts index 8b5d6855ca..11b77b3a53 100644 --- a/packages/cli/src/modules/new/lib/execution/writeTemplateContents.ts +++ b/packages/cli/src/modules/new/lib/execution/writeTemplateContents.ts @@ -22,6 +22,7 @@ import { PortableTemplate, PortableTemplateInput } from '../types'; import { ForwardedError, InputError } from '@backstage/errors'; import { isMonoRepo as getIsMonoRepo } from '@backstage/cli-node'; import { PortableTemplater } from './PortableTemplater'; +import { isChildPath } from '@backstage/cli-common'; export async function writeTemplateContents( template: PortableTemplate, @@ -63,7 +64,12 @@ export async function writeTemplateContents( } for (const file of template.files) { - const destPath = resolvePath(targetDir, file.path); + const destPath = resolvePath(targetDir, templater.template(file.path)); + if (!isChildPath(targetDir, destPath)) { + throw new Error( + `Path ${destPath} is outside of target directory ${targetDir}`, + ); + } await fs.ensureDir(dirname(destPath)); let content = diff --git a/packages/cli/src/modules/new/lib/preparation/loadPortableTemplateConfig.test.ts b/packages/cli/src/modules/new/lib/preparation/loadPortableTemplateConfig.test.ts index b8a7940bcf..f8167ef339 100644 --- a/packages/cli/src/modules/new/lib/preparation/loadPortableTemplateConfig.test.ts +++ b/packages/cli/src/modules/new/lib/preparation/loadPortableTemplateConfig.test.ts @@ -196,7 +196,7 @@ describe('loadPortableTemplateConfig', () => { private: true, version: '0.1.0', packageNamePrefix: '@internal/', - packageNamePluginInfix: 'plugin-', + packageNamePluginInfix: 'backstage-plugin-', }); }); @@ -316,7 +316,7 @@ describe('loadPortableTemplateConfig', () => { version: '0.1.0', private: true, packageNamePrefix: '@internal/', - packageNamePluginInfix: 'plugin-', + packageNamePluginInfix: 'backstage-plugin-', }); await expect( @@ -333,7 +333,7 @@ describe('loadPortableTemplateConfig', () => { version: '0.1.0', private: true, packageNamePrefix: '@internal/', - packageNamePluginInfix: 'plugin-', + packageNamePluginInfix: 'backstage-plugin-', }); }); }); diff --git a/packages/cli/src/modules/new/lib/preparation/loadPortableTemplateConfig.ts b/packages/cli/src/modules/new/lib/preparation/loadPortableTemplateConfig.ts index 5ef61bfb6b..2e736d9c29 100644 --- a/packages/cli/src/modules/new/lib/preparation/loadPortableTemplateConfig.ts +++ b/packages/cli/src/modules/new/lib/preparation/loadPortableTemplateConfig.ts @@ -70,6 +70,21 @@ type LoadConfigOptions = { overrides?: Partial; }; +function computePackageNamePluginInfix( + packageNamePrefix: string, + namePluginInfix?: string, +) { + const packageNamePluginInfix = + namePluginInfix ?? + (packageNamePrefix.includes('backstage') + ? defaults.packageNamePluginInfix + : 'backstage-plugin-'); + + return { + packageNamePluginInfix, + }; +} + export async function loadPortableTemplateConfig( options: LoadConfigOptions = {}, ): Promise { @@ -116,6 +131,16 @@ export async function loadPortableTemplateConfig( templateNameConflicts.set(pointer.name, rawPointer); } + const packageNamePrefix = + overrides.packageNamePrefix ?? + config?.globals?.namePrefix ?? + defaults.packageNamePrefix; + + const { packageNamePluginInfix } = computePackageNamePluginInfix( + packageNamePrefix, + overrides.packageNamePluginInfix ?? config?.globals?.namePluginInfix, + ); + return { isUsingDefaultTemplates: !config?.templates, templatePointers: templatePointerEntries.map(({ pointer }) => pointer), @@ -126,14 +151,8 @@ export async function loadPortableTemplateConfig( overrides.publishRegistry ?? config?.globals?.publishRegistry ?? defaults.publishRegistry, - packageNamePrefix: - overrides.packageNamePrefix ?? - config?.globals?.namePrefix ?? - defaults.packageNamePrefix, - packageNamePluginInfix: - overrides.packageNamePluginInfix ?? - config?.globals?.namePluginInfix ?? - defaults.packageNamePluginInfix, + packageNamePrefix, + packageNamePluginInfix, }; } diff --git a/packages/cli/templates/catalog-provider-module/.eslintrc.js.hbs b/packages/cli/templates/catalog-provider-module/.eslintrc.js.hbs new file mode 100644 index 0000000000..e2a53a6ad2 --- /dev/null +++ b/packages/cli/templates/catalog-provider-module/.eslintrc.js.hbs @@ -0,0 +1 @@ +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/packages/cli/templates/catalog-provider-module/README.md.hbs b/packages/cli/templates/catalog-provider-module/README.md.hbs new file mode 100644 index 0000000000..5ab3d8b18e --- /dev/null +++ b/packages/cli/templates/catalog-provider-module/README.md.hbs @@ -0,0 +1,5 @@ +# {{packageName}} + +The {{fullModuleId}} module for [@backstage/plugin-catalog-backend](https://www.npmjs.com/package/@backstage/plugin-catalog-backend). + +_This plugin was created through the Backstage CLI_ diff --git a/packages/cli/templates/catalog-provider-module/config.d.ts.hbs b/packages/cli/templates/catalog-provider-module/config.d.ts.hbs new file mode 100644 index 0000000000..8fe5911236 --- /dev/null +++ b/packages/cli/templates/catalog-provider-module/config.d.ts.hbs @@ -0,0 +1,34 @@ +import { SchedulerServiceTaskScheduleDefinitionConfig } from '@backstage/backend-plugin-api'; + +export interface Config { + catalog?: { + providers?: { + /** + * {{providerClass}} configuration. + */ + {{providerVar}}?: + | { + /** + * The target that this provider should consume. + */ + target: string; + /** + * Overrides the schedule at which this provider runs. + */ + schedule?: SchedulerServiceTaskScheduleDefinitionConfig; + } + | { + [name: string]: { + /** + * The target that this provider should consume. + */ + target: string; + /** + * Overrides the schedule at which this provider runs. + */ + schedule?: SchedulerServiceTaskScheduleDefinitionConfig; + }; + }; + }; + }; +} diff --git a/packages/cli/templates/catalog-provider-module/package.json.hbs b/packages/cli/templates/catalog-provider-module/package.json.hbs new file mode 100644 index 0000000000..cc27f4c8e5 --- /dev/null +++ b/packages/cli/templates/catalog-provider-module/package.json.hbs @@ -0,0 +1,36 @@ +{ + "name": "{{packageName}}", + "description": "The {{fullModuleId}} module for @backstage/plugin-catalog-backend", + "main": "src/index.ts", + "types": "src/index.ts", + "publishConfig": { + "access": "public", + "main": "dist/index.cjs.js", + "types": "dist/index.d.ts" + }, + "backstage": { + "role": "backend-plugin-module", + "pluginId": "catalog", + "pluginPackage": "@backstage/plugin-catalog-backend" + }, + "scripts": { + "start": "backstage-cli package start", + "build": "backstage-cli package build", + "lint": "backstage-cli package lint", + "test": "backstage-cli package test", + "clean": "backstage-cli package clean", + "prepack": "backstage-cli package prepack", + "postpack": "backstage-cli package postpack" + }, + "dependencies": { + "@backstage/backend-plugin-api": "{{versionQuery '@backstage/backend-plugin-api'}}", + "@backstage/plugin-catalog-node": "{{versionQuery '@backstage/plugin-catalog-node'}}" + }, + "devDependencies": { + "@backstage/cli": "{{versionQuery '@backstage/cli'}}", + "@backstage/backend-test-utils": "{{versionQuery '@backstage/backend-test-utils'}}" + }, + "files": [ + "dist" + ] +} diff --git a/packages/cli/templates/catalog-provider-module/portable-template.yaml b/packages/cli/templates/catalog-provider-module/portable-template.yaml new file mode 100644 index 0000000000..317a1e34a6 --- /dev/null +++ b/packages/cli/templates/catalog-provider-module/portable-template.yaml @@ -0,0 +1,9 @@ +name: catalog-provider-module +role: backend-plugin-module +description: An Entity Provider module for the Software Catalog +values: + pluginId: catalog + fullModuleId: '{{ moduleId }}-provider' + moduleVar: '{{ camelCase pluginId }}Module{{ upperFirst ( camelCase moduleId ) }}' + providerVar: '{{ camelCase moduleId }}Provider' + providerClass: '{{ upperFirst ( camelCase moduleId ) }}Provider' diff --git a/packages/cli/templates/catalog-provider-module/src/index.ts.hbs b/packages/cli/templates/catalog-provider-module/src/index.ts.hbs new file mode 100644 index 0000000000..6b0b8249d1 --- /dev/null +++ b/packages/cli/templates/catalog-provider-module/src/index.ts.hbs @@ -0,0 +1,8 @@ +/***/ +/** + * The {{fullModuleId}} module for @backstage/plugin-catalog-backend + * + * @packageDocumentation + */ + +export { {{moduleVar}} as default } from './module'; diff --git a/packages/cli/templates/catalog-provider-module/src/module.ts.hbs b/packages/cli/templates/catalog-provider-module/src/module.ts.hbs new file mode 100644 index 0000000000..d84e876b5d --- /dev/null +++ b/packages/cli/templates/catalog-provider-module/src/module.ts.hbs @@ -0,0 +1,29 @@ +import { + coreServices, + createBackendModule, +} from '@backstage/backend-plugin-api'; +import { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node/alpha'; +import { {{providerClass}} } from './provider/{{providerClass}}'; + +export const {{moduleVar}} = createBackendModule({ + moduleId: '{{fullModuleId}}', + pluginId: '{{pluginId}}', + register({ registerInit }) { + registerInit({ + deps: { + logger: coreServices.logger, + config: coreServices.rootConfig, + scheduler: coreServices.scheduler, + processing: catalogProcessingExtensionPoint, + }, + async init({ logger, scheduler, config, processing }) { + processing.addEntityProvider( + {{providerClass}}.fromConfig(config, { + logger, + scheduler, + }), + ); + } + }); + }, +}) diff --git a/packages/cli/templates/catalog-provider-module/src/provider/readProviderConfigs.ts.hbs b/packages/cli/templates/catalog-provider-module/src/provider/readProviderConfigs.ts.hbs new file mode 100644 index 0000000000..9e71f77ab5 --- /dev/null +++ b/packages/cli/templates/catalog-provider-module/src/provider/readProviderConfigs.ts.hbs @@ -0,0 +1,78 @@ +import { + readSchedulerServiceTaskScheduleDefinitionFromConfig, + SchedulerServiceTaskScheduleDefinition, +} from '@backstage/backend-plugin-api'; +import { Config } from '@backstage/config'; + +const DEFAULT_PROVIDER_ID = 'default'; +const DEFAULT_SCHEDULE: SchedulerServiceTaskScheduleDefinition = { + frequency: { + minutes: 30, + }, + timeout: { + minutes: 3, + }, +} + +export type {{providerClass}}ProviderConfig = { + id: string; + target: string; + schedule: SchedulerServiceTaskScheduleDefinition; +} + +/** + * Parses all configured providers. + * + * @param config - The root of the provider config hierarchy + * + * @public + */ +export function readProviderConfigs( + config: Config, +): {{providerClass}}ProviderConfig[] { + const providersConfig = config.getOptionalConfig( + 'catalog.providers.{{providerVar}}', + ); + if (!providersConfig) { + return []; + } + + if ((providersConfig).has('target')) { + // simple/single config variant + return [readProviderConfig(DEFAULT_PROVIDER_ID, providersConfig)]; + } + + return providersConfig.keys().map(id => { + const providerConfig = providersConfig.getConfig(id); + + return readProviderConfig(id, providerConfig); + }); +} + +/** + * Parses a single configured provider by id. + * + * @param id - the id of the provider to parse + * @param config - The root of the provider config hierarchy + * + * @public + */ +export function readProviderConfig( + id: string, + config: Config, +): {{providerClass}}ProviderConfig { + + const target = config.getString('target'); + + const schedule = config.has('schedule') + ? readSchedulerServiceTaskScheduleDefinitionFromConfig( + config.getConfig('schedule'), + ) + : DEFAULT_SCHEDULE; + + return { + id, + target, + schedule, + }; +} diff --git a/packages/cli/templates/catalog-provider-module/src/provider/{{providerClass}}.test.ts.hbs b/packages/cli/templates/catalog-provider-module/src/provider/{{providerClass}}.test.ts.hbs new file mode 100644 index 0000000000..65c135bd94 --- /dev/null +++ b/packages/cli/templates/catalog-provider-module/src/provider/{{providerClass}}.test.ts.hbs @@ -0,0 +1,18 @@ +import { {{providerClass}} } from './{{providerClass}}'; +import { mockServices } from '@backstage/backend-test-utils'; + +describe('{{providerClass}}', () => { + it('should read entities from the target', async () => { + const logger = mockServices.logger.mock(); + const provider = new {{providerClass}}({ + id: 'test', + target: 'https://example.com', + logger: mockServices.logger.mock(), + taskRunner: { run: jest.fn() }, + }); + + const entities = await provider.read({ logger }); + + expect(entities).toEqual([]); + }); +}) diff --git a/packages/cli/templates/catalog-provider-module/src/provider/{{providerClass}}.ts.hbs b/packages/cli/templates/catalog-provider-module/src/provider/{{providerClass}}.ts.hbs new file mode 100644 index 0000000000..3e4c5718ae --- /dev/null +++ b/packages/cli/templates/catalog-provider-module/src/provider/{{providerClass}}.ts.hbs @@ -0,0 +1,109 @@ +import { Config } from '@backstage/config'; +import { + DeferredEntity, + EntityProvider, + EntityProviderConnection, +} from '@backstage/plugin-catalog-node'; +import * as uuid from 'uuid'; +import { readProviderConfigs } from './readProviderConfigs'; +import { + LoggerService, + SchedulerService, + SchedulerServiceTaskRunner, +} from '@backstage/backend-plugin-api'; + +export type {{providerClass}}Options = { + /** + * The logger to use. + */ + logger: LoggerService; + + /** + * Scheduler used to schedule refreshes based on + * the schedule config. + */ + scheduler: SchedulerService; +}; + +export class {{providerClass}} implements EntityProvider { + static fromConfig( + configRoot: Config, + options: {{providerClass}}Options, + ): {{providerClass}}[] { + return readProviderConfigs(configRoot).map(providerConfig => { + return new {{providerClass}}({ + id: providerConfig.id, + target: providerConfig.target, + logger: options.logger, + taskRunner: options.scheduler.createScheduledTaskRunner( + providerConfig.schedule, + ), + }); + }); + } + + readonly #id: string; + readonly #target: string; + readonly #logger: LoggerService; + readonly #taskRunner: SchedulerServiceTaskRunner; + + constructor(options: { + id: string; + target: string; + logger: LoggerService; + taskRunner: SchedulerServiceTaskRunner; + }) { + this.#id = options.id; + this.#target = options.target; + this.#logger = options.logger; + this.#taskRunner = options.taskRunner; + } + + /** {@inheritdoc @backstage/plugin-catalog-node#EntityProvider.getProviderName} */ + getProviderName() { + return `{{providerClass}}:${this.#id}`; + } + + /** {@inheritdoc @backstage/plugin-catalog-node#EntityProvider.connect} */ + async connect(connection: EntityProviderConnection) { + const id = `${this.getProviderName()}:refresh`; + + // Schedule a refresh task to be run periodically + await this.#taskRunner.run({ + id, + fn: async () => { + const logger = this.#logger.child({ + taskId: id, + taskInstanceId: uuid.v4(), + }); + + try { + const entities = await this.read({ logger }); + + logger.info(`Read ${entities.length} entities`); + + await connection.applyMutation({ + type: 'full', + entities, + }); + } catch (error) { + logger.error(`Refresh failed`, error); + } + }, + }); + } + + /** + * Reads entities to be added to the catalog. + */ + async read(options: { logger: LoggerService }): Promise { + const { logger } = options; + + logger.info(`Reading entities from ${this.#target}`); + + // TODO: Implement entity reading logic from the target + const entities: DeferredEntity[] = []; + + return entities; + } +} diff --git a/packages/config-loader/CHANGELOG.md b/packages/config-loader/CHANGELOG.md index b29be4a927..c2433f3c35 100644 --- a/packages/config-loader/CHANGELOG.md +++ b/packages/config-loader/CHANGELOG.md @@ -1,5 +1,24 @@ # @backstage/config-loader +## 1.10.6-next.0 + +### Patch Changes + +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/config@1.3.6-next.0 + - @backstage/cli-common@0.1.15 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + +## 1.10.5 + +### Patch Changes + +- b45b094: Allow colon to be used as config key. +- Updated dependencies + - @backstage/config@1.3.5 + ## 1.10.4-next.0 ### Patch Changes diff --git a/packages/config-loader/package.json b/packages/config-loader/package.json index dda4d6e222..10afca8ba4 100644 --- a/packages/config-loader/package.json +++ b/packages/config-loader/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/config-loader", - "version": "1.10.4-next.0", + "version": "1.10.6-next.0", "description": "Config loading functionality used by Backstage backend, and CLI", "backstage": { "role": "node-library" diff --git a/packages/config-loader/src/sources/ObservableConfigProxy.ts b/packages/config-loader/src/sources/ObservableConfigProxy.ts index 3d2084beac..cc4859ea5a 100644 --- a/packages/config-loader/src/sources/ObservableConfigProxy.ts +++ b/packages/config-loader/src/sources/ObservableConfigProxy.ts @@ -27,11 +27,18 @@ export class ObservableConfigProxy implements Config { return new ObservableConfigProxy(undefined, undefined, abortController); } + private readonly parent?: ObservableConfigProxy; + private readonly parentKey?: string; + private readonly abortController?: AbortController; + private constructor( - private readonly parent?: ObservableConfigProxy, - private readonly parentKey?: string, - private readonly abortController?: AbortController, + parent?: ObservableConfigProxy, + parentKey?: string, + abortController?: AbortController, ) { + this.parent = parent; + this.parentKey = parentKey; + this.abortController = abortController; if (parent && !parentKey) { throw new Error('parentKey is required if parent is set'); } diff --git a/packages/config-loader/src/sources/StaticConfigSource.ts b/packages/config-loader/src/sources/StaticConfigSource.ts index 014ead8a99..9fc7ccf8dc 100644 --- a/packages/config-loader/src/sources/StaticConfigSource.ts +++ b/packages/config-loader/src/sources/StaticConfigSource.ts @@ -37,10 +37,13 @@ export interface StaticConfigSourceOptions { /** @internal */ class StaticObservableConfigSource implements ConfigSource { - constructor( - private readonly data: Observable, - private readonly context: string, - ) {} + private readonly data: Observable; + private readonly context: string; + + constructor(data: Observable, context: string) { + this.data = data; + this.context = context; + } async *readConfigData( options?: ReadConfigDataOptions | undefined, @@ -131,10 +134,16 @@ export class StaticConfigSource implements ConfigSource { return new StaticConfigSource(data, context); } + private readonly promise: JsonObject | PromiseLike; + private readonly context: string; + private constructor( - private readonly promise: JsonObject | PromiseLike, - private readonly context: string, - ) {} + promise: JsonObject | PromiseLike, + context: string, + ) { + this.promise = promise; + this.context = context; + } async *readConfigData(): AsyncConfigSourceGenerator { yield { configs: [{ data: await this.promise, context: this.context }] }; diff --git a/packages/config/CHANGELOG.md b/packages/config/CHANGELOG.md index 43bcc0551b..596a67a1ed 100644 --- a/packages/config/CHANGELOG.md +++ b/packages/config/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/config +## 1.3.6-next.0 + +### Patch Changes + +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + +## 1.3.5 + +### Patch Changes + +- b45b094: Allow colon to be used as config key. + ## 1.3.4-next.0 ### Patch Changes diff --git a/packages/config/package.json b/packages/config/package.json index ec6cd78422..75d15678c0 100644 --- a/packages/config/package.json +++ b/packages/config/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/config", - "version": "1.3.4-next.0", + "version": "1.3.6-next.0", "description": "Config API used by Backstage core, backend, and CLI", "backstage": { "role": "common-library" diff --git a/packages/config/report.api.md b/packages/config/report.api.md index 848687d0cc..94c4652b44 100644 --- a/packages/config/report.api.md +++ b/packages/config/report.api.md @@ -48,7 +48,7 @@ export class ConfigReader implements Config { constructor( data: JsonObject_2 | undefined, context?: string, - fallback?: ConfigReader | undefined, + fallback?: ConfigReader, prefix?: string, ); static fromConfigs(configs: AppConfig[]): ConfigReader; diff --git a/packages/config/src/reader.ts b/packages/config/src/reader.ts index 7ad0cf738d..8186ee8faa 100644 --- a/packages/config/src/reader.ts +++ b/packages/config/src/reader.ts @@ -139,12 +139,22 @@ export class ConfigReader implements Config { ); } + private readonly data: JsonObject | undefined; + private readonly context: string; + private readonly fallback?: ConfigReader; + private readonly prefix: string; + constructor( - private readonly data: JsonObject | undefined, - private readonly context: string = 'mock-config', - private readonly fallback?: ConfigReader, - private readonly prefix: string = '', - ) {} + data: JsonObject | undefined, + context: string = 'mock-config', + fallback?: ConfigReader, + prefix: string = '', + ) { + this.data = data; + this.context = context; + this.fallback = fallback; + this.prefix = prefix; + } /** {@inheritdoc Config.has} */ has(key: string): boolean { diff --git a/packages/core-app-api/CHANGELOG.md b/packages/core-app-api/CHANGELOG.md index 334a36acb3..0fd09f851b 100644 --- a/packages/core-app-api/CHANGELOG.md +++ b/packages/core-app-api/CHANGELOG.md @@ -1,5 +1,32 @@ # @backstage/core-app-api +## 1.19.2-next.1 + +### Patch Changes + +- b2bef92: Convert all enums to erasable-syntax compliant patterns +- Updated dependencies + - @backstage/core-plugin-api@1.11.2-next.1 + +## 1.19.2-next.0 + +### Patch Changes + +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/core-plugin-api@1.11.2-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/types@1.2.2 + - @backstage/version-bridge@1.0.11 + +## 1.19.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.5 + - @backstage/core-plugin-api@1.11.1 + ## 1.19.1-next.0 ### Patch Changes diff --git a/packages/core-app-api/package.json b/packages/core-app-api/package.json index d4a4dce17d..3ce23e2422 100644 --- a/packages/core-app-api/package.json +++ b/packages/core-app-api/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/core-app-api", - "version": "1.19.1-next.0", + "version": "1.19.2-next.1", "description": "Core app API used by Backstage apps", "backstage": { "role": "web-library" diff --git a/packages/core-app-api/src/apis/implementations/DiscoveryApi/FrontendHostDiscovery.ts b/packages/core-app-api/src/apis/implementations/DiscoveryApi/FrontendHostDiscovery.ts index 9c10c386d6..27430ab3bb 100644 --- a/packages/core-app-api/src/apis/implementations/DiscoveryApi/FrontendHostDiscovery.ts +++ b/packages/core-app-api/src/apis/implementations/DiscoveryApi/FrontendHostDiscovery.ts @@ -71,10 +71,16 @@ export class FrontendHostDiscovery implements DiscoveryApi { ); } + private readonly endpoints: Map; + private readonly defaultEndpoint: DiscoveryApi; + private constructor( - private readonly endpoints: Map, - private readonly defaultEndpoint: DiscoveryApi, - ) {} + endpoints: Map, + defaultEndpoint: DiscoveryApi, + ) { + this.endpoints = endpoints; + this.defaultEndpoint = defaultEndpoint; + } async getBaseUrl(pluginId: string): Promise { const endpoint = this.endpoints.get(pluginId); diff --git a/packages/core-app-api/src/apis/implementations/ErrorApi/ErrorAlerter.ts b/packages/core-app-api/src/apis/implementations/ErrorApi/ErrorAlerter.ts index 350213d938..34d527f73a 100644 --- a/packages/core-app-api/src/apis/implementations/ErrorApi/ErrorAlerter.ts +++ b/packages/core-app-api/src/apis/implementations/ErrorApi/ErrorAlerter.ts @@ -27,10 +27,13 @@ import { * @public */ export class ErrorAlerter implements ErrorApi { - constructor( - private readonly alertApi: AlertApi, - private readonly errorApi: ErrorApi, - ) {} + private readonly alertApi: AlertApi; + private readonly errorApi: ErrorApi; + + constructor(alertApi: AlertApi, errorApi: ErrorApi) { + this.alertApi = alertApi; + this.errorApi = errorApi; + } post(error: ErrorApiError, context?: ErrorApiErrorContext) { if (!context?.hidden) { diff --git a/packages/core-app-api/src/apis/implementations/FetchApi/IdentityAuthInjectorFetchMiddleware.ts b/packages/core-app-api/src/apis/implementations/FetchApi/IdentityAuthInjectorFetchMiddleware.ts index 842f9191eb..7c13c3ce74 100644 --- a/packages/core-app-api/src/apis/implementations/FetchApi/IdentityAuthInjectorFetchMiddleware.ts +++ b/packages/core-app-api/src/apis/implementations/FetchApi/IdentityAuthInjectorFetchMiddleware.ts @@ -64,12 +64,22 @@ export class IdentityAuthInjectorFetchMiddleware implements FetchMiddleware { }); } + public readonly identityApi: IdentityApi; + public readonly allowUrl: (url: string) => boolean; + public readonly headerName: string; + public readonly headerValue: (pluginId: string) => string; + constructor( - public readonly identityApi: IdentityApi, - public readonly allowUrl: (url: string) => boolean, - public readonly headerName: string, - public readonly headerValue: (pluginId: string) => string, - ) {} + identityApi: IdentityApi, + allowUrl: (url: string) => boolean, + headerName: string, + headerValue: (pluginId: string) => string, + ) { + this.identityApi = identityApi; + this.allowUrl = allowUrl; + this.headerName = headerName; + this.headerValue = headerValue; + } apply(next: typeof fetch): typeof fetch { return async (input, init) => { diff --git a/packages/core-app-api/src/apis/implementations/StorageApi/WebStorage.ts b/packages/core-app-api/src/apis/implementations/StorageApi/WebStorage.ts index 06ae6fc76a..ddb2b9a611 100644 --- a/packages/core-app-api/src/apis/implementations/StorageApi/WebStorage.ts +++ b/packages/core-app-api/src/apis/implementations/StorageApi/WebStorage.ts @@ -30,10 +30,13 @@ export const buckets = new Map(); * @public */ export class WebStorage implements StorageApi { - constructor( - private readonly namespace: string, - private readonly errorApi: ErrorApi, - ) {} + private readonly namespace: string; + private readonly errorApi: ErrorApi; + + constructor(namespace: string, errorApi: ErrorApi) { + this.namespace = namespace; + this.errorApi = errorApi; + } private static hasSubscribed = false; diff --git a/packages/core-app-api/src/apis/implementations/TranslationApi/I18nextTranslationApi.test.tsx b/packages/core-app-api/src/apis/implementations/TranslationApi/I18nextTranslationApi.test.tsx index b6e79b20ed..c1a0b36efb 100644 --- a/packages/core-app-api/src/apis/implementations/TranslationApi/I18nextTranslationApi.test.tsx +++ b/packages/core-app-api/src/apis/implementations/TranslationApi/I18nextTranslationApi.test.tsx @@ -37,7 +37,7 @@ const resourceRef = createTranslationRef({ bar: 'Bar', }, translations: { - sv: () => Promise.resolve({ default: { foo: 'Föö', bar: null } }), + sv: () => Promise.resolve({ default: { foo: 'Föö' } as any }), }, }); diff --git a/packages/core-app-api/src/apis/system/ApiFactoryRegistry.ts b/packages/core-app-api/src/apis/system/ApiFactoryRegistry.ts index 5f56793cae..1824937a02 100644 --- a/packages/core-app-api/src/apis/system/ApiFactoryRegistry.ts +++ b/packages/core-app-api/src/apis/system/ApiFactoryRegistry.ts @@ -31,11 +31,11 @@ export type ApiFactoryScope = | 'app' // Factories registered in the app, overriding default ones | 'static'; // APIs that can't be overridden, e.g. config -enum ScopePriority { - default = 10, - app = 50, - static = 100, -} +const ScopePriority = { + default: 10, + app: 50, + static: 100, +} as const; type FactoryTuple = { priority: number; diff --git a/packages/core-app-api/src/app/AppManager.test.tsx b/packages/core-app-api/src/app/AppManager.test.tsx index 054c28ad03..3981da56ad 100644 --- a/packages/core-app-api/src/app/AppManager.test.tsx +++ b/packages/core-app-api/src/app/AppManager.test.tsx @@ -323,7 +323,7 @@ describe('Integration Test', () => { foo: 'Foo', }, translations: { - de: () => Promise.resolve({ default: { foo: 'Bar' } }), + de: () => Promise.resolve({ default: { foo: 'Bar' } as any }), }, }); diff --git a/packages/core-app-api/src/routing/RouteResolver.ts b/packages/core-app-api/src/routing/RouteResolver.ts index 11eb753bc6..620096ebd0 100644 --- a/packages/core-app-api/src/routing/RouteResolver.ts +++ b/packages/core-app-api/src/routing/RouteResolver.ts @@ -170,16 +170,25 @@ function resolveBasePath( } export class RouteResolver { + private readonly routePaths: Map; + private readonly routeParents: Map; + private readonly routeObjects: BackstageRouteObject[]; + private readonly routeBindings: Map; + private readonly appBasePath: string; // base path without a trailing slash + constructor( - private readonly routePaths: Map, - private readonly routeParents: Map, - private readonly routeObjects: BackstageRouteObject[], - private readonly routeBindings: Map< - ExternalRouteRef, - RouteRef | SubRouteRef - >, - private readonly appBasePath: string, // base path without a trailing slash - ) {} + routePaths: Map, + routeParents: Map, + routeObjects: BackstageRouteObject[], + routeBindings: Map, + appBasePath: string, // base path without a trailing slash + ) { + this.routePaths = routePaths; + this.routeParents = routeParents; + this.routeObjects = routeObjects; + this.routeBindings = routeBindings; + this.appBasePath = appBasePath; + } resolve( anyRouteRef: diff --git a/packages/core-compat-api/CHANGELOG.md b/packages/core-compat-api/CHANGELOG.md index f0cb3b6878..b8d03b5e1c 100644 --- a/packages/core-compat-api/CHANGELOG.md +++ b/packages/core-compat-api/CHANGELOG.md @@ -1,5 +1,24 @@ # @backstage/core-compat-api +## 0.5.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.3-next.0 + - @backstage/core-plugin-api@1.11.2-next.0 + - @backstage/frontend-plugin-api@0.12.2-next.0 + - @backstage/version-bridge@1.0.11 + +## 0.5.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.2 + - @backstage/frontend-plugin-api@0.12.1 + - @backstage/core-plugin-api@1.11.1 + ## 0.5.3-next.1 ### Patch Changes diff --git a/packages/core-compat-api/package.json b/packages/core-compat-api/package.json index 0bbe284db9..cedf7e661e 100644 --- a/packages/core-compat-api/package.json +++ b/packages/core-compat-api/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/core-compat-api", - "version": "0.5.3-next.1", + "version": "0.5.4-next.0", "backstage": { "role": "web-library" }, diff --git a/packages/core-compat-api/src/collectLegacyRoutes.test.tsx b/packages/core-compat-api/src/collectLegacyRoutes.test.tsx index fe4166016e..c5138dac3d 100644 --- a/packages/core-compat-api/src/collectLegacyRoutes.test.tsx +++ b/packages/core-compat-api/src/collectLegacyRoutes.test.tsx @@ -39,7 +39,7 @@ import { useApp, } from '@backstage/core-plugin-api'; import { screen } from '@testing-library/react'; -import { renderInTestApp } from '@backstage/frontend-test-utils'; +import { renderTestApp } from '@backstage/frontend-test-utils'; const exampleApiRef = createApiRef({ id: 'plugin.example.service', @@ -304,7 +304,7 @@ describe('collectLegacyRoutes', () => { , ); - renderInTestApp(
    , { features }); + renderTestApp({ features }); await expect( screen.findByText('plugins: app, test'), diff --git a/packages/core-compat-api/src/convertLegacyApp.test.tsx b/packages/core-compat-api/src/convertLegacyApp.test.tsx index 7bbe13867c..1cb2b757e3 100644 --- a/packages/core-compat-api/src/convertLegacyApp.test.tsx +++ b/packages/core-compat-api/src/convertLegacyApp.test.tsx @@ -27,7 +27,7 @@ import { createRouteRef, } from '@backstage/core-plugin-api'; import { EntityLayout, EntitySwitch, isKind } from '@backstage/plugin-catalog'; -import { renderInTestApp } from '@backstage/frontend-test-utils'; +import { renderTestApp } from '@backstage/frontend-test-utils'; import { default as catalogPlugin } from '@backstage/plugin-catalog/alpha'; import { catalogApiRef } from '@backstage/plugin-catalog-react'; import { catalogApiMock } from '@backstage/plugin-catalog-react/testUtils'; @@ -263,7 +263,7 @@ describe('convertLegacyApp', () => { }); // Overview - const renderOverviewTest = await renderInTestApp(
    , { + const renderOverviewTest = await renderTestApp({ features: [catalogOverride, ...converted], initialRouteEntries: ['/catalog/default/test/x'], }); @@ -272,7 +272,7 @@ describe('convertLegacyApp', () => { ).resolves.toBeInTheDocument(); renderOverviewTest.unmount(); - const renderOverviewOther = await renderInTestApp(
    , { + const renderOverviewOther = await renderTestApp({ features: [catalogOverride, ...converted], initialRouteEntries: ['/catalog/default/other/x'], }); @@ -282,7 +282,7 @@ describe('convertLegacyApp', () => { renderOverviewOther.unmount(); // Foo tab - const renderFooTest = await renderInTestApp(
    , { + const renderFooTest = await renderTestApp({ features: [catalogOverride, ...converted], initialRouteEntries: ['/catalog/default/test/x/foo'], }); @@ -291,7 +291,7 @@ describe('convertLegacyApp', () => { ).resolves.toBeInTheDocument(); renderFooTest.unmount(); - const renderFooOther = await renderInTestApp(
    , { + const renderFooOther = await renderTestApp({ features: [catalogOverride, ...converted], initialRouteEntries: ['/catalog/default/other/x/foo'], }); @@ -301,7 +301,7 @@ describe('convertLegacyApp', () => { renderFooOther.unmount(); // Bar tab - const renderBarTest = await renderInTestApp(
    , { + const renderBarTest = await renderTestApp({ features: [catalogOverride, ...converted], initialRouteEntries: ['/catalog/default/test/x/bar'], }); @@ -310,7 +310,7 @@ describe('convertLegacyApp', () => { ).resolves.toBeInTheDocument(); renderBarTest.unmount(); - const renderBarOther = await renderInTestApp(
    , { + const renderBarOther = await renderTestApp({ features: [catalogOverride, ...converted], initialRouteEntries: ['/catalog/default/other/x/bar'], }); diff --git a/packages/core-components/CHANGELOG.md b/packages/core-components/CHANGELOG.md index b6109caf2e..b4d7f1c7b0 100644 --- a/packages/core-components/CHANGELOG.md +++ b/packages/core-components/CHANGELOG.md @@ -1,5 +1,58 @@ # @backstage/core-components +## 0.18.3-next.2 + +### Patch Changes + +- 96ad674: Line numbers in LogViewer will not be selectable in UI anymore + +## 0.18.3-next.1 + +### Patch Changes + +- b2bef92: Convert all enums to erasable-syntax compliant patterns +- Updated dependencies + - @backstage/core-plugin-api@1.11.2-next.1 + +## 0.18.3-next.0 + +### Patch Changes + +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/core-plugin-api@1.11.2-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/errors@1.2.7 + - @backstage/theme@0.7.0 + - @backstage/version-bridge@1.0.11 + +## 0.18.2 + +### Patch Changes + +- d493126: Swap base token for semantic token in ItemCardHeader to ensure readability in light mode. +- 431130c: Added `renderEdge` prop to `` component in `@backstage/core-components` to allow custom rendering of graph edges. +- 6981ae6: Fixed DependencyGraph `svg` size not adapting to the container size +- 95935fb: Fixed dependency graph automatically scrolling forever +- Updated dependencies + - @backstage/config@1.3.5 + - @backstage/theme@0.7.0 + - @backstage/core-plugin-api@1.11.1 + +## 0.18.2-next.3 + +### Patch Changes + +- 431130c: Added `renderEdge` prop to `` component in `@backstage/core-components` to allow custom rendering of graph edges. + +## 0.18.2-next.2 + +### Patch Changes + +- 95935fb: Fixed dependency graph automatically scrolling forever +- Updated dependencies + - @backstage/theme@0.6.9-next.0 + ## 0.18.2-next.1 ### Patch Changes diff --git a/packages/core-components/package.json b/packages/core-components/package.json index c2d09762b3..958f566c88 100644 --- a/packages/core-components/package.json +++ b/packages/core-components/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/core-components", - "version": "0.18.2-next.1", + "version": "0.18.3-next.2", "description": "Core components used by Backstage plugins and apps", "backstage": { "role": "web-library" diff --git a/packages/core-components/report.api.md b/packages/core-components/report.api.md index b89b61ac77..02fbb04805 100644 --- a/packages/core-components/report.api.md +++ b/packages/core-components/report.api.md @@ -274,6 +274,7 @@ export interface DependencyGraphProps paddingY?: number; ranker?: DependencyGraphTypes.Ranker; rankMargin?: number; + renderEdge?: DependencyGraphTypes.RenderEdgeFunction; renderLabel?: DependencyGraphTypes.RenderLabelFunction; renderNode?: DependencyGraphTypes.RenderNodeFunction; showArrowHeads?: boolean; @@ -282,11 +283,18 @@ export interface DependencyGraphProps // @public export namespace DependencyGraphTypes { - export enum Alignment { - DOWN_LEFT = 'DL', - DOWN_RIGHT = 'DR', - UP_LEFT = 'UL', - UP_RIGHT = 'UR', + // (undocumented) + export type Alignment = (typeof Alignment)[keyof typeof Alignment]; + // (undocumented) + export namespace Alignment { + // (undocumented) + export type DOWN_LEFT = typeof Alignment.DOWN_LEFT; + // (undocumented) + export type DOWN_RIGHT = typeof Alignment.DOWN_RIGHT; + // (undocumented) + export type UP_LEFT = typeof Alignment.UP_LEFT; + // (undocumented) + export type UP_RIGHT = typeof Alignment.UP_RIGHT; } export type DependencyEdge = T & { from: string; @@ -297,31 +305,97 @@ export namespace DependencyGraphTypes { export type DependencyNode = T & { id: string; }; - export enum Direction { - BOTTOM_TOP = 'BT', - LEFT_RIGHT = 'LR', - RIGHT_LEFT = 'RL', - TOP_BOTTOM = 'TB', - } - export enum LabelPosition { + // (undocumented) + export type Direction = (typeof Direction)[keyof typeof Direction]; + // (undocumented) + export namespace Direction { // (undocumented) - CENTER = 'c', + export type BOTTOM_TOP = typeof Direction.BOTTOM_TOP; // (undocumented) - LEFT = 'l', + export type LEFT_RIGHT = typeof Direction.LEFT_RIGHT; // (undocumented) - RIGHT = 'r', + export type RIGHT_LEFT = typeof Direction.RIGHT_LEFT; + // (undocumented) + export type TOP_BOTTOM = typeof Direction.TOP_BOTTOM; } - export enum Ranker { - LONGEST_PATH = 'longest-path', - NETWORK_SIMPLEX = 'network-simplex', - TIGHT_TREE = 'tight-tree', + // (undocumented) + export type LabelPosition = + (typeof LabelPosition)[keyof typeof LabelPosition]; + // (undocumented) + export namespace LabelPosition { + // (undocumented) + export type CENTER = typeof LabelPosition.CENTER; + // (undocumented) + export type LEFT = typeof LabelPosition.LEFT; + // (undocumented) + export type RIGHT = typeof LabelPosition.RIGHT; } + const Direction: { + readonly TOP_BOTTOM: 'TB'; + readonly BOTTOM_TOP: 'BT'; + readonly LEFT_RIGHT: 'LR'; + readonly RIGHT_LEFT: 'RL'; + }; + // (undocumented) + export type Ranker = (typeof Ranker)[keyof typeof Ranker]; + // (undocumented) + export namespace Ranker { + // (undocumented) + export type LONGEST_PATH = typeof Ranker.LONGEST_PATH; + // (undocumented) + export type NETWORK_SIMPLEX = typeof Ranker.NETWORK_SIMPLEX; + // (undocumented) + export type TIGHT_TREE = typeof Ranker.TIGHT_TREE; + } + const Alignment: { + readonly UP_LEFT: 'UL'; + readonly UP_RIGHT: 'UR'; + readonly DOWN_LEFT: 'DL'; + readonly DOWN_RIGHT: 'DR'; + }; + export type RenderEdgeFunction = ( + props: RenderEdgeProps, + ) => ReactNode; + export type RenderEdgeProps = { + edge: T & { + points: { + x: number; + y: number; + }[]; + label?: string; + labeloffset?: number; + labelpos?: string; + width?: number; + height?: number; + weight?: number; + minlen?: number; + showArrowHeads?: boolean; + from?: string; + to?: string; + relations?: string[]; + }; + id: { + v: string; + w: string; + name?: string | undefined; + }; + }; + const Ranker: { + readonly NETWORK_SIMPLEX: 'network-simplex'; + readonly TIGHT_TREE: 'tight-tree'; + readonly LONGEST_PATH: 'longest-path'; + }; export type RenderLabelFunction = ( props: RenderLabelProps, ) => ReactNode; export type RenderLabelProps = { edge: DependencyEdge; }; + const LabelPosition: { + readonly LEFT: 'l'; + readonly RIGHT: 'r'; + readonly CENTER: 'c'; + }; export type RenderNodeFunction = ( props: RenderNodeProps, ) => ReactNode; diff --git a/packages/core-components/src/components/DependencyGraph/DependencyGraph.tsx b/packages/core-components/src/components/DependencyGraph/DependencyGraph.tsx index df26bb86fd..a4ef0ac682 100644 --- a/packages/core-components/src/components/DependencyGraph/DependencyGraph.tsx +++ b/packages/core-components/src/components/DependencyGraph/DependencyGraph.tsx @@ -54,7 +54,7 @@ const useStyles = makeStyles((theme: Theme) => ({ minWidth: '100%', }, fixedHeight: { - height: '100%', + maxHeight: '100%', }, fullscreen: { backgroundColor: theme.palette.background.paper, @@ -79,7 +79,7 @@ export interface DependencyGraphProps */ nodes: Types.DependencyNode[]; /** - * Graph {@link DependencyGraphTypes.Direction | direction} + * Graph {@link DependencyGraphTypes.(Direction:namespace) | direction} * * @remarks * @@ -87,7 +87,7 @@ export interface DependencyGraphProps */ direction?: Types.Direction; /** - * Node {@link DependencyGraphTypes.Alignment | alignment} + * Node {@link DependencyGraphTypes.(Alignment:namespace) | alignment} */ align?: Types.Alignment; /** @@ -135,7 +135,7 @@ export interface DependencyGraphProps */ acyclicer?: 'greedy'; /** - * {@link DependencyGraphTypes.Ranker | Algorithm} used to rank nodes + * {@link DependencyGraphTypes.(Ranker:namespace) | Algorithm} used to rank nodes * * @remarks * @@ -143,7 +143,7 @@ export interface DependencyGraphProps */ ranker?: Types.Ranker; /** - * {@link DependencyGraphTypes.LabelPosition | Position} of label in relation to edge + * {@link DependencyGraphTypes.(LabelPosition:namespace) | Position} of label in relation to edge * * @remarks * @@ -167,6 +167,10 @@ export interface DependencyGraphProps * Weight applied to edges in graph */ edgeWeight?: number; + /** + * Custom edge rendering component + */ + renderEdge?: Types.RenderEdgeFunction; /** * Custom node rendering component */ @@ -248,6 +252,7 @@ export function DependencyGraph( labelOffset = 10, edgeRanks = 1, edgeWeight = 1, + renderEdge, renderLabel, defs, zoom = 'enabled', @@ -282,7 +287,8 @@ export function DependencyGraph( const [_measureRef] = useMeasure(); const measureRef = once(_measureRef); - const scalableHeight = fit === 'grow' ? maxHeight : containerHeight; + const scalableHeight = + fit === 'grow' && !fullScreenHandle.active ? maxHeight : '100%'; const containerRef = useMemo( () => @@ -335,10 +341,16 @@ export function DependencyGraph( const { width: newContainerWidth, height: newContainerHeight } = root.getBoundingClientRect(); - if (containerWidth !== newContainerWidth) { + if ( + containerWidth !== newContainerWidth && + newContainerWidth <= maxWidth + ) { setContainerWidth(newContainerWidth); } - if (containerHeight !== newContainerHeight) { + if ( + containerHeight !== newContainerHeight && + newContainerHeight <= maxHeight + ) { setContainerHeight(newContainerHeight); } }, 100), @@ -464,39 +476,32 @@ export function DependencyGraph( ); return ( -
    - - {allowFullscreen && ( - - - {fullScreenHandle.active ? ( - - ) : ( - - )} - - - )} + {allowFullscreen && ( + + + {fullScreenHandle.active ? ( + + ) : ( + + )} + + + )} +
    ( height={graphHeight} y={maxHeight / 2 - graphHeight / 2} x={maxWidth / 2 - graphWidth / 2} - viewBox={`0 0 ${graphWidth} ${graphHeight}`} + viewBox={`-25 -25 ${graphWidth + 50} ${graphHeight + 50}`} > {graphEdges.map(e => { const edge = graph.current.edge(e) as GraphEdge; if (!edge) return null; + if (renderEdge) return renderEdge({ edge, id: e }); + return ( ( - -
    +
    + ); } diff --git a/packages/core-components/src/components/DependencyGraph/types.ts b/packages/core-components/src/components/DependencyGraph/types.ts index a449637e49..6a75df27b5 100644 --- a/packages/core-components/src/components/DependencyGraph/types.ts +++ b/packages/core-components/src/components/DependencyGraph/types.ts @@ -13,6 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +/* We want to maintain the same information as an enum, so we disable the redeclaration warning */ +/* eslint-disable @typescript-eslint/no-redeclare */ /** * Types used to customize and provide data to {@link DependencyGraph} @@ -93,28 +95,79 @@ export namespace DependencyGraphTypes { props: RenderNodeProps, ) => ReactNode; + /** + * Properties of {@link DependencyGraphTypes.RenderEdgeFunction} for {@link DependencyGraphTypes.DependencyEdge} + * + * @public + */ + export type RenderEdgeProps = { + edge: T & { + points: { x: number; y: number }[]; + label?: string; + labeloffset?: number; + labelpos?: string; + width?: number; + height?: number; + weight?: number; + minlen?: number; + showArrowHeads?: boolean; + from?: string; + to?: string; + relations?: string[]; + }; + id: { + v: string; + w: string; + name?: string | undefined; + }; + }; + + /** + * Custom React component for graph {@link DependencyGraphTypes.DependencyEdge} + * + * @public + */ + export type RenderEdgeFunction = ( + props: RenderEdgeProps, + ) => ReactNode; + /** * Graph direction * * @public */ - export enum Direction { + export const Direction = { /** * Top to Bottom */ - TOP_BOTTOM = 'TB', + TOP_BOTTOM: 'TB', /** * Bottom to Top */ - BOTTOM_TOP = 'BT', + BOTTOM_TOP: 'BT', /** * Left to Right */ - LEFT_RIGHT = 'LR', + LEFT_RIGHT: 'LR', /** * Right to Left */ - RIGHT_LEFT = 'RL', + RIGHT_LEFT: 'RL', + } as const; + + /** + * @public + */ + export type Direction = (typeof Direction)[keyof typeof Direction]; + + /** + * @public + */ + export namespace Direction { + export type TOP_BOTTOM = typeof Direction.TOP_BOTTOM; + export type BOTTOM_TOP = typeof Direction.BOTTOM_TOP; + export type LEFT_RIGHT = typeof Direction.LEFT_RIGHT; + export type RIGHT_LEFT = typeof Direction.RIGHT_LEFT; } /** @@ -122,23 +175,38 @@ export namespace DependencyGraphTypes { * * @public */ - export enum Alignment { + export const Alignment = { /** * Up Left */ - UP_LEFT = 'UL', + UP_LEFT: 'UL', /** * Up Right */ - UP_RIGHT = 'UR', + UP_RIGHT: 'UR', /** * Down Left */ - DOWN_LEFT = 'DL', + DOWN_LEFT: 'DL', /** * Down Right */ - DOWN_RIGHT = 'DR', + DOWN_RIGHT: 'DR', + } as const; + + /** + * @public + */ + export type Alignment = (typeof Alignment)[keyof typeof Alignment]; + + /** + * @public + */ + export namespace Alignment { + export type UP_LEFT = typeof Alignment.UP_LEFT; + export type UP_RIGHT = typeof Alignment.UP_RIGHT; + export type DOWN_LEFT = typeof Alignment.DOWN_LEFT; + export type DOWN_RIGHT = typeof Alignment.DOWN_RIGHT; } /** @@ -146,15 +214,15 @@ export namespace DependencyGraphTypes { * * @public */ - export enum Ranker { + export const Ranker = { /** * {@link https://en.wikipedia.org/wiki/Network_simplex_algorithm | Network Simplex} algorithm */ - NETWORK_SIMPLEX = 'network-simplex', + NETWORK_SIMPLEX: 'network-simplex', /** * Tight Tree algorithm */ - TIGHT_TREE = 'tight-tree', + TIGHT_TREE: 'tight-tree', /** * Longest path algorithm * @@ -162,7 +230,21 @@ export namespace DependencyGraphTypes { * * Simplest and fastest */ - LONGEST_PATH = 'longest-path', + LONGEST_PATH: 'longest-path', + } as const; + + /** + * @public + */ + export type Ranker = (typeof Ranker)[keyof typeof Ranker]; + + /** + * @public + */ + export namespace Ranker { + export type NETWORK_SIMPLEX = typeof Ranker.NETWORK_SIMPLEX; + export type TIGHT_TREE = typeof Ranker.TIGHT_TREE; + export type LONGEST_PATH = typeof Ranker.LONGEST_PATH; } /** @@ -170,9 +252,24 @@ export namespace DependencyGraphTypes { * * @public */ - export enum LabelPosition { - LEFT = 'l', - RIGHT = 'r', - CENTER = 'c', + export const LabelPosition = { + LEFT: 'l', + RIGHT: 'r', + CENTER: 'c', + } as const; + + /** + * @public + */ + export type LabelPosition = + (typeof LabelPosition)[keyof typeof LabelPosition]; + + /** + * @public + */ + export namespace LabelPosition { + export type LEFT = typeof LabelPosition.LEFT; + export type RIGHT = typeof LabelPosition.RIGHT; + export type CENTER = typeof LabelPosition.CENTER; } } diff --git a/packages/core-components/src/components/LogViewer/AnsiProcessor.ts b/packages/core-components/src/components/LogViewer/AnsiProcessor.ts index bcd5af7ced..0db5e69211 100644 --- a/packages/core-components/src/components/LogViewer/AnsiProcessor.ts +++ b/packages/core-components/src/components/LogViewer/AnsiProcessor.ts @@ -78,11 +78,12 @@ export interface AnsiChunk { export class AnsiLine { text: string; + readonly lineNumber: number; + readonly chunks: AnsiChunk[]; - constructor( - readonly lineNumber: number = 1, - readonly chunks: AnsiChunk[] = [], - ) { + constructor(lineNumber: number = 1, chunks: AnsiChunk[] = []) { + this.lineNumber = lineNumber; + this.chunks = chunks; this.text = chunks .map(c => c.text) .join('') diff --git a/packages/core-components/src/components/LogViewer/styles.ts b/packages/core-components/src/components/LogViewer/styles.ts index 76e045ca32..238361f9a9 100644 --- a/packages/core-components/src/components/LogViewer/styles.ts +++ b/packages/core-components/src/components/LogViewer/styles.ts @@ -97,6 +97,7 @@ export const useStyles = makeStyles( marginRight: theme.spacing(1), cursor: 'pointer', flexShrink: 0, + userSelect: 'none', }, textHighlight: { background: alpha(theme.palette.info.main, 0.15), diff --git a/packages/core-components/src/layout/Sidebar/Bar.tsx b/packages/core-components/src/layout/Sidebar/Bar.tsx index 00fe6f96ae..bfa1e15d5e 100644 --- a/packages/core-components/src/layout/Sidebar/Bar.tsx +++ b/packages/core-components/src/layout/Sidebar/Bar.tsx @@ -97,11 +97,11 @@ const useStyles = makeStyles( { name: 'BackstageSidebar' }, ); -enum State { - Closed, - Idle, - Open, -} +const State = { + Closed: 0, + Idle: 1, + Open: 2, +} as const; /** @public */ export type SidebarProps = { @@ -144,7 +144,9 @@ const DesktopSidebar = (props: DesktopSidebarProps) => { theme => theme.breakpoints.down('md'), { noSsr: true }, ); - const [state, setState] = useState(State.Closed); + const [state, setState] = useState<(typeof State)[keyof typeof State]>( + State.Closed, + ); const hoverTimerRef = useRef(); const { isPinned, toggleSidebarPinState } = useSidebarPinState(); diff --git a/packages/core-components/src/layout/Sidebar/localStorage.ts b/packages/core-components/src/layout/Sidebar/localStorage.ts index 78ec355306..b62252e1df 100644 --- a/packages/core-components/src/layout/Sidebar/localStorage.ts +++ b/packages/core-components/src/layout/Sidebar/localStorage.ts @@ -14,9 +14,9 @@ * limitations under the License. */ -enum LocalStorageKeys { - SIDEBAR_PIN_STATE = 'sidebarPinState', -} +const LocalStorageKeys = { + SIDEBAR_PIN_STATE: 'sidebarPinState', +} as const; export const LocalStorage = { getSidebarPinState(): boolean { diff --git a/packages/core-components/src/layout/SignInPage/UserIdentity.ts b/packages/core-components/src/layout/SignInPage/UserIdentity.ts index 781cb47c0e..910b46b016 100644 --- a/packages/core-components/src/layout/SignInPage/UserIdentity.ts +++ b/packages/core-components/src/layout/SignInPage/UserIdentity.ts @@ -96,13 +96,19 @@ export class UserIdentity implements IdentityApi { return new UserIdentity(options.identity, options.authApi, options.profile); } + private readonly identity: BackstageUserIdentity; + private readonly authApi: ProfileInfoApi & BackstageIdentityApi & SessionApi; + private readonly profile?: ProfileInfo; + private constructor( - private readonly identity: BackstageUserIdentity, - private readonly authApi: ProfileInfoApi & - BackstageIdentityApi & - SessionApi, - private readonly profile?: ProfileInfo, - ) {} + identity: BackstageUserIdentity, + authApi: ProfileInfoApi & BackstageIdentityApi & SessionApi, + profile?: ProfileInfo, + ) { + this.identity = identity; + this.authApi = authApi; + this.profile = profile; + } /** {@inheritdoc @backstage/core-plugin-api#IdentityApi.getUserId} */ getUserId(): string { diff --git a/packages/core-plugin-api/CHANGELOG.md b/packages/core-plugin-api/CHANGELOG.md index 89ad055230..29dedfe6a7 100644 --- a/packages/core-plugin-api/CHANGELOG.md +++ b/packages/core-plugin-api/CHANGELOG.md @@ -1,5 +1,29 @@ # @backstage/core-plugin-api +## 1.11.2-next.1 + +### Patch Changes + +- b2bef92: Convert all enums to erasable-syntax compliant patterns + +## 1.11.2-next.0 + +### Patch Changes + +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/config@1.3.6-next.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.2 + - @backstage/version-bridge@1.0.11 + +## 1.11.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.5 + ## 1.11.1-next.0 ### Patch Changes diff --git a/packages/core-plugin-api/package.json b/packages/core-plugin-api/package.json index d019090bb4..430d081b49 100644 --- a/packages/core-plugin-api/package.json +++ b/packages/core-plugin-api/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/core-plugin-api", - "version": "1.11.1-next.0", + "version": "1.11.2-next.1", "description": "Core API used by Backstage plugins", "backstage": { "role": "web-library" diff --git a/packages/core-plugin-api/report.api.md b/packages/core-plugin-api/report.api.md index b40088dc78..e1b1a243df 100644 --- a/packages/core-plugin-api/report.api.md +++ b/packages/core-plugin-api/report.api.md @@ -461,9 +461,21 @@ export type FeatureFlagsSaveOptions = { }; // @public -export enum FeatureFlagState { - Active = 1, - None = 0, +export const FeatureFlagState: { + readonly None: 0; + readonly Active: 1; +}; + +// @public (undocumented) +export type FeatureFlagState = + (typeof FeatureFlagState)[keyof typeof FeatureFlagState]; + +// @public (undocumented) +export namespace FeatureFlagState { + // (undocumented) + export type Active = typeof FeatureFlagState.Active; + // (undocumented) + export type None = typeof FeatureFlagState.None; } // @public @@ -696,9 +708,20 @@ export type SessionApi = { }; // @public -export enum SessionState { - SignedIn = 'SignedIn', - SignedOut = 'SignedOut', +export const SessionState: { + readonly SignedIn: 'SignedIn'; + readonly SignedOut: 'SignedOut'; +}; + +// @public (undocumented) +export type SessionState = (typeof SessionState)[keyof typeof SessionState]; + +// @public (undocumented) +export namespace SessionState { + // (undocumented) + export type SignedIn = typeof SessionState.SignedIn; + // (undocumented) + export type SignedOut = typeof SessionState.SignedOut; } // @public diff --git a/packages/core-plugin-api/src/analytics/Tracker.ts b/packages/core-plugin-api/src/analytics/Tracker.ts index 4ae31b43fd..5507075551 100644 --- a/packages/core-plugin-api/src/analytics/Tracker.ts +++ b/packages/core-plugin-api/src/analytics/Tracker.ts @@ -65,14 +65,19 @@ const globalEvents = getOrCreateGlobalSingleton( export const routableExtensionRenderedEvent = '_ROUTABLE-EXTENSION-RENDERED'; export class Tracker implements AnalyticsTracker { + private readonly analyticsApi: AnalyticsApi; + private context: AnalyticsContextValue; + constructor( - private readonly analyticsApi: AnalyticsApi, - private context: AnalyticsContextValue = { + analyticsApi: AnalyticsApi, + context: AnalyticsContextValue = { routeRef: 'unknown', pluginId: 'root', extension: 'App', }, ) { + this.analyticsApi = analyticsApi; + this.context = context; // Only register a single beforeunload event across all trackers. if (!globalEvents.beforeUnloadRegistered) { // Before the page unloads, attempt to capture any deferred navigation diff --git a/packages/core-plugin-api/src/apis/definitions/FeatureFlagsApi.ts b/packages/core-plugin-api/src/apis/definitions/FeatureFlagsApi.ts index 66260afa32..d4429975cc 100644 --- a/packages/core-plugin-api/src/apis/definitions/FeatureFlagsApi.ts +++ b/packages/core-plugin-api/src/apis/definitions/FeatureFlagsApi.ts @@ -13,6 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +/* We want to maintain the same information as an enum, so we disable the redeclaration warning */ +/* eslint-disable @typescript-eslint/no-redeclare */ import { ApiRef, createApiRef } from '../system'; @@ -32,15 +34,29 @@ export type FeatureFlag = { * * @public */ -export enum FeatureFlagState { +export const FeatureFlagState = { /** * Feature flag inactive (disabled). */ - None = 0, + None: 0, /** * Feature flag active (enabled). */ - Active = 1, + Active: 1, +} as const; + +/** + * @public + */ +export type FeatureFlagState = + (typeof FeatureFlagState)[keyof typeof FeatureFlagState]; + +/** + * @public + */ +export namespace FeatureFlagState { + export type None = typeof FeatureFlagState.None; + export type Active = typeof FeatureFlagState.Active; } /** diff --git a/packages/core-plugin-api/src/apis/definitions/auth.ts b/packages/core-plugin-api/src/apis/definitions/auth.ts index f8686cb128..9a0ddcd958 100644 --- a/packages/core-plugin-api/src/apis/definitions/auth.ts +++ b/packages/core-plugin-api/src/apis/definitions/auth.ts @@ -13,6 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +/* We want to maintain the same information as an enum, so we disable the redeclaration warning */ +/* eslint-disable @typescript-eslint/no-redeclare */ import { ApiRef, createApiRef } from '../system'; import { IconComponent } from '../../icons/types'; @@ -269,15 +271,28 @@ export type ProfileInfo = { * * @public */ -export enum SessionState { +export const SessionState = { /** * User signed in. */ - SignedIn = 'SignedIn', + SignedIn: 'SignedIn', /** * User not signed in. */ - SignedOut = 'SignedOut', + SignedOut: 'SignedOut', +} as const; + +/** + * @public + */ +export type SessionState = (typeof SessionState)[keyof typeof SessionState]; + +/** + * @public + */ +export namespace SessionState { + export type SignedIn = typeof SessionState.SignedIn; + export type SignedOut = typeof SessionState.SignedOut; } /** diff --git a/packages/core-plugin-api/src/extensions/useElementFilter.tsx b/packages/core-plugin-api/src/extensions/useElementFilter.tsx index fa04e7c2c6..c696b3b77e 100644 --- a/packages/core-plugin-api/src/extensions/useElementFilter.tsx +++ b/packages/core-plugin-api/src/extensions/useElementFilter.tsx @@ -135,10 +135,13 @@ export interface ElementCollection { } class Collection implements ElementCollection { - constructor( - private readonly node: ReactNode, - private readonly featureFlagsApi: FeatureFlagsApi, - ) {} + private readonly node: ReactNode; + private readonly featureFlagsApi: FeatureFlagsApi; + + constructor(node: ReactNode, featureFlagsApi: FeatureFlagsApi) { + this.node = node; + this.featureFlagsApi = featureFlagsApi; + } selectByComponentData(query: { key: string; withStrictError?: string }) { const selection = selectChildren( diff --git a/packages/core-plugin-api/src/routing/ExternalRouteRef.ts b/packages/core-plugin-api/src/routing/ExternalRouteRef.ts index d61001aa10..16e2f9bc72 100644 --- a/packages/core-plugin-api/src/routing/ExternalRouteRef.ts +++ b/packages/core-plugin-api/src/routing/ExternalRouteRef.ts @@ -34,12 +34,22 @@ export class ExternalRouteRefImpl< declare $$routeRefType: 'external'; readonly [routeRefType] = 'external'; + private readonly id: string; + readonly params: ParamKeys; + readonly optional: Optional; + readonly defaultTarget: string | undefined; + constructor( - private readonly id: string, - readonly params: ParamKeys, - readonly optional: Optional, - readonly defaultTarget: string | undefined, - ) {} + id: string, + params: ParamKeys, + optional: Optional, + defaultTarget: string | undefined, + ) { + this.id = id; + this.params = params; + this.optional = optional; + this.defaultTarget = defaultTarget; + } toString() { return `routeRef{type=external,id=${this.id}}`; diff --git a/packages/core-plugin-api/src/routing/RouteRef.ts b/packages/core-plugin-api/src/routing/RouteRef.ts index 36ca3f073d..d78f3ddb8d 100644 --- a/packages/core-plugin-api/src/routing/RouteRef.ts +++ b/packages/core-plugin-api/src/routing/RouteRef.ts @@ -32,10 +32,13 @@ export class RouteRefImpl declare $$routeRefType: 'absolute'; readonly [routeRefType] = 'absolute'; - constructor( - private readonly id: string, - readonly params: ParamKeys, - ) {} + private readonly id: string; + readonly params: ParamKeys; + + constructor(id: string, params: ParamKeys) { + this.id = id; + this.params = params; + } get title() { return this.id; diff --git a/packages/core-plugin-api/src/routing/SubRouteRef.ts b/packages/core-plugin-api/src/routing/SubRouteRef.ts index b08368d74e..ed9ded41d4 100644 --- a/packages/core-plugin-api/src/routing/SubRouteRef.ts +++ b/packages/core-plugin-api/src/routing/SubRouteRef.ts @@ -36,12 +36,22 @@ export class SubRouteRefImpl declare $$routeRefType: 'sub'; readonly [routeRefType] = 'sub'; + private readonly id: string; + readonly path: string; + readonly parent: RouteRef; + readonly params: ParamKeys; + constructor( - private readonly id: string, - readonly path: string, - readonly parent: RouteRef, - readonly params: ParamKeys, - ) {} + id: string, + path: string, + parent: RouteRef, + params: ParamKeys, + ) { + this.id = id; + this.path = path; + this.parent = parent; + this.params = params; + } toString() { return `routeRef{type=sub,id=${this.id}}`; diff --git a/packages/create-app/CHANGELOG.md b/packages/create-app/CHANGELOG.md index b2960b4c83..f86586b990 100644 --- a/packages/create-app/CHANGELOG.md +++ b/packages/create-app/CHANGELOG.md @@ -1,5 +1,53 @@ # @backstage/create-app +## 0.7.6-next.3 + +### Patch Changes + +- Bumped create-app version. + +## 0.7.6-next.2 + +### Patch Changes + +- 9f939a6: Added `@backstage/plugin-app-visualizer` to the app in the `--next` template. + +## 0.7.6-next.1 + +### Patch Changes + +- Bumped create-app version. + +## 0.7.6-next.0 + +### Patch Changes + +- fc7cbfc: Added the new `@backstage/cli/templates/catalog-provider-module` template to the explicit template configuration for the `next-app` template. +- Updated dependencies + - @backstage/cli-common@0.1.15 + +## 0.7.5 + +### Patch Changes + +- c73bfa4: Bumped create-app version. +- c2c6054: Bumped create-app version. +- ffb5b44: Bumped create-app version. +- 316d077: Bumped create-app version. +- 7dcedff: Bump `better-sqlite3` to the latest version + +## 0.7.5-next.3 + +### Patch Changes + +- Bumped create-app version. + +## 0.7.5-next.2 + +### Patch Changes + +- Bumped create-app version. + ## 0.7.5-next.1 ### Patch Changes diff --git a/packages/create-app/package.json b/packages/create-app/package.json index cd37f6eef6..d0b5850d58 100644 --- a/packages/create-app/package.json +++ b/packages/create-app/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/create-app", - "version": "0.7.5-next.1", + "version": "0.7.6-next.3", "description": "A CLI that helps you create your own Backstage app", "backstage": { "role": "cli" diff --git a/packages/create-app/seed-yarn.lock b/packages/create-app/seed-yarn.lock index 00a86105cc..9d4d5449d1 100644 --- a/packages/create-app/seed-yarn.lock +++ b/packages/create-app/seed-yarn.lock @@ -16,3 +16,9 @@ // package: the name of the package, e.g. @testing-library/react // query: the version query to pin the version for, e.g. ^14.0.0 // version: the version to pin to, must be in range of the query, e.g. 14.11.0 + +// Here to fix a postinstall breakage: https://github.com/swc-project/swc/issues/11129 +"@swc/core@^1.3.46": + version "1.11.31" + resolved "https://registry.yarnpkg.com/@swc/core/-/core-1.11.31.tgz#e5de9ed005551ce9a16aa69e79935fc33065475c" + integrity sha512-mAby9aUnKRjMEA7v8cVZS9Ah4duoRBnX7X6r5qrhTxErx+68MoY1TPrVwj/66/SWN3Bl+jijqAqoB8Qx0QE34A== diff --git a/packages/create-app/src/lib/versions.ts b/packages/create-app/src/lib/versions.ts index 6785fb9b52..51fa8dff84 100644 --- a/packages/create-app/src/lib/versions.ts +++ b/packages/create-app/src/lib/versions.ts @@ -53,6 +53,7 @@ import { version as repoTools } from '../../../repo-tools/package.json'; import { version as ui } from '../../../ui/package.json'; import { version as pluginApiDocs } from '../../../../plugins/api-docs/package.json'; +import { version as pluginAppVisualizer } from '../../../../plugins/app-visualizer/package.json'; import { version as pluginAppBackend } from '../../../../plugins/app-backend/package.json'; import { version as pluginAuthBackend } from '../../../../plugins/auth-backend/package.json'; import { version as pluginAuthBackendModuleGithubProvider } from '../../../../plugins/auth-backend-module-github-provider/package.json'; @@ -117,6 +118,7 @@ export const packageVersions = { '@backstage/repo-tools': repoTools, '@backstage/plugin-api-docs': pluginApiDocs, '@backstage/plugin-app-backend': pluginAppBackend, + '@backstage/plugin-app-visualizer': pluginAppVisualizer, '@backstage/plugin-auth-backend': pluginAuthBackend, '@backstage/plugin-auth-backend-module-github-provider': pluginAuthBackendModuleGithubProvider, diff --git a/packages/create-app/templates/next-app/package.json.hbs b/packages/create-app/templates/next-app/package.json.hbs index ae905dbd3f..33e76aba3b 100644 --- a/packages/create-app/templates/next-app/package.json.hbs +++ b/packages/create-app/templates/next-app/package.json.hbs @@ -38,6 +38,7 @@ "@backstage/cli/templates/plugin-common-library", "@backstage/cli/templates/web-library", "@backstage/cli/templates/node-library", + "@backstage/cli/templates/catalog-provider-module", "@backstage/cli/templates/scaffolder-backend-module" ] } diff --git a/packages/create-app/templates/next-app/packages/app/package.json.hbs b/packages/create-app/templates/next-app/packages/app/package.json.hbs index bc02fdc21a..726983fd69 100644 --- a/packages/create-app/templates/next-app/packages/app/package.json.hbs +++ b/packages/create-app/templates/next-app/packages/app/package.json.hbs @@ -21,6 +21,7 @@ "@backstage/frontend-defaults": "^{{ version '@backstage/frontend-defaults'}}", "@backstage/frontend-plugin-api": "^{{ version '@backstage/frontend-plugin-api'}}", "@backstage/integration-react": "^{{ version '@backstage/integration-react'}}", + "@backstage/plugin-app-visualizer": "^{{ version '@backstage/plugin-app-visualizer'}}", "@backstage/plugin-catalog": "^{{ version '@backstage/plugin-catalog'}}", "@backstage/plugin-notifications": "^{{ version '@backstage/plugin-notifications'}}", "@backstage/plugin-org": "^{{ version '@backstage/plugin-org'}}", diff --git a/packages/create-app/templates/next-app/packages/backend/package.json.hbs b/packages/create-app/templates/next-app/packages/backend/package.json.hbs index 483c0b7a4e..518683ffd7 100644 --- a/packages/create-app/templates/next-app/packages/backend/package.json.hbs +++ b/packages/create-app/templates/next-app/packages/backend/package.json.hbs @@ -44,7 +44,7 @@ "@backstage/plugin-signals-backend": "^{{version '@backstage/plugin-signals-backend'}}", "@backstage/plugin-techdocs-backend": "^{{version '@backstage/plugin-techdocs-backend'}}", "app": "link:../app", - "better-sqlite3": "^9.0.0", + "better-sqlite3": "^12.0.0", "node-gyp": "^10.0.0", "pg": "^8.11.3" }, diff --git a/packages/dev-utils/CHANGELOG.md b/packages/dev-utils/CHANGELOG.md index 7f7338afeb..056bc4d9f9 100644 --- a/packages/dev-utils/CHANGELOG.md +++ b/packages/dev-utils/CHANGELOG.md @@ -1,5 +1,50 @@ # @backstage/dev-utils +## 1.1.17-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/ui@0.9.0-next.1 + +## 1.1.17-next.0 + +### Patch Changes + +- b29a856: Fixed styling of the dev app by adding a lazy import of `@backstage/ui/css/styles.css`. +- Updated dependencies + - @backstage/ui@0.8.2-next.0 + - @backstage/plugin-catalog-react@1.21.3-next.0 + - @backstage/core-app-api@1.19.2-next.0 + - @backstage/core-plugin-api@1.11.2-next.0 + - @backstage/core-components@0.18.3-next.0 + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/app-defaults@1.7.2-next.0 + - @backstage/integration-react@1.2.12-next.0 + - @backstage/theme@0.7.0 + +## 1.1.15 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.21.2 + - @backstage/core-components@0.18.2 + - @backstage/theme@0.7.0 + - @backstage/app-defaults@1.7.1 + - @backstage/core-app-api@1.19.1 + - @backstage/core-plugin-api@1.11.1 + - @backstage/integration-react@1.2.11 + +## 1.1.15-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2-next.2 + - @backstage/theme@0.6.9-next.0 + - @backstage/app-defaults@1.7.1-next.2 + ## 1.1.15-next.1 ### Patch Changes diff --git a/packages/dev-utils/package.json b/packages/dev-utils/package.json index 707717f6a4..c7147efda9 100644 --- a/packages/dev-utils/package.json +++ b/packages/dev-utils/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/dev-utils", - "version": "1.1.15-next.1", + "version": "1.1.17-next.1", "description": "Utilities for developing Backstage plugins.", "backstage": { "role": "web-library" @@ -44,6 +44,7 @@ "@backstage/integration-react": "workspace:^", "@backstage/plugin-catalog-react": "workspace:^", "@backstage/theme": "workspace:^", + "@backstage/ui": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "react-use": "^17.2.4" diff --git a/packages/ui/src/props/spacing.props.ts b/packages/dev-utils/src/devApp/BuiCss.tsx similarity index 62% rename from packages/ui/src/props/spacing.props.ts rename to packages/dev-utils/src/devApp/BuiCss.tsx index d33bab2aa5..308c59266f 100644 --- a/packages/ui/src/props/spacing.props.ts +++ b/packages/dev-utils/src/devApp/BuiCss.tsx @@ -13,29 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { paddingPropDefs } from './padding.props'; -import { marginPropDefs } from './margin.props'; -export const spacingValues = [ - '0.5', - '1', - '1.5', - '2', - '3', - '4', - '5', - '6', - '7', - '8', - '9', - '10', - '11', - '12', - '13', - '14', -]; +// This ensures that dev apps always have the BUI CSS loaded. +// eslint-disable-next-line @backstage/no-ui-css-imports-in-non-frontend +import '@backstage/ui/css/styles.css'; -export const spacingPropDefs = { - ...paddingPropDefs(spacingValues), - ...marginPropDefs(spacingValues), -}; +/** + * Placeholder component to allow lazy loading of the BUI CSS import. This + * ensures that we don't load the CSS as soon as anyone imports this package. + */ +export default () => null; diff --git a/packages/dev-utils/src/devApp/render.tsx b/packages/dev-utils/src/devApp/render.tsx index b3f8388009..0d063084f7 100644 --- a/packages/dev-utils/src/devApp/render.tsx +++ b/packages/dev-utils/src/devApp/render.tsx @@ -47,12 +47,14 @@ import { } from '@backstage/integration-react'; import Box from '@material-ui/core/Box'; import BookmarkIcon from '@material-ui/icons/Bookmark'; -import { ComponentType, PropsWithChildren, ReactNode } from 'react'; +import { ComponentType, lazy, PropsWithChildren, ReactNode } from 'react'; import { createRoutesFromChildren, Route } from 'react-router-dom'; import { SidebarThemeSwitcher } from './SidebarThemeSwitcher'; import 'react-dom'; import { SidebarLanguageSwitcher, SidebarSignOutButton } from '../components'; +const BuiCss = lazy(() => import('./BuiCss')); + let ReactDOMPromise: Promise< typeof import('react-dom') | typeof import('react-dom/client') >; @@ -274,6 +276,7 @@ export class DevAppBuilder { const DevApp = ( <> + {this.rootChildren} diff --git a/packages/e2e-test/CHANGELOG.md b/packages/e2e-test/CHANGELOG.md index fae3f16eb4..a10481c28d 100644 --- a/packages/e2e-test/CHANGELOG.md +++ b/packages/e2e-test/CHANGELOG.md @@ -1,5 +1,21 @@ # e2e-test +## 0.2.34-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/create-app@0.7.6-next.0 + - @backstage/cli-common@0.1.15 + - @backstage/errors@1.2.7 + +## 0.2.33 + +### Patch Changes + +- Updated dependencies + - @backstage/create-app@0.7.5 + ## 0.2.33-next.0 ### Patch Changes diff --git a/packages/e2e-test/package.json b/packages/e2e-test/package.json index 80a2ea7d3f..116c219359 100644 --- a/packages/e2e-test/package.json +++ b/packages/e2e-test/package.json @@ -1,6 +1,6 @@ { "name": "e2e-test", - "version": "0.2.33-next.0", + "version": "0.2.34-next.0", "description": "E2E test for verifying Backstage packages", "backstage": { "role": "cli" diff --git a/packages/eslint-plugin/CHANGELOG.md b/packages/eslint-plugin/CHANGELOG.md index e12e91f97a..65acedd942 100644 --- a/packages/eslint-plugin/CHANGELOG.md +++ b/packages/eslint-plugin/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/eslint-plugin +## 0.2.0-next.0 + +### Minor Changes + +- 926389b: Added `@backstage/no-ui-css-imports-in-non-frontend` rule, which ensures that CSS from `@backstage/ui` is not imported outside of the frontend app. + +## 0.1.12 + +### Patch Changes + +- 5e98e61: Minor doc updates +- a1dae71: Allow frontend plugin to import from another frontend plugin with same plugin id. + + This prevents the ESLint rule from incorrectly flagging these imports in the new frontend system + where plugin override requires cross-plugin imports. + ## 0.1.11 ### Patch Changes diff --git a/packages/eslint-plugin/README.md b/packages/eslint-plugin/README.md index e30b9e5de3..9141770189 100644 --- a/packages/eslint-plugin/README.md +++ b/packages/eslint-plugin/README.md @@ -42,3 +42,4 @@ The following rules are provided by this plugin: | [@backstage/no-undeclared-imports](./docs/rules/no-undeclared-imports.md) | Forbid imports of external packages that have not been declared in the appropriate dependencies field in `package.json`. | | [@backstage/no-top-level-material-ui-4-imports](./docs/rules/no-top-level-material-ui-4-imports.md) | Forbid top level import from Material UI v4 packages. | | [@backstage/no-mixed-plugin-imports](./docs/rules/no-mixed-plugin-imports.md) | Disallow mixed plugin imports. | +| [@backstage/no-ui-css-imports-in-non-frontend](./docs/rules/no-ui-css-imports-in-non-frontend.md) | Ensure that only packages with the `frontend` role can import CSS files from `@backstage/ui`. | diff --git a/packages/eslint-plugin/docs/rules/no-mixed-plugin-imports.md b/packages/eslint-plugin/docs/rules/no-mixed-plugin-imports.md index e8570054e4..26a332d105 100644 --- a/packages/eslint-plugin/docs/rules/no-mixed-plugin-imports.md +++ b/packages/eslint-plugin/docs/rules/no-mixed-plugin-imports.md @@ -1,6 +1,25 @@ # @backstage/no-mixed-plugin-imports -Disallow mixed imports between backstage plugins. +This rule ensures that imports between Backstage plugins are consistent with their intended usage. + +The rule checks the `backstage.role` field in the `package.json` of both the importing and target packages to determine if they are compatible. + +Plugin roles include: + +- `frontend-plugin` (or `frontend` for short) +- `backend-plugin` (or `backend` for short) +- `web-library` (or `react` for short) +- `node-library` (or `node` for short) +- `common-library` (or `common` for short) + +Prohibited imports include: + +- A `frontend` plugin importing directly from another `frontend`, `backend`, or `node` package. Instead, it should import from the corresponding `react` or `common` package. + - With an exception with the new frontend system where frontend plugins with the same plugin id are allowed to import from each other. +- A `backend` plugin importing directly from another `backend`, `frontend`, or `react` package. Instead, it should import from the corresponding `node` or `common` package. +- A `react` package importing from `frontend`, `backend`, or `node` packages. +- A `node` package importing from `frontend`, `backend`, or `react` packages +- A `common` package importing directly from any other plugin package. ## Usage diff --git a/packages/eslint-plugin/docs/rules/no-relative-monorepo-imports.md b/packages/eslint-plugin/docs/rules/no-relative-monorepo-imports.md index 6a41a73fb6..c53deef048 100644 --- a/packages/eslint-plugin/docs/rules/no-relative-monorepo-imports.md +++ b/packages/eslint-plugin/docs/rules/no-relative-monorepo-imports.md @@ -10,7 +10,7 @@ Add the rules as follows, it has no options: "@backstage/no-relative-monorepo-imports": ["error"] ``` -The following patterns are considered files used during development, and only need dependencies to be declared in devDependencies: +The following patterns are considered files used during development, and only need dependencies to be declared in `devDependencies`: ```python !src/** # Any files outside of src are considered dev files diff --git a/packages/eslint-plugin/docs/rules/no-ui-css-imports-in-non-frontend.md b/packages/eslint-plugin/docs/rules/no-ui-css-imports-in-non-frontend.md new file mode 100644 index 0000000000..dd207a43c8 --- /dev/null +++ b/packages/eslint-plugin/docs/rules/no-ui-css-imports-in-non-frontend.md @@ -0,0 +1,50 @@ +# no-ui-css-imports-in-non-frontend + +Ensures that only packages with `backstage.role` set to `"frontend"` can import CSS files from `@backstage/ui`. + +This rule prevents non-frontend packages from accidentally importing global CSS styles from `@backstage/ui`. These CSS files should only be imported by the app, never by plugins, modules or libraries. + +## Rule Details + +This rule checks imports from `@backstage/ui` that end with `.css` and verifies that the importing package has `backstage.role: "frontend"` in its `package.json`. + +If a package does not have a `backstage.role` field defined at all, the import is allowed (the check is skipped). + +Examples of **incorrect** code for this rule: + +```js +// In a package with "backstage.role": "frontend-plugin" +import '@backstage/ui/css/styles.css'; +``` + +```js +// In a package with "backstage.role": "web-library" +import '@backstage/ui/css/styles.css'; +``` + +```js +// In a package with "backstage.role": "backend" +require('@backstage/ui/css/styles.css'); +``` + +Examples of **correct** code for this rule: + +```js +// In a package with "backstage.role": "frontend" +import '@backstage/ui/css/styles.css'; +``` + +```js +// In a package without a "backstage.role" field +import '@backstage/ui/css/styles.css'; +``` + +```js +// Non-CSS imports are allowed in any package +import { Button, Text } from '@backstage/ui'; +``` + +```js +// In a package with "backstage.role": "backend" +import { Button } from '@backstage/ui'; +``` diff --git a/packages/eslint-plugin/docs/rules/no-undeclared-imports.md b/packages/eslint-plugin/docs/rules/no-undeclared-imports.md index 6102c772e6..2eee20e7c9 100644 --- a/packages/eslint-plugin/docs/rules/no-undeclared-imports.md +++ b/packages/eslint-plugin/docs/rules/no-undeclared-imports.md @@ -10,7 +10,7 @@ Add the rules as follows, it has no options: "@backstage/no-undeclared-imports": ["error"] ``` -The following patterns are considered files used during development, and only need dependencies to be declared in devDependencies: +The following patterns are considered files used during development, and only need dependencies to be declared in `devDependencies`: ```python !src/** # Any files outside of src are considered dev files diff --git a/packages/eslint-plugin/index.js b/packages/eslint-plugin/index.js index 58b597a438..d911eee1ef 100644 --- a/packages/eslint-plugin/index.js +++ b/packages/eslint-plugin/index.js @@ -23,6 +23,7 @@ module.exports = { '@backstage/no-relative-monorepo-imports': 'error', '@backstage/no-undeclared-imports': 'error', '@backstage/no-mixed-plugin-imports': 'warn', + '@backstage/no-ui-css-imports-in-non-frontend': 'error', }, }, }, @@ -32,5 +33,6 @@ module.exports = { 'no-undeclared-imports': require('./rules/no-undeclared-imports'), 'no-top-level-material-ui-4-imports': require('./rules/no-top-level-material-ui-4-imports'), 'no-mixed-plugin-imports': require('./rules/no-mixed-plugin-imports'), + 'no-ui-css-imports-in-non-frontend': require('./rules/no-ui-css-imports-in-non-frontend'), }, }; diff --git a/packages/eslint-plugin/lib/getPackages.js b/packages/eslint-plugin/lib/getPackages.js index e90536ff52..68739d358b 100644 --- a/packages/eslint-plugin/lib/getPackages.js +++ b/packages/eslint-plugin/lib/getPackages.js @@ -21,7 +21,7 @@ const manypkg = require('@manypkg/get-packages'); /** * @typedef ExtendedPackage - * @type {import('@manypkg/get-packages').Package & { packageJson: { exports?: Record, files?: Array, backstage?: { inline?: boolean, role?: string } }}} packageJson + * @type {import('@manypkg/get-packages').Package & { packageJson: { exports?: Record, files?: Array, backstage?: { inline?: boolean, role?: string, pluginId?: string } }}} packageJson */ /** diff --git a/packages/eslint-plugin/package.json b/packages/eslint-plugin/package.json index 8a44cfd1e8..f7dc9eebcc 100644 --- a/packages/eslint-plugin/package.json +++ b/packages/eslint-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/eslint-plugin", - "version": "0.1.11", + "version": "0.2.0-next.0", "description": "Backstage ESLint plugin", "publishConfig": { "access": "public" diff --git a/packages/eslint-plugin/rules/no-mixed-plugin-imports.js b/packages/eslint-plugin/rules/no-mixed-plugin-imports.js index b5e79b509e..9f00d38014 100644 --- a/packages/eslint-plugin/rules/no-mixed-plugin-imports.js +++ b/packages/eslint-plugin/rules/no-mixed-plugin-imports.js @@ -64,6 +64,7 @@ module.exports = { messages: { forbidden: '{{sourcePackage}} ({{sourceRole}}) uses forbidden import from {{targetPackage}} ({{targetRole}}).', + useSamePluginId: `Import of {{targetPackage}} ({{targetRole}}) from {{sourceRole}} is forbidden unless you are overriding the plugin, in which case the \`backstage.pluginId\` in {{sourcePackage}}/package.json must be the same as in {{targetPackage}}`, useReactPlugin: 'Use web library {{targetPackage}}-react or common library instead.', useNodePlugin: @@ -146,11 +147,24 @@ module.exports = { } const sourceRole = pkg.packageJson.backstage?.role; + const sourcePluginId = pkg.packageJson.backstage?.pluginId; const targetRole = targetPackage.packageJson.backstage?.role; + const targetPluginId = targetPackage.packageJson.backstage?.pluginId; if (!sourceRole || !targetRole) { return; } + // Allow frontend plugins to import from other frontend plugins with the same pluginId for NFS + if ( + sourceRole === 'frontend-plugin' && + targetRole === 'frontend-plugin' && + sourcePluginId && + targetPluginId && + sourcePluginId === targetPluginId + ) { + return; + } + if ( roleRules.some( rule => @@ -165,6 +179,19 @@ module.exports = { (sourceRole === 'frontend-plugin' || sourceRole === 'web-library') && targetRole === 'frontend-plugin' ) { + suggest.push({ + messageId: 'useSamePluginId', + data: { + targetPackage: targetName, + targetRole: targetRole, + sourcePackage: sourceName, + sourceRole: sourceRole, + }, + /** @param {import('eslint').Rule.RuleFixer} _fixer */ + fix(_fixer) { + // Not a fixable case, just give a suggestion to change plugin id + }, + }); suggest.push({ messageId: 'useReactPlugin', data: { diff --git a/packages/eslint-plugin/rules/no-ui-css-imports-in-non-frontend.js b/packages/eslint-plugin/rules/no-ui-css-imports-in-non-frontend.js new file mode 100644 index 0000000000..b6470bb1d3 --- /dev/null +++ b/packages/eslint-plugin/rules/no-ui-css-imports-in-non-frontend.js @@ -0,0 +1,77 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// @ts-check + +const visitImports = require('../lib/visitImports'); +const getPackages = require('../lib/getPackages'); + +/** @type {import('eslint').Rule.RuleModule} */ +module.exports = { + meta: { + type: 'problem', + messages: { + noCssImport: + 'CSS imports from @backstage/ui are only allowed in packages with backstage.role set to "frontend". Current role: "{{role}}"', + }, + docs: { + description: + 'Ensure that only packages with backstage.role set to "frontend" can import CSS files from @backstage/ui.', + url: 'https://github.com/backstage/backstage/blob/master/packages/eslint-plugin/docs/rules/no-ui-css-imports-in-non-frontend.md', + }, + }, + create(context) { + const packages = getPackages(context.getCwd()); + if (!packages) { + return {}; + } + + const currentPackage = packages.byPath(context.filename); + if (!currentPackage) { + return {}; + } + + return visitImports(context, (node, imp) => { + const isBuiImport = + (imp.type === 'external' || imp.type === 'internal') && + imp.packageName === '@backstage/ui'; + if (!isBuiImport) { + return; + } + + const isCssImport = imp.path?.endsWith('.css'); + if (!isCssImport) { + return; + } + + const backstageRole = currentPackage.packageJson.backstage?.role; + if (!backstageRole) { + // Allow if no role is defined + return; + } + + if (backstageRole !== 'frontend') { + context.report({ + node: node, + messageId: 'noCssImport', + data: { + role: backstageRole, + }, + }); + } + }); + }, +}; diff --git a/packages/eslint-plugin/src/__fixtures__/monorepo/packages/bar-override/package.json b/packages/eslint-plugin/src/__fixtures__/monorepo/packages/bar-override/package.json new file mode 100644 index 0000000000..ff8996a0a2 --- /dev/null +++ b/packages/eslint-plugin/src/__fixtures__/monorepo/packages/bar-override/package.json @@ -0,0 +1,19 @@ +{ + "name": "@internal/bar-override", + "backstage": { + "role": "frontend-plugin", + "pluginId": "bar" + }, + "exports": { + ".": "./src/index.ts", + "./BarPage": "./src/components/Bar.tsx", + "./package.json": "./package.json" + }, + "dependencies": { + "inline-dep": "*", + "react-router": "*" + }, + "devDependencies": { + "react-router-dom": "*" + } +} diff --git a/packages/eslint-plugin/src/__fixtures__/monorepo/packages/bar/package.json b/packages/eslint-plugin/src/__fixtures__/monorepo/packages/bar/package.json index ba55598dc2..bd60f93ce6 100644 --- a/packages/eslint-plugin/src/__fixtures__/monorepo/packages/bar/package.json +++ b/packages/eslint-plugin/src/__fixtures__/monorepo/packages/bar/package.json @@ -1,7 +1,8 @@ { "name": "@internal/bar", "backstage": { - "role": "frontend-plugin" + "role": "frontend-plugin", + "pluginId": "bar" }, "exports": { ".": "./src/index.ts", diff --git a/packages/eslint-plugin/src/__fixtures__/monorepo/packages/foo/package.json b/packages/eslint-plugin/src/__fixtures__/monorepo/packages/foo/package.json index e8e4d4ba70..66be5cecf2 100644 --- a/packages/eslint-plugin/src/__fixtures__/monorepo/packages/foo/package.json +++ b/packages/eslint-plugin/src/__fixtures__/monorepo/packages/foo/package.json @@ -1,7 +1,8 @@ { "name": "@internal/foo", "backstage": { - "role": "frontend-plugin" + "role": "frontend-plugin", + "pluginId": "foo" }, "files": [ "dist", diff --git a/packages/eslint-plugin/src/__fixtures__/monorepo/packages/frontend-pkg/package.json b/packages/eslint-plugin/src/__fixtures__/monorepo/packages/frontend-pkg/package.json new file mode 100644 index 0000000000..9d3fe95884 --- /dev/null +++ b/packages/eslint-plugin/src/__fixtures__/monorepo/packages/frontend-pkg/package.json @@ -0,0 +1,6 @@ +{ + "name": "@internal/frontend-pkg", + "backstage": { + "role": "frontend" + } +} diff --git a/packages/eslint-plugin/src/__fixtures__/monorepo/packages/frontend-plugin-pkg/package.json b/packages/eslint-plugin/src/__fixtures__/monorepo/packages/frontend-plugin-pkg/package.json new file mode 100644 index 0000000000..c944f3dff6 --- /dev/null +++ b/packages/eslint-plugin/src/__fixtures__/monorepo/packages/frontend-plugin-pkg/package.json @@ -0,0 +1,6 @@ +{ + "name": "@internal/frontend-plugin-pkg", + "backstage": { + "role": "frontend-plugin" + } +} diff --git a/packages/eslint-plugin/src/__fixtures__/monorepo/packages/no-role-pkg/package.json b/packages/eslint-plugin/src/__fixtures__/monorepo/packages/no-role-pkg/package.json new file mode 100644 index 0000000000..bee386117c --- /dev/null +++ b/packages/eslint-plugin/src/__fixtures__/monorepo/packages/no-role-pkg/package.json @@ -0,0 +1,3 @@ +{ + "name": "@internal/no-role-pkg" +} diff --git a/packages/eslint-plugin/src/no-ui-css-imports-in-non-frontend.test.ts b/packages/eslint-plugin/src/no-ui-css-imports-in-non-frontend.test.ts new file mode 100644 index 0000000000..373659ae6e --- /dev/null +++ b/packages/eslint-plugin/src/no-ui-css-imports-in-non-frontend.test.ts @@ -0,0 +1,99 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { RuleTester } from 'eslint'; +import path from 'path'; +import rule from '../rules/no-ui-css-imports-in-non-frontend'; + +const RULE = 'no-ui-css-imports-in-non-frontend'; +const FIXTURE = path.resolve(__dirname, '__fixtures__/monorepo'); + +const ERR = (role: string) => ({ + message: `CSS imports from @backstage/ui are only allowed in packages with backstage.role set to "frontend". Current role: "${role}"`, +}); + +// cwd must be restored +const origDir = process.cwd(); +afterAll(() => { + process.chdir(origDir); +}); +process.chdir(FIXTURE); + +const ruleTester = new RuleTester({ + parserOptions: { + sourceType: 'module', + ecmaVersion: 2021, + }, +}); + +ruleTester.run(RULE, rule, { + valid: [ + // Frontend package can import CSS from @backstage/ui + { + code: `import '@backstage/ui/css/styles.css'`, + filename: path.join(FIXTURE, 'packages/frontend-pkg/index.ts'), + }, + { + code: `import '@backstage/ui/css/other.css'`, + filename: path.join(FIXTURE, 'packages/frontend-pkg/index.ts'), + }, + { + code: `require('@backstage/ui/css/styles.css')`, + filename: path.join(FIXTURE, 'packages/frontend-pkg/index.ts'), + }, + // Package without backstage.role can import CSS (skip check) + { + code: `import '@backstage/ui/css/styles.css'`, + filename: path.join(FIXTURE, 'packages/no-role-pkg/index.ts'), + }, + // Non-CSS imports are allowed in any package + { + code: `import { Button } from '@backstage/ui'`, + filename: path.join(FIXTURE, 'packages/frontend-plugin-pkg/index.ts'), + }, + { + code: `import { Text } from '@backstage/ui/components'`, + filename: path.join(FIXTURE, 'packages/frontend-plugin-pkg/index.ts'), + }, + // Imports from other packages are allowed + { + code: `import './styles.css'`, + filename: path.join(FIXTURE, 'packages/frontend-plugin-pkg/index.ts'), + }, + { + code: `import 'some-other-package/styles.css'`, + filename: path.join(FIXTURE, 'packages/frontend-plugin-pkg/index.ts'), + }, + ], + invalid: [ + // Backend package cannot import CSS from @backstage/ui + { + code: `import '@backstage/ui/css/styles.css'`, + filename: path.join(FIXTURE, 'packages/frontend-plugin-pkg/index.ts'), + errors: [ERR('frontend-plugin')], + }, + { + code: `import '@backstage/ui/css/other.css'`, + filename: path.join(FIXTURE, 'packages/frontend-plugin-pkg/index.ts'), + errors: [ERR('frontend-plugin')], + }, + { + code: `require('@backstage/ui/css/styles.css')`, + filename: path.join(FIXTURE, 'packages/frontend-plugin-pkg/index.ts'), + errors: [ERR('frontend-plugin')], + }, + ], +}); diff --git a/packages/frontend-app-api/CHANGELOG.md b/packages/frontend-app-api/CHANGELOG.md index 780e62fbe8..38ec33c33a 100644 --- a/packages/frontend-app-api/CHANGELOG.md +++ b/packages/frontend-app-api/CHANGELOG.md @@ -1,5 +1,31 @@ # @backstage/frontend-app-api +## 0.13.2-next.0 + +### Patch Changes + +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/core-app-api@1.19.2-next.0 + - @backstage/core-plugin-api@1.11.2-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/frontend-plugin-api@0.12.2-next.0 + - @backstage/errors@1.2.7 + - @backstage/frontend-defaults@0.3.3-next.0 + - @backstage/types@1.2.2 + - @backstage/version-bridge@1.0.11 + +## 0.13.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.12.1 + - @backstage/config@1.3.5 + - @backstage/core-app-api@1.19.1 + - @backstage/core-plugin-api@1.11.1 + - @backstage/frontend-defaults@0.3.2 + ## 0.13.1-next.1 ### Patch Changes diff --git a/packages/frontend-app-api/package.json b/packages/frontend-app-api/package.json index 707067b54f..9bdf329f0f 100644 --- a/packages/frontend-app-api/package.json +++ b/packages/frontend-app-api/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/frontend-app-api", - "version": "0.13.1-next.1", + "version": "0.13.2-next.0", "backstage": { "role": "web-library" }, diff --git a/packages/frontend-app-api/src/routing/RouteResolver.ts b/packages/frontend-app-api/src/routing/RouteResolver.ts index 4b2f906e45..4c0ad0e3fa 100644 --- a/packages/frontend-app-api/src/routing/RouteResolver.ts +++ b/packages/frontend-app-api/src/routing/RouteResolver.ts @@ -179,18 +179,31 @@ function resolveBasePath( } export class RouteResolver implements RouteResolutionApi { + private readonly routePaths: Map; + private readonly routeParents: Map; + private readonly routeObjects: BackstageRouteObject[]; + private readonly routeBindings: Map; + private readonly appBasePath: string; // base path without a trailing slash + private readonly routeAliasResolver: RouteAliasResolver; + private readonly routeRefsById: Map; + constructor( - private readonly routePaths: Map, - private readonly routeParents: Map, - private readonly routeObjects: BackstageRouteObject[], - private readonly routeBindings: Map< - ExternalRouteRef, - RouteRef | SubRouteRef - >, - private readonly appBasePath: string, // base path without a trailing slash - private readonly routeAliasResolver: RouteAliasResolver, - private readonly routeRefsById: Map, - ) {} + routePaths: Map, + routeParents: Map, + routeObjects: BackstageRouteObject[], + routeBindings: Map, + appBasePath: string, // base path without a trailing slash + routeAliasResolver: RouteAliasResolver, + routeRefsById: Map, + ) { + this.routePaths = routePaths; + this.routeParents = routeParents; + this.routeObjects = routeObjects; + this.routeBindings = routeBindings; + this.appBasePath = appBasePath; + this.routeAliasResolver = routeAliasResolver; + this.routeRefsById = routeRefsById; + } resolve( anyRouteRef: diff --git a/packages/frontend-app-api/src/tree/instantiateAppNodeTree.test.ts b/packages/frontend-app-api/src/tree/instantiateAppNodeTree.test.ts index 13ec6e8a59..a07fdf352d 100644 --- a/packages/frontend-app-api/src/tree/instantiateAppNodeTree.test.ts +++ b/packages/frontend-app-api/src/tree/instantiateAppNodeTree.test.ts @@ -787,14 +787,8 @@ describe('instantiateAppNodeTree', () => { inputs: { [name in string]: | undefined - | ResolvedExtensionInput< - ExtensionInput - > - | Array< - ResolvedExtensionInput< - ExtensionInput - > - >; + | ResolvedExtensionInput + | Array>; }; }) { return [ diff --git a/packages/frontend-app-api/src/tree/instantiateAppNodeTree.ts b/packages/frontend-app-api/src/tree/instantiateAppNodeTree.ts index 39fc88c19a..04082f8520 100644 --- a/packages/frontend-app-api/src/tree/instantiateAppNodeTree.ts +++ b/packages/frontend-app-api/src/tree/instantiateAppNodeTree.ts @@ -243,20 +243,10 @@ function resolveV1Inputs( } function resolveV2Inputs( - inputMap: { - [inputName in string]: ExtensionInput< - ExtensionDataRef, - { optional: boolean; singleton: boolean } - >; - }, + inputMap: { [inputName in string]: ExtensionInput }, attachments: ReadonlyMap, parentCollector: ErrorCollector<{ node: AppNode }>, -): ResolvedExtensionInputs<{ - [inputName in string]: ExtensionInput< - ExtensionDataRef, - { optional: boolean; singleton: boolean } - >; -}> { +): ResolvedExtensionInputs<{ [inputName in string]: ExtensionInput }> { return mapValues(inputMap, (input, inputName) => { const attachedNodes = attachments.get(inputName) ?? []; const collector = parentCollector.child({ inputName }); @@ -297,12 +287,7 @@ function resolveV2Inputs( collector, ), ); - }) as ResolvedExtensionInputs<{ - [inputName in string]: ExtensionInput< - ExtensionDataRef, - { optional: boolean; singleton: boolean } - >; - }>; + }) as ResolvedExtensionInputs<{ [inputName in string]: ExtensionInput }>; } /** @internal */ diff --git a/packages/frontend-app-api/src/tree/resolveAppNodeSpecs.test.ts b/packages/frontend-app-api/src/tree/resolveAppNodeSpecs.test.ts index 2eacd0ae67..3ccdf5258f 100644 --- a/packages/frontend-app-api/src/tree/resolveAppNodeSpecs.test.ts +++ b/packages/frontend-app-api/src/tree/resolveAppNodeSpecs.test.ts @@ -44,6 +44,7 @@ function makeExt( version: 'v1', id, attachTo: { id: attachId, input: 'default' }, + inputs: {}, disabled: status === 'disabled', toString: expect.any(Function), } as Extension; @@ -61,6 +62,7 @@ function makeExtDef( name, attachTo: { id: attachId, input: 'default' }, disabled: status === 'disabled', + inputs: {}, override: () => ({} as ExtensionDefinition), } as ExtensionDefinition; } diff --git a/packages/frontend-app-api/src/wiring/createSpecializedApp.tsx b/packages/frontend-app-api/src/wiring/createSpecializedApp.tsx index 6c6d75415b..9e75d19454 100644 --- a/packages/frontend-app-api/src/wiring/createSpecializedApp.tsx +++ b/packages/frontend-app-api/src/wiring/createSpecializedApp.tsx @@ -115,11 +115,13 @@ function deduplicateFeatures( // Helps delay callers from reaching out to the API before the app tree has been materialized class AppTreeApiProxy implements AppTreeApi { #routeInfo?: RouteInfo; + private readonly tree: AppTree; + private readonly appBasePath: string; - constructor( - private readonly tree: AppTree, - private readonly appBasePath: string, - ) {} + constructor(tree: AppTree, appBasePath: string) { + this.tree = tree; + this.appBasePath = appBasePath; + } private checkIfInitialized() { if (!this.#routeInfo) { @@ -163,13 +165,16 @@ class RouteResolutionApiProxy implements RouteResolutionApi { #delegate: RouteResolutionApi | undefined; #routeObjects: BackstageRouteObject[] | undefined; + private readonly routeBindings: Map; + private readonly appBasePath: string; + constructor( - private readonly routeBindings: Map< - ExternalRouteRef, - RouteRef | SubRouteRef - >, - private readonly appBasePath: string, - ) {} + routeBindings: Map, + appBasePath: string, + ) { + this.routeBindings = routeBindings; + this.appBasePath = appBasePath; + } resolve( anyRouteRef: diff --git a/packages/frontend-defaults/CHANGELOG.md b/packages/frontend-defaults/CHANGELOG.md index f60f948f3a..651bdeb691 100644 --- a/packages/frontend-defaults/CHANGELOG.md +++ b/packages/frontend-defaults/CHANGELOG.md @@ -1,5 +1,28 @@ # @backstage/frontend-defaults +## 0.3.3-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-app-api@0.13.2-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/core-components@0.18.3-next.0 + - @backstage/frontend-plugin-api@0.12.2-next.0 + - @backstage/errors@1.2.7 + - @backstage/plugin-app@0.3.2-next.0 + +## 0.3.2 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2 + - @backstage/plugin-app@0.3.1 + - @backstage/frontend-plugin-api@0.12.1 + - @backstage/config@1.3.5 + - @backstage/frontend-app-api@0.13.1 + ## 0.3.2-next.1 ### Patch Changes diff --git a/packages/frontend-defaults/package.json b/packages/frontend-defaults/package.json index ab8f88b653..3fcbb2c990 100644 --- a/packages/frontend-defaults/package.json +++ b/packages/frontend-defaults/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/frontend-defaults", - "version": "0.3.2-next.1", + "version": "0.3.3-next.0", "backstage": { "role": "web-library" }, diff --git a/packages/frontend-defaults/src/createPublicSignInApp.test.tsx b/packages/frontend-defaults/src/createPublicSignInApp.test.tsx index 7e12499a1c..e80d058140 100644 --- a/packages/frontend-defaults/src/createPublicSignInApp.test.tsx +++ b/packages/frontend-defaults/src/createPublicSignInApp.test.tsx @@ -96,6 +96,7 @@ describe('createPublicSignInApp', () => {
    ; readonly inputs: { @@ -67,25 +67,17 @@ export const OpaqueExtensionDefinition = OpaqueType.create<{ readonly kind?: string; readonly namespace?: string; readonly name?: string; - readonly attachTo: ExtensionAttachToSpec; + readonly attachTo: ExtensionDefinitionAttachTo; readonly disabled: boolean; readonly configSchema?: PortableSchema; - readonly inputs: { - [inputName in string]: ExtensionInput< - ExtensionDataRef, - { optional: boolean; singleton: boolean } - >; - }; + readonly inputs: { [inputName in string]: ExtensionInput }; readonly output: Array; factory(context: { node: AppNode; apis: ApiHolder; config: object; inputs: ResolvedExtensionInputs<{ - [inputName in string]: ExtensionInput< - ExtensionDataRef, - { optional: boolean; singleton: boolean } - >; + [inputName in string]: ExtensionInput; }>; }): Iterable>; }; diff --git a/packages/ui/src/components/Icon/provider.tsx b/packages/frontend-internal/src/wiring/InternalExtensionInput.ts similarity index 54% rename from packages/ui/src/components/Icon/provider.tsx rename to packages/frontend-internal/src/wiring/InternalExtensionInput.ts index 7f78096edb..b816890f1a 100644 --- a/packages/ui/src/components/Icon/provider.tsx +++ b/packages/frontend-internal/src/wiring/InternalExtensionInput.ts @@ -14,20 +14,23 @@ * limitations under the License. */ -import { icons } from './icons'; -import { IconContext } from './context'; -import type { IconProviderProps } from './types'; +import { ExtensionInput } from '@backstage/frontend-plugin-api'; +import { OpaqueType } from '@internal/opaque'; -/** @public */ -export const IconProvider = (props: IconProviderProps) => { - const { children, overrides } = props; - - // Merge provided overrides with default icons - const combinedIcons = { ...icons, ...overrides }; - - return ( - - {children} - - ); +export type ExtensionInputContext = { + input: string; + kind?: string; + name?: string; }; + +export const OpaqueExtensionInput = OpaqueType.create<{ + public: ExtensionInput; + versions: { + readonly version: undefined; + readonly context?: ExtensionInputContext; + withContext(context: ExtensionInputContext): ExtensionInput; + }; +}>({ + type: '@backstage/ExtensionInput', + versions: [undefined], +}); diff --git a/packages/frontend-internal/src/wiring/index.ts b/packages/frontend-internal/src/wiring/index.ts index b61294cb1f..1efcc7d16d 100644 --- a/packages/frontend-internal/src/wiring/index.ts +++ b/packages/frontend-internal/src/wiring/index.ts @@ -15,6 +15,10 @@ */ export { createExtensionDataContainer } from './createExtensionDataContainer'; -export { OpaqueSwappableComponentRef } from './InternalSwappableComponentRef'; export { OpaqueExtensionDefinition } from './InternalExtensionDefinition'; +export { + OpaqueExtensionInput, + type ExtensionInputContext, +} from './InternalExtensionInput'; export { OpaqueFrontendPlugin } from './InternalFrontendPlugin'; +export { OpaqueSwappableComponentRef } from './InternalSwappableComponentRef'; diff --git a/packages/frontend-plugin-api/CHANGELOG.md b/packages/frontend-plugin-api/CHANGELOG.md index e12aedb385..9c97055ceb 100644 --- a/packages/frontend-plugin-api/CHANGELOG.md +++ b/packages/frontend-plugin-api/CHANGELOG.md @@ -1,5 +1,63 @@ # @backstage/frontend-plugin-api +## 0.12.2-next.2 + +### Patch Changes + +- 7c6a66d: Added support for plugin-relative `attachTo` declarations for extension definitions. This allows for the creation of extension and extension blueprints that attach to other extensions of a particular `kind` in the same plugin, rather than needing to provide the exact extension ID. This is particularly useful when wanting to provide extension blueprints with a built-in hierarchy where the extensions created from one blueprint attach to extensions created from the other blueprint, for example: + + ```ts + // kind: 'tabbed-page' + const parentPage = TabbedPageBlueprint.make({ + params: {....} + }) + // attachTo: { kind: 'tabbed-page', input: 'tabs' } + const child1 = TabContentBlueprint.make({ + name: 'tab1', + params: {....} + }) + ``` + +- Updated dependencies + - @backstage/core-components@0.18.3-next.2 + +## 0.12.2-next.1 + +### Patch Changes + +- 878c251: Updated to `ExtensionInput` to make all type parameters optional. +- Updated dependencies + - @backstage/core-components@0.18.3-next.1 + - @backstage/core-plugin-api@1.11.2-next.1 + +## 0.12.2-next.0 + +### Patch Changes + +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/core-plugin-api@1.11.2-next.0 + - @backstage/core-components@0.18.3-next.0 + - @backstage/types@1.2.2 + - @backstage/version-bridge@1.0.11 + +## 0.12.1 + +### Patch Changes + +- 8ed53eb: Added `coreExtensionData.title`, especially useful for creating extensible layout with tabbed pages, but available for use for other cases too. +- Updated dependencies + - @backstage/core-components@0.18.2 + - @backstage/core-plugin-api@1.11.1 + +## 0.12.1-next.2 + +### Patch Changes + +- 8ed53eb: Added `coreExtensionData.title`, especially useful for creating extensible layout with tabbed pages, but available for use for other cases too. +- Updated dependencies + - @backstage/core-components@0.18.2-next.3 + ## 0.12.1-next.1 ### Patch Changes diff --git a/packages/frontend-plugin-api/package.json b/packages/frontend-plugin-api/package.json index 15e557101c..fec762ff63 100644 --- a/packages/frontend-plugin-api/package.json +++ b/packages/frontend-plugin-api/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/frontend-plugin-api", - "version": "0.12.1-next.1", + "version": "0.12.2-next.2", "backstage": { "role": "web-library" }, diff --git a/packages/frontend-plugin-api/report.api.md b/packages/frontend-plugin-api/report.api.md index f9e2336408..c3dd12119f 100644 --- a/packages/frontend-plugin-api/report.api.md +++ b/packages/frontend-plugin-api/report.api.md @@ -259,7 +259,7 @@ export interface AppNodeInstance { // @public export interface AppNodeSpec { // (undocumented) - readonly attachTo: ExtensionAttachToSpec; + readonly attachTo: ExtensionAttachTo; // (undocumented) readonly config?: unknown; // (undocumented) @@ -377,6 +377,7 @@ export interface ConfigurableExtensionDataRef< // @public (undocumented) export const coreExtensionData: { + title: ConfigurableExtensionDataRef; reactElement: ConfigurableExtensionDataRef< JSX_3.Element, 'core.reactElement', @@ -398,13 +399,7 @@ export { createApiRef }; export function createExtension< UOutput extends ExtensionDataRef, TInputs extends { - [inputName in string]: ExtensionInput< - ExtensionDataRef, - { - optional: boolean; - singleton: boolean; - } - >; + [inputName in string]: ExtensionInput; }, TConfigSchema extends { [key: string]: (zImpl: typeof z) => z.ZodType; @@ -412,6 +407,7 @@ export function createExtension< UFactoryOutput extends ExtensionDataValue, const TKind extends string | undefined = undefined, const TName extends string | undefined = undefined, + UParentInputs extends ExtensionDataRef = ExtensionDataRef, >( options: CreateExtensionOptions< TKind, @@ -419,7 +415,8 @@ export function createExtension< UOutput, TInputs, TConfigSchema, - UFactoryOutput + UFactoryOutput, + UParentInputs >, ): ExtensionDefinition<{ config: string extends keyof TConfigSchema @@ -452,19 +449,14 @@ export function createExtensionBlueprint< TParams extends object | ExtensionBlueprintDefineParams, UOutput extends ExtensionDataRef, TInputs extends { - [inputName in string]: ExtensionInput< - ExtensionDataRef, - { - optional: boolean; - singleton: boolean; - } - >; + [inputName in string]: ExtensionInput; }, TConfigSchema extends { [key in string]: (zImpl: typeof z) => z.ZodType; }, UFactoryOutput extends ExtensionDataValue, TKind extends string, + UParentInputs extends ExtensionDataRef, TDataRefs extends { [name in string]: ExtensionDataRef; } = never, @@ -476,7 +468,8 @@ export function createExtensionBlueprint< TInputs, TConfigSchema, UFactoryOutput, - TDataRefs + TDataRefs, + UParentInputs >, ): ExtensionBlueprint<{ kind: TKind; @@ -510,13 +503,7 @@ export type CreateExtensionBlueprintOptions< TParams extends object | ExtensionBlueprintDefineParams, UOutput extends ExtensionDataRef, TInputs extends { - [inputName in string]: ExtensionInput< - ExtensionDataRef, - { - optional: boolean; - singleton: boolean; - } - >; + [inputName in string]: ExtensionInput; }, TConfigSchema extends { [key in string]: (zImpl: typeof z) => z.ZodType; @@ -525,9 +512,11 @@ export type CreateExtensionBlueprintOptions< TDataRefs extends { [name in string]: ExtensionDataRef; }, + UParentInputs extends ExtensionDataRef, > = { kind: TKind; - attachTo: ExtensionAttachToSpec; + attachTo: ExtensionDefinitionAttachTo & + VerifyExtensionAttachTo; disabled?: boolean; inputs?: TInputs; output: Array; @@ -600,22 +589,18 @@ export type CreateExtensionOptions< TName extends string | undefined, UOutput extends ExtensionDataRef, TInputs extends { - [inputName in string]: ExtensionInput< - ExtensionDataRef, - { - optional: boolean; - singleton: boolean; - } - >; + [inputName in string]: ExtensionInput; }, TConfigSchema extends { [key: string]: (zImpl: typeof z) => z.ZodType; }, UFactoryOutput extends ExtensionDataValue, + UParentInputs extends ExtensionDataRef, > = { kind?: TKind; name?: TName; - attachTo: ExtensionAttachToSpec; + attachTo: ExtensionDefinitionAttachTo & + VerifyExtensionAttachTo; disabled?: boolean; inputs?: TInputs; output: Array; @@ -862,7 +847,7 @@ export interface Extension { } // @public (undocumented) -export type ExtensionAttachToSpec = +export type ExtensionAttachTo = | { id: string; input: string; @@ -872,6 +857,9 @@ export type ExtensionAttachToSpec = input: string; }>; +// @public @deprecated (undocumented) +export type ExtensionAttachToSpec = ExtensionAttachTo; + // @public (undocumented) export interface ExtensionBlueprint< T extends ExtensionBlueprintParameters = ExtensionBlueprintParameters, @@ -882,9 +870,11 @@ export interface ExtensionBlueprint< make< TName extends string | undefined, TParamsInput extends AnyParamsInput>, + UParentInputs extends ExtensionDataRef, >(args: { name?: TName; - attachTo?: ExtensionAttachToSpec; + attachTo?: ExtensionDefinitionAttachTo & + VerifyExtensionAttachTo, UParentInputs>; disabled?: boolean; params: TParamsInput extends ExtensionBlueprintDefineParams ? TParamsInput @@ -908,17 +898,18 @@ export interface ExtensionBlueprint< UFactoryOutput extends ExtensionDataValue, UNewOutput extends ExtensionDataRef, TExtraInputs extends { - [inputName in string]: ExtensionInput< - ExtensionDataRef, - { - optional: boolean; - singleton: boolean; - } - >; + [inputName in string]: ExtensionInput; }, + UParentInputs extends ExtensionDataRef, >(args: { name?: TName; - attachTo?: ExtensionAttachToSpec; + attachTo?: ExtensionDefinitionAttachTo & + VerifyExtensionAttachTo< + ExtensionDataRef extends UNewOutput + ? NonNullable + : UNewOutput, + UParentInputs + >; disabled?: boolean; inputs?: TExtraInputs & { [KName in keyof T['inputs']]?: `Error: Input '${KName & @@ -1007,13 +998,7 @@ export type ExtensionBlueprintParameters = { }; output?: ExtensionDataRef; inputs?: { - [KName in string]: ExtensionInput< - ExtensionDataRef, - { - optional: boolean; - singleton: boolean; - } - >; + [KName in string]: ExtensionInput; }; dataRefs?: { [name in string]: ExtensionDataRef; @@ -1108,6 +1093,11 @@ export type ExtensionDefinition< > = { $$type: '@backstage/ExtensionDefinition'; readonly T: T; + readonly inputs: { + [K in keyof T['inputs']]: ExtensionInput< + T['inputs'][K] extends ExtensionInput ? IData : never + >; + }; override< TExtensionConfigSchema extends { [key in string]: (zImpl: typeof z) => z.ZodType; @@ -1115,19 +1105,20 @@ export type ExtensionDefinition< UFactoryOutput extends ExtensionDataValue, UNewOutput extends ExtensionDataRef, TExtraInputs extends { - [inputName in string]: ExtensionInput< - ExtensionDataRef, - { - optional: boolean; - singleton: boolean; - } - >; + [inputName in string]: ExtensionInput; }, TParamsInput extends AnyParamsInput_2>, + UParentInputs extends ExtensionDataRef, >( args: Expand< { - attachTo?: ExtensionAttachToSpec; + attachTo?: ExtensionDefinitionAttachTo & + VerifyExtensionAttachTo< + ExtensionDataRef extends UNewOutput + ? NonNullable + : UNewOutput, + UParentInputs + >; disabled?: boolean; inputs?: TExtraInputs & { [KName in keyof T['inputs']]?: `Error: Input '${KName & @@ -1209,6 +1200,41 @@ export type ExtensionDefinition< }>; }; +// @public +export type ExtensionDefinitionAttachTo< + UParentInputs extends ExtensionDataRef = ExtensionDataRef, +> = + | { + id: string; + input: string; + relative?: never; + } + | { + relative: { + kind?: string; + name?: string; + }; + input: string; + id?: never; + } + | ExtensionInput + | Array< + | { + id: string; + input: string; + relative?: never; + } + | { + relative: { + kind?: string; + name?: string; + }; + input: string; + id?: never; + } + | ExtensionInput + >; + // @public (undocumented) export type ExtensionDefinitionParameters = { kind?: string; @@ -1221,13 +1247,7 @@ export type ExtensionDefinitionParameters = { }; output?: ExtensionDataRef; inputs?: { - [KName in string]: ExtensionInput< - ExtensionDataRef, - { - optional: boolean; - singleton: boolean; - } - >; + [KName in string]: ExtensionInput; }; params?: object | ExtensionBlueprintDefineParams; }; @@ -1252,20 +1272,23 @@ export interface ExtensionInput< { optional?: true; } - >, + > = ExtensionDataRef, TConfig extends { singleton: boolean; optional: boolean; + } = { + singleton: boolean; + optional: boolean; }, > { // (undocumented) - $$type: '@backstage/ExtensionInput'; + readonly $$type: '@backstage/ExtensionInput'; // (undocumented) - config: TConfig; + readonly config: TConfig; // (undocumented) - extensionData: Array; + readonly extensionData: Array; // (undocumented) - replaces?: Array<{ + readonly replaces?: Array<{ id: string; input: string; }>; @@ -1624,18 +1647,17 @@ export const Progress: { export type ProgressProps = {}; // @public -export type ResolvedExtensionInput< - TExtensionInput extends ExtensionInput, -> = TExtensionInput['extensionData'] extends Array - ? { - node: AppNode; - } & ExtensionDataContainer - : never; +export type ResolvedExtensionInput = + TExtensionInput['extensionData'] extends Array + ? { + node: AppNode; + } & ExtensionDataContainer + : never; // @public export type ResolvedExtensionInputs< TInputs extends { - [name in string]: ExtensionInput; + [name in string]: ExtensionInput; }, > = { [InputName in keyof TInputs]: false extends TInputs[InputName]['config']['singleton'] diff --git a/packages/frontend-plugin-api/src/analytics/Tracker.ts b/packages/frontend-plugin-api/src/analytics/Tracker.ts index dd0d770354..5aae3ec19d 100644 --- a/packages/frontend-plugin-api/src/analytics/Tracker.ts +++ b/packages/frontend-plugin-api/src/analytics/Tracker.ts @@ -65,13 +65,18 @@ const globalEvents = getOrCreateGlobalSingleton( export const routableExtensionRenderedEvent = '_ROUTABLE-EXTENSION-RENDERED'; export class Tracker implements AnalyticsTracker { + private readonly analyticsApi: AnalyticsApi; + private context: AnalyticsContextValue; + constructor( - private readonly analyticsApi: AnalyticsApi, - private context: AnalyticsContextValue = { + analyticsApi: AnalyticsApi, + context: AnalyticsContextValue = { pluginId: 'root', extensionId: 'App', }, ) { + this.analyticsApi = analyticsApi; + this.context = context; // Only register a single beforeunload event across all trackers. if (!globalEvents.beforeUnloadRegistered) { // Before the page unloads, attempt to capture any deferred navigation diff --git a/packages/frontend-plugin-api/src/apis/definitions/AppTreeApi.ts b/packages/frontend-plugin-api/src/apis/definitions/AppTreeApi.ts index f4fd2f3a6f..6c318d782a 100644 --- a/packages/frontend-plugin-api/src/apis/definitions/AppTreeApi.ts +++ b/packages/frontend-plugin-api/src/apis/definitions/AppTreeApi.ts @@ -15,12 +15,8 @@ */ import { createApiRef } from '@backstage/core-plugin-api'; -import { - FrontendPlugin, - Extension, - ExtensionDataRef, - ExtensionAttachToSpec, -} from '../../wiring'; +import { FrontendPlugin, Extension, ExtensionDataRef } from '../../wiring'; +import { ExtensionAttachTo } from '../../wiring/resolveExtensionDefinition'; /** * The specification for this {@link AppNode} in the {@link AppTree}. @@ -33,7 +29,7 @@ import { */ export interface AppNodeSpec { readonly id: string; - readonly attachTo: ExtensionAttachToSpec; + readonly attachTo: ExtensionAttachTo; readonly extension: Extension; readonly disabled: boolean; readonly config?: unknown; diff --git a/packages/frontend-plugin-api/src/blueprints/ApiBlueprint.test.ts b/packages/frontend-plugin-api/src/blueprints/ApiBlueprint.test.ts index 471e933b12..988c5a15c0 100644 --- a/packages/frontend-plugin-api/src/blueprints/ApiBlueprint.test.ts +++ b/packages/frontend-plugin-api/src/blueprints/ApiBlueprint.test.ts @@ -203,10 +203,16 @@ describe('ApiBlueprint', () => { "optional": false, "singleton": false, }, + "context": { + "input": "test", + "kind": "api", + "name": "test", + }, "extensionData": [ [Function], ], "replaces": undefined, + "withContext": [Function], }, }, "kind": "api", diff --git a/packages/frontend-plugin-api/src/blueprints/AppRootWrapperBlueprint.test.tsx b/packages/frontend-plugin-api/src/blueprints/AppRootWrapperBlueprint.test.tsx index b4ad8ed2d2..0f26af3243 100644 --- a/packages/frontend-plugin-api/src/blueprints/AppRootWrapperBlueprint.test.tsx +++ b/packages/frontend-plugin-api/src/blueprints/AppRootWrapperBlueprint.test.tsx @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - +import { Fragment } from 'react'; import { AppRootWrapperBlueprint } from './AppRootWrapperBlueprint'; import { screen, waitFor } from '@testing-library/react'; import { @@ -21,7 +21,7 @@ import { createExtension, createExtensionInput, } from '../wiring'; -import { renderInTestApp } from '@backstage/frontend-test-utils'; +import { renderTestApp } from '@backstage/frontend-test-utils'; describe('AppRootWrapperBlueprint', () => { it('should return an extension with sensible defaults', () => { @@ -63,7 +63,7 @@ describe('AppRootWrapperBlueprint', () => { }, }); - renderInTestApp(
    , { extensions: [extension] }); + renderTestApp({ extensions: [extension] }); await waitFor(() => expect(screen.getByText('Hello')).toBeInTheDocument()); }); @@ -83,16 +83,18 @@ describe('AppRootWrapperBlueprint', () => { component: ({ children }) => (
    {children} - {inputs.children.flatMap(c => - c.get(coreExtensionData.reactElement), - )} + {inputs.children.flatMap((c, index) => ( + + {c.get(coreExtensionData.reactElement)} + + ))}
    ), }); }, }); - renderInTestApp(
    , { + renderTestApp({ extensions: [ extension, createExtension({ diff --git a/packages/frontend-plugin-api/src/blueprints/SwappableComponentBlueprint.test.tsx b/packages/frontend-plugin-api/src/blueprints/SwappableComponentBlueprint.test.tsx index fae4bf7ebf..63436308ed 100644 --- a/packages/frontend-plugin-api/src/blueprints/SwappableComponentBlueprint.test.tsx +++ b/packages/frontend-plugin-api/src/blueprints/SwappableComponentBlueprint.test.tsx @@ -13,11 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { renderInTestApp } from '@backstage/frontend-test-utils'; +import { renderTestApp } from '@backstage/frontend-test-utils'; import { createSwappableComponent } from '../components'; import { SwappableComponentBlueprint } from './SwappableComponentBlueprint'; import { PageBlueprint } from './PageBlueprint'; -import { waitFor, screen } from '@testing-library/react'; +import { screen } from '@testing-library/react'; describe('SwappableComponentBlueprint', () => { it('should allow defining a component override for a component ref', () => { @@ -48,21 +48,19 @@ describe('SwappableComponentBlueprint', () => { loader: () => (props: { hello: string }) =>
    {props.hello}
    , }); - renderInTestApp(
    , { + renderTestApp({ extensions: [ PageBlueprint.make({ params: define => define({ - // todo(blam): there's a bug that this path cannot be `/`? - path: '/test', + path: '/', loader: async () => , }), }), ], - initialRouteEntries: ['/test'], }); - await waitFor(() => expect(screen.getByText('test!')).toBeInTheDocument()); + await expect(screen.findByText('test!')).resolves.toBeInTheDocument(); }); it('should render a component ref without a default implementation', async () => { @@ -70,22 +68,21 @@ describe('SwappableComponentBlueprint', () => { id: 'test.component', }); - renderInTestApp(
    , { + renderTestApp({ extensions: [ PageBlueprint.make({ params: define => define({ - path: '/test', + path: '/', loader: async () => , }), }), ], - initialRouteEntries: ['/test'], }); - await waitFor(() => - expect(screen.getByTestId('test.component')).toBeInTheDocument(), - ); + await expect( + screen.findByTestId('test.component'), + ).resolves.toBeInTheDocument(); }); it('should render a component ref with an async loader implementation', async () => { @@ -95,21 +92,20 @@ describe('SwappableComponentBlueprint', () => {
    {props.hello}
    , }); - renderInTestApp(
    , { + renderTestApp({ extensions: [ PageBlueprint.make({ params: define => define({ // todo(blam): there's a bug that this path cannot be `/`? - path: '/test', + path: '/', loader: async () => , }), }), ], - initialRouteEntries: ['/test'], }); - await waitFor(() => expect(screen.getByText('test!')).toBeInTheDocument()); + await expect(screen.findByText('test!')).resolves.toBeInTheDocument(); }); it('should render a component ref with an async loader implementation and prop transform', async () => { @@ -120,22 +116,18 @@ describe('SwappableComponentBlueprint', () => { transformProps: ({ hello }) => ({ hello: `tr ${hello}` }), }); - renderInTestApp(
    , { + renderTestApp({ extensions: [ PageBlueprint.make({ params: define => define({ - // todo(blam): there's a bug that this path cannot be `/`? - path: '/test', + path: '/', loader: async () => , }), }), ], - initialRouteEntries: ['/test'], }); - await waitFor(() => - expect(screen.getByText('tr test!')).toBeInTheDocument(), - ); + await expect(screen.findByText('tr test!')).resolves.toBeInTheDocument(); }); }); diff --git a/packages/frontend-plugin-api/src/routing/ExternalRouteRef.ts b/packages/frontend-plugin-api/src/routing/ExternalRouteRef.ts index 6ce36707c0..3de276f85c 100644 --- a/packages/frontend-plugin-api/src/routing/ExternalRouteRef.ts +++ b/packages/frontend-plugin-api/src/routing/ExternalRouteRef.ts @@ -71,13 +71,17 @@ class ExternalRouteRefImpl implements InternalExternalRouteRef { readonly $$type = '@backstage/ExternalRouteRef' as any; + readonly params: string[]; + readonly defaultTarget: string | undefined; constructor( - readonly params: string[] = [], - readonly defaultTarget: string | undefined, + params: string[] = [], + defaultTarget: string | undefined, creationSite: string, ) { super(params, creationSite); + this.params = params; + this.defaultTarget = defaultTarget; } getDefaultTarget() { diff --git a/packages/frontend-plugin-api/src/wiring/coreExtensionData.ts b/packages/frontend-plugin-api/src/wiring/coreExtensionData.ts index 18466d8196..b778539101 100644 --- a/packages/frontend-plugin-api/src/wiring/coreExtensionData.ts +++ b/packages/frontend-plugin-api/src/wiring/coreExtensionData.ts @@ -20,6 +20,7 @@ import { createExtensionDataRef } from './createExtensionDataRef'; /** @public */ export const coreExtensionData = { + title: createExtensionDataRef().with({ id: 'core.title' }), reactElement: createExtensionDataRef().with({ id: 'core.reactElement', }), diff --git a/packages/frontend-plugin-api/src/wiring/createExtension.test.ts b/packages/frontend-plugin-api/src/wiring/createExtension.test.ts index 59a81f1b59..4996fbc535 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtension.test.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtension.test.ts @@ -216,6 +216,167 @@ describe('createExtension', () => { ); }); + it('should create an extension with relative attachment points', () => { + const extension = createExtension({ + attachTo: [ + { relative: {}, input: 'tabs' }, + { relative: { kind: 'page' }, input: 'tabs' }, + { relative: { name: 'index' }, input: 'tabs' }, + { relative: { kind: 'page', name: 'index' }, input: 'tabs' }, + ], + output: [stringDataRef], + factory: () => [stringDataRef('bar')], + }); + expect(String(extension)).toBe( + 'ExtensionDefinition{attachTo=@tabs+page:@tabs+/index@tabs+page:/index@tabs}', + ); + }); + + it('should create an extension with relative attachment points by reference', () => { + const baseOpts = { + attachTo: { id: 'root', input: 'children' }, + inputs: { + tabs: createExtensionInput([stringDataRef]), + }, + output: [], + factory: () => [], + }; + const parent1 = createExtension({ + ...baseOpts, + }); + const parent2 = createExtension({ + ...baseOpts, + kind: 'page', + }); + const parent3 = createExtension({ + ...baseOpts, + name: 'index', + }); + const parent4 = createExtension({ + ...baseOpts, + inputs: {}, + kind: 'page', + name: 'index', + }).override({ + inputs: { + otherTabs: createExtensionInput([stringDataRef]), + }, + factory: () => [], + }); + const extension = createExtension({ + attachTo: [ + parent1.inputs.tabs, + parent2.inputs.tabs, + parent3.inputs.tabs, + parent4.inputs.otherTabs, + ], + output: [stringDataRef], + factory: () => [stringDataRef('bar')], + }); + expect(String(extension)).toBe( + 'ExtensionDefinition{attachTo=@tabs+page:@tabs+/index@tabs+page:/index@otherTabs}', + ); + const overrdeExtension = extension.override({ + attachTo: [ + parent2.inputs.tabs, + parent3.inputs.tabs, + parent4.inputs.otherTabs, + ], + }); + expect(String(overrdeExtension)).toBe( + 'ExtensionDefinition{attachTo=page:@tabs+/index@tabs+page:/index@otherTabs}', + ); + }); + + it('should provide type safe attachments by reference', () => { + const parent = createExtension({ + attachTo: { id: 'root', input: 'children' }, + inputs: { + string: createExtensionInput([stringDataRef]), + stringOpt: createExtensionInput([stringDataRef.optional()]), + number: createExtensionInput([numberDataRef]), + numberOpt: createExtensionInput([numberDataRef.optional()]), + both: createExtensionInput([stringDataRef, numberDataRef]), + bothOptString: createExtensionInput([ + stringDataRef.optional(), + numberDataRef, + ]), + bothOptNumber: createExtensionInput([ + stringDataRef, + numberDataRef.optional(), + ]), + bothOpt: createExtensionInput([ + stringDataRef.optional(), + numberDataRef.optional(), + ]), + }, + output: [], + factory: () => [], + }); + const strOutExt = createExtension({ + attachTo: parent.inputs.string, + output: [stringDataRef], + factory: () => [stringDataRef('str')], + }); + strOutExt.override({ + attachTo: parent.inputs.string, + }); + strOutExt.override({ + attachTo: parent.inputs.stringOpt, + }); + strOutExt.override({ + // @ts-expect-error + attachTo: parent.inputs.number, + }); + strOutExt.override({ + attachTo: parent.inputs.numberOpt, + }); + strOutExt.override({ + // @ts-expect-error + attachTo: parent.inputs.both, + }); + strOutExt.override({ + attachTo: parent.inputs.bothOptNumber, + }); + strOutExt.override({ + // @ts-expect-error + attachTo: parent.inputs.bothOptString, + }); + strOutExt.override({ + attachTo: parent.inputs.bothOpt, + }); + const numberOutExt = createExtension({ + // @ts-expect-error + attachTo: parent.inputs.string, + output: [numberDataRef], + factory: () => [numberDataRef(1)], + }); + numberOutExt.override({ + // @ts-expect-error + attachTo: parent.inputs.string, + }); + numberOutExt.override({ + attachTo: parent.inputs.number, + }); + const bothOutExt = createExtension({ + attachTo: parent.inputs.both, + output: [numberDataRef, stringDataRef], + factory: () => [numberDataRef(1), stringDataRef('str')], + }); + // TODO(Rugvip): Potentially encapsulate the parent input type in the extension, until then we can't verify this + bothOutExt.override({ + output: [numberDataRef.optional(), stringDataRef], + factory: () => [stringDataRef('str')], + }); + bothOutExt.override({ + // @ts-expect-error + attachTo: parent.inputs.both, + output: [numberDataRef.optional(), stringDataRef], + factory: () => [stringDataRef('str')], + }); + expect('types').not.toBe('broken'); + }); + it('should create an extension with input', () => { const extension = createExtension({ attachTo: { id: 'root', input: 'default' }, diff --git a/packages/frontend-plugin-api/src/wiring/createExtension.ts b/packages/frontend-plugin-api/src/wiring/createExtension.ts index 0c0b75a572..0fec40f4c9 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtension.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtension.ts @@ -20,7 +20,10 @@ import { ResolvedInputValueOverrides, resolveInputOverrides, } from './resolveInputOverrides'; -import { createExtensionDataContainer } from '@internal/frontend'; +import { + createExtensionDataContainer, + OpaqueExtensionInput, +} from '@internal/frontend'; import { ExtensionDataRef, ExtensionDataValue } from './createExtensionDataRef'; import { ExtensionInput } from './createExtensionInput'; import { z } from 'zod'; @@ -44,13 +47,12 @@ export const ctxParamsSymbol = Symbol('params'); * Convert a single extension input into a matching resolved input. * @public */ -export type ResolvedExtensionInput< - TExtensionInput extends ExtensionInput, -> = TExtensionInput['extensionData'] extends Array - ? { - node: AppNode; - } & ExtensionDataContainer - : never; +export type ResolvedExtensionInput = + TExtensionInput['extensionData'] extends Array + ? { + node: AppNode; + } & ExtensionDataContainer + : never; /** * Converts an extension input map into a matching collection of resolved inputs. @@ -58,7 +60,7 @@ export type ResolvedExtensionInput< */ export type ResolvedExtensionInputs< TInputs extends { - [name in string]: ExtensionInput; + [name in string]: ExtensionInput; }, > = { [InputName in keyof TInputs]: false extends TInputs[InputName]['config']['singleton'] @@ -91,50 +93,104 @@ type JoinStringUnion< : JoinStringUnion : TResult; +/** @ignore */ +export type RequiredExtensionIds = + UExtensionData extends any + ? UExtensionData['config']['optional'] extends true + ? never + : UExtensionData['id'] + : never; + /** @ignore */ export type VerifyExtensionFactoryOutput< UDeclaredOutput extends ExtensionDataRef, UFactoryOutput extends ExtensionDataValue, -> = ( - UDeclaredOutput extends any - ? UDeclaredOutput['config']['optional'] extends true - ? never - : UDeclaredOutput['id'] - : never -) extends infer IRequiredOutputIds - ? [IRequiredOutputIds] extends [UFactoryOutput['id']] - ? [UFactoryOutput['id']] extends [UDeclaredOutput['id']] - ? {} - : `Error: The extension factory has undeclared output(s): ${JoinStringUnion< - Exclude - >}` - : `Error: The extension factory is missing the following output(s): ${JoinStringUnion< - Exclude +> = [RequiredExtensionIds] extends [UFactoryOutput['id']] + ? [UFactoryOutput['id']] extends [UDeclaredOutput['id']] + ? {} + : `Error: The extension factory has undeclared output(s): ${JoinStringUnion< + Exclude >}` - : never; + : `Error: The extension factory is missing the following output(s): ${JoinStringUnion< + Exclude, UFactoryOutput['id']> + >}`; -/** @public */ -export type ExtensionAttachToSpec = - | { id: string; input: string } - | Array<{ id: string; input: string }>; +/** @ignore */ +export type VerifyExtensionAttachTo< + UOutput extends ExtensionDataRef, + UParentInput extends ExtensionDataRef, +> = ExtensionDataRef extends UParentInput + ? {} + : [RequiredExtensionIds] extends [RequiredExtensionIds] + ? {} + : `Error: This parent extension input requires the following extension data, but it is not declared as guaranteed output of this extension: ${JoinStringUnion< + Exclude, RequiredExtensionIds> + >}`; + +/** + * Specifies where an extension should attach in the extension tree. + * + * @remarks + * + * A standard attachment point declaration will specify the ID of the parent extension, as well as the name of the input to attach to. + * + * There are three more advanced forms that are available for more complex use-cases: + * + * 1. Relative attachment points: using the `relative` property instead of `id`, the attachment point is resolved relative to the current plugin. + * 2. Extension input references: using a reference in code to another extension's input in the same plugin. These references are always relative. + * 3. Array of attachment points: an array of attachment points can be used to clone and attach to multiple extensions at once. + * + * @example + * ```ts + * // Attach to a specific extension by full ID + * { id: 'app/routes', input: 'routes' } + * + * // Attach to an extension in the same plugin by kind + * { relative: { kind: 'page' }, input: 'actions' } + * + * // Attach to a specific input of another extension + * const page = ParentBlueprint.make({ ... }); + * const child = ChildBlueprint.make({ attachTo: page.inputs.children }); + * + * // Attach to multiple parents at once + * [ + * { id: 'page/home', input: 'widgets' }, + * { relative: { kind: 'page' }, input: 'widgets' }, + * ] + * ``` + * + * @public + */ +export type ExtensionDefinitionAttachTo< + UParentInputs extends ExtensionDataRef = ExtensionDataRef, +> = + | { id: string; input: string; relative?: never } + | { relative: { kind?: string; name?: string }; input: string; id?: never } + | ExtensionInput + | Array< + | { id: string; input: string; relative?: never } + | { + relative: { kind?: string; name?: string }; + input: string; + id?: never; + } + | ExtensionInput + >; /** @public */ export type CreateExtensionOptions< TKind extends string | undefined, TName extends string | undefined, UOutput extends ExtensionDataRef, - TInputs extends { - [inputName in string]: ExtensionInput< - ExtensionDataRef, - { optional: boolean; singleton: boolean } - >; - }, + TInputs extends { [inputName in string]: ExtensionInput }, TConfigSchema extends { [key: string]: (zImpl: typeof z) => z.ZodType }, UFactoryOutput extends ExtensionDataValue, + UParentInputs extends ExtensionDataRef, > = { kind?: TKind; name?: TName; - attachTo: ExtensionAttachToSpec; + attachTo: ExtensionDefinitionAttachTo & + VerifyExtensionAttachTo; disabled?: boolean; inputs?: TInputs; output: Array; @@ -158,12 +214,7 @@ export type ExtensionDefinitionParameters = { configInput?: { [K in string]: any }; config?: { [K in string]: any }; output?: ExtensionDataRef; - inputs?: { - [KName in string]: ExtensionInput< - ExtensionDataRef, - { optional: boolean; singleton: boolean } - >; - }; + inputs?: { [KName in string]: ExtensionInput }; params?: object | ExtensionBlueprintDefineParams; }; @@ -188,23 +239,34 @@ export type ExtensionDefinition< $$type: '@backstage/ExtensionDefinition'; readonly T: T; + /** + * References to the inputs of this extension, which can be used to attach child extensions. + */ + readonly inputs: { + [K in keyof T['inputs']]: ExtensionInput< + T['inputs'][K] extends ExtensionInput ? IData : never + >; + }; + override< TExtensionConfigSchema extends { [key in string]: (zImpl: typeof z) => z.ZodType; }, UFactoryOutput extends ExtensionDataValue, UNewOutput extends ExtensionDataRef, - TExtraInputs extends { - [inputName in string]: ExtensionInput< - ExtensionDataRef, - { optional: boolean; singleton: boolean } - >; - }, + TExtraInputs extends { [inputName in string]: ExtensionInput }, TParamsInput extends AnyParamsInput>, + UParentInputs extends ExtensionDataRef, >( args: Expand< { - attachTo?: ExtensionAttachToSpec; + attachTo?: ExtensionDefinitionAttachTo & + VerifyExtensionAttachTo< + ExtensionDataRef extends UNewOutput + ? NonNullable + : UNewOutput, + UParentInputs + >; disabled?: boolean; inputs?: TExtraInputs & { [KName in keyof T['inputs']]?: `Error: Input '${KName & @@ -286,6 +348,30 @@ export type ExtensionDefinition< }>; }; +/** + * @internal + */ +function bindInputs( + inputs: { [inputName in string]: ExtensionInput } | undefined, + kind?: string, + name?: string, +) { + if (!inputs) { + return {}; + } + + return Object.fromEntries( + Object.entries(inputs).map(([inputName, input]) => [ + inputName, + OpaqueExtensionInput.toInternal(input).withContext({ + kind, + name, + input: inputName, + }), + ]), + ); +} + /** * Creates a new extension definition for installation in a Backstage app. * @@ -323,16 +409,12 @@ export type ExtensionDefinition< */ export function createExtension< UOutput extends ExtensionDataRef, - TInputs extends { - [inputName in string]: ExtensionInput< - ExtensionDataRef, - { optional: boolean; singleton: boolean } - >; - }, + TInputs extends { [inputName in string]: ExtensionInput }, TConfigSchema extends { [key: string]: (zImpl: typeof z) => z.ZodType }, UFactoryOutput extends ExtensionDataValue, const TKind extends string | undefined = undefined, const TName extends string | undefined = undefined, + UParentInputs extends ExtensionDataRef = ExtensionDataRef, >( options: CreateExtensionOptions< TKind, @@ -340,7 +422,8 @@ export function createExtension< UOutput, TInputs, TConfigSchema, - UFactoryOutput + UFactoryOutput, + UParentInputs >, ): ExtensionDefinition<{ config: string extends keyof TConfigSchema @@ -404,7 +487,7 @@ export function createExtension< name: options.name, attachTo: options.attachTo, disabled: options.disabled ?? false, - inputs: options.inputs ?? {}, + inputs: bindInputs(options.inputs, options.kind, options.name), output: options.output, configSchema, factory: options.factory, @@ -416,12 +499,41 @@ export function createExtension< if (options.name) { parts.push(`name=${options.name}`); } - parts.push( - `attachTo=${[options.attachTo] - .flat() - .map(a => `${a.id}@${a.input}`) - .join('+')}`, - ); + const attachTo = [options.attachTo] + .flat() + .map(aAny => { + const a = aAny as ExtensionDefinitionAttachTo; + if (OpaqueExtensionInput.isType(a)) { + const { context } = OpaqueExtensionInput.toInternal(a); + if (!context) { + return ''; + } + let id = ''; + if (context?.kind) { + id = `${context?.kind}:${id}`; + } + if (context?.name) { + id = `${id}/${context?.name}`; + } + return `${id}@${context.input}`; + } + if ('relative' in a && a.relative) { + let id = ''; + if (a.relative.kind) { + id = `${a.relative.kind}:${id}`; + } + if (a.relative.name) { + id = `${id}/${a.relative.name}`; + } + return `${id}@${a.input}`; + } + if ('id' in a) { + return `${a.id}@${a.input}`; + } + throw new Error('Invalid attachment point specification'); + }) + .join('+'); + parts.push(`attachTo=${attachTo}`); return `ExtensionDefinition{${parts.join(',')}}`; }, override(overrideOptions) { @@ -449,9 +561,17 @@ export function createExtension< return createExtension({ kind: options.kind, name: options.name, - attachTo: overrideOptions.attachTo ?? options.attachTo, + attachTo: (overrideOptions.attachTo ?? + options.attachTo) as ExtensionDefinitionAttachTo, disabled: overrideOptions.disabled ?? options.disabled, - inputs: { ...overrideOptions.inputs, ...options.inputs }, + inputs: bindInputs( + { + ...(options.inputs ?? {}), + ...(overrideOptions.inputs ?? {}), + }, + options.kind, + options.name, + ), output: (overrideOptions.output ?? options.output) as ExtensionDataRef[], config: diff --git a/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.test.tsx b/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.test.tsx index e4cc876ff0..4759441187 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.test.tsx +++ b/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.test.tsx @@ -30,7 +30,7 @@ import { } from './createExtensionDataRef'; import { createExtensionInput } from './createExtensionInput'; import { RouteRef } from '../routing'; -import { ExtensionDefinition } from './createExtension'; +import { createExtension, ExtensionDefinition } from './createExtension'; import { createExtensionDataContainer, OpaqueExtensionDefinition, @@ -1345,6 +1345,235 @@ describe('createExtensionBlueprint', () => { ).toThrow('Refused to override params and factory at the same time'); }); + describe('with relative attachment points', () => { + const dataRef = createExtensionDataRef().with({ id: 'test.data' }); + + it('should create an extension with relative attachment points', () => { + const blueprint = createExtensionBlueprint({ + kind: 'test', + attachTo: [ + { relative: {}, input: 'tabs' }, + { relative: { kind: 'page' }, input: 'tabs' }, + { relative: { name: 'index' }, input: 'tabs' }, + { relative: { kind: 'page', name: 'index' }, input: 'tabs' }, + ], + output: [dataRef], + factory: () => [dataRef('bar')], + }); + + expect(String(blueprint.make({ params: {} }))).toBe( + 'ExtensionDefinition{kind=test,attachTo=@tabs+page:@tabs+/index@tabs+page:/index@tabs}', + ); + expect( + String( + blueprint.make({ + attachTo: [ + { relative: { kind: 'page' }, input: 'tabs' }, + { relative: { name: 'index' }, input: 'tabs' }, + { relative: { kind: 'page', name: 'index' }, input: 'tabs' }, + ], + params: {}, + }), + ), + ).toBe( + 'ExtensionDefinition{kind=test,attachTo=page:@tabs+/index@tabs+page:/index@tabs}', + ); + expect( + String( + blueprint.makeWithOverrides({ + attachTo: { + relative: { kind: 'page', name: 'index' }, + input: 'tabs', + }, + factory: orig => orig({}), + }), + ), + ).toBe( + 'ExtensionDefinition{kind=test,attachTo=page:/index@tabs}', + ); + }); + + it('should create an extension with relative attachment points by reference', () => { + const baseOpts = { + attachTo: { id: 'root', input: 'children' }, + inputs: { + tabs: createExtensionInput([dataRef]), + }, + output: [], + factory: () => [], + }; + const parent1 = createExtension({ + ...baseOpts, + }); + const parent2 = createExtension({ + ...baseOpts, + kind: 'page', + }); + const parent3 = createExtension({ + ...baseOpts, + name: 'index', + }); + const parent4 = createExtension({ + ...baseOpts, + inputs: {}, + kind: 'page', + name: 'index', + }).override({ + inputs: { + otherTabs: createExtensionInput([dataRef]), + }, + factory: () => [], + }); + const blueprint = createExtensionBlueprint({ + kind: 'test', + attachTo: [ + parent1.inputs.tabs, + parent2.inputs.tabs, + parent3.inputs.tabs, + parent4.inputs.otherTabs, + ], + output: [dataRef], + factory: () => [dataRef('bar')], + }); + expect(String(blueprint.make({ params: {} }))).toBe( + 'ExtensionDefinition{kind=test,attachTo=@tabs+page:@tabs+/index@tabs+page:/index@otherTabs}', + ); + expect( + String( + blueprint.make({ + attachTo: [ + parent2.inputs.tabs, + parent3.inputs.tabs, + parent4.inputs.otherTabs, + ], + params: {}, + }), + ), + ).toBe( + 'ExtensionDefinition{kind=test,attachTo=page:@tabs+/index@tabs+page:/index@otherTabs}', + ); + expect( + String( + blueprint.makeWithOverrides({ + attachTo: parent4.inputs.otherTabs, + factory: orig => orig({}), + }), + ), + ).toBe( + 'ExtensionDefinition{kind=test,attachTo=page:/index@otherTabs}', + ); + }); + + it('should provide type safe attachments by reference', () => { + const stringDataRef = createExtensionDataRef().with({ + id: 'test.string', + }); + const numberDataRef = createExtensionDataRef().with({ + id: 'test.number', + }); + + const parent = createExtensionBlueprint({ + kind: 'test-parent', + attachTo: { id: 'root', input: 'children' }, + inputs: { + string: createExtensionInput([stringDataRef]), + stringOpt: createExtensionInput([stringDataRef.optional()]), + number: createExtensionInput([numberDataRef]), + numberOpt: createExtensionInput([numberDataRef.optional()]), + both: createExtensionInput([stringDataRef, numberDataRef]), + bothOptString: createExtensionInput([ + stringDataRef.optional(), + numberDataRef, + ]), + bothOptNumber: createExtensionInput([ + stringDataRef, + numberDataRef.optional(), + ]), + bothOpt: createExtensionInput([ + stringDataRef.optional(), + numberDataRef.optional(), + ]), + }, + output: [], + factory: () => [], + }).make({ params: {} }); + const strOutExt = createExtensionBlueprint({ + kind: 'test', + attachTo: parent.inputs.string, + output: [stringDataRef], + factory: () => [stringDataRef('str')], + }); + strOutExt.make({ + attachTo: parent.inputs.string, + params: {}, + }); + strOutExt.makeWithOverrides({ + attachTo: parent.inputs.stringOpt, + factory: orig => orig({}), + }); + strOutExt.make({ + // @ts-expect-error + attachTo: parent.inputs.number, + params: {}, + }); + strOutExt.makeWithOverrides({ + attachTo: parent.inputs.numberOpt, + factory: orig => orig({}), + }); + strOutExt.make({ + // @ts-expect-error + attachTo: parent.inputs.both, + params: {}, + }); + strOutExt.make({ + attachTo: parent.inputs.bothOptNumber, + params: {}, + }); + strOutExt.make({ + // @ts-expect-error + attachTo: parent.inputs.bothOptString, + params: {}, + }); + strOutExt.make({ + attachTo: parent.inputs.bothOpt, + params: {}, + }); + const numberOutExt = createExtensionBlueprint({ + kind: 'test', + // @ts-expect-error + attachTo: parent.inputs.string, + output: [numberDataRef], + factory: () => [numberDataRef(1)], + }); + numberOutExt.make({ + // @ts-expect-error + attachTo: parent.inputs.string, + params: {}, + }); + numberOutExt.makeWithOverrides({ + attachTo: parent.inputs.number, + factory: orig => orig({}), + }); + const bothOutExt = createExtensionBlueprint({ + kind: 'test', + attachTo: parent.inputs.both, + output: [numberDataRef, stringDataRef], + factory: () => [numberDataRef(1), stringDataRef('str')], + }); + bothOutExt.makeWithOverrides({ + output: [numberDataRef.optional(), stringDataRef], + factory: orig => orig({}), + }); + bothOutExt.makeWithOverrides({ + // @ts-expect-error + attachTo: parent.inputs.both, + output: [numberDataRef.optional(), stringDataRef], + factory: orig => orig({}), + }); + expect('types').not.toBe('broken'); + }); + }); + describe('with advanced parameter types', () => { const testDataRef = createExtensionDataRef().with({ id: 'test' }); diff --git a/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.ts b/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.ts index 1a24eed712..f9a3731743 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtensionBlueprint.ts @@ -18,12 +18,13 @@ import { ApiHolder, AppNode } from '../apis'; import { Expand } from '@backstage/types'; import { OpaqueType } from '@internal/opaque'; import { - ExtensionAttachToSpec, + ExtensionDefinitionAttachTo, ExtensionDefinition, ResolvedExtensionInputs, VerifyExtensionFactoryOutput, createExtension, ctxParamsSymbol, + VerifyExtensionAttachTo, } from './createExtension'; import { z } from 'zod'; import { ExtensionInput } from './createExtensionInput'; @@ -103,18 +104,15 @@ export type CreateExtensionBlueprintOptions< TKind extends string, TParams extends object | ExtensionBlueprintDefineParams, UOutput extends ExtensionDataRef, - TInputs extends { - [inputName in string]: ExtensionInput< - ExtensionDataRef, - { optional: boolean; singleton: boolean } - >; - }, + TInputs extends { [inputName in string]: ExtensionInput }, TConfigSchema extends { [key in string]: (zImpl: typeof z) => z.ZodType }, UFactoryOutput extends ExtensionDataValue, TDataRefs extends { [name in string]: ExtensionDataRef }, + UParentInputs extends ExtensionDataRef, > = { kind: TKind; - attachTo: ExtensionAttachToSpec; + attachTo: ExtensionDefinitionAttachTo & + VerifyExtensionAttachTo; disabled?: boolean; inputs?: TInputs; output: Array; @@ -186,12 +184,7 @@ export type ExtensionBlueprintParameters = { configInput?: { [K in string]: any }; config?: { [K in string]: any }; output?: ExtensionDataRef; - inputs?: { - [KName in string]: ExtensionInput< - ExtensionDataRef, - { optional: boolean; singleton: boolean } - >; - }; + inputs?: { [KName in string]: ExtensionInput }; dataRefs?: { [name in string]: ExtensionDataRef }; }; @@ -222,9 +215,11 @@ export interface ExtensionBlueprint< make< TName extends string | undefined, TParamsInput extends AnyParamsInput>, + UParentInputs extends ExtensionDataRef, >(args: { name?: TName; - attachTo?: ExtensionAttachToSpec; + attachTo?: ExtensionDefinitionAttachTo & + VerifyExtensionAttachTo, UParentInputs>; disabled?: boolean; params: TParamsInput extends ExtensionBlueprintDefineParams ? TParamsInput @@ -254,15 +249,17 @@ export interface ExtensionBlueprint< }, UFactoryOutput extends ExtensionDataValue, UNewOutput extends ExtensionDataRef, - TExtraInputs extends { - [inputName in string]: ExtensionInput< - ExtensionDataRef, - { optional: boolean; singleton: boolean } - >; - }, + TExtraInputs extends { [inputName in string]: ExtensionInput }, + UParentInputs extends ExtensionDataRef, >(args: { name?: TName; - attachTo?: ExtensionAttachToSpec; + attachTo?: ExtensionDefinitionAttachTo & + VerifyExtensionAttachTo< + ExtensionDataRef extends UNewOutput + ? NonNullable + : UNewOutput, + UParentInputs + >; disabled?: boolean; inputs?: TExtraInputs & { [KName in keyof T['inputs']]?: `Error: Input '${KName & @@ -455,15 +452,11 @@ function unwrapParams( export function createExtensionBlueprint< TParams extends object | ExtensionBlueprintDefineParams, UOutput extends ExtensionDataRef, - TInputs extends { - [inputName in string]: ExtensionInput< - ExtensionDataRef, - { optional: boolean; singleton: boolean } - >; - }, + TInputs extends { [inputName in string]: ExtensionInput }, TConfigSchema extends { [key in string]: (zImpl: typeof z) => z.ZodType }, UFactoryOutput extends ExtensionDataValue, TKind extends string, + UParentInputs extends ExtensionDataRef, TDataRefs extends { [name in string]: ExtensionDataRef } = never, >( options: CreateExtensionBlueprintOptions< @@ -473,7 +466,8 @@ export function createExtensionBlueprint< TInputs, TConfigSchema, UFactoryOutput, - TDataRefs + TDataRefs, + UParentInputs >, ): ExtensionBlueprint<{ kind: TKind; @@ -509,7 +503,8 @@ export function createExtensionBlueprint< return createExtension({ kind: options.kind, name: args.name, - attachTo: args.attachTo ?? options.attachTo, + attachTo: (args.attachTo ?? + options.attachTo) as ExtensionDefinitionAttachTo, disabled: args.disabled ?? options.disabled, inputs: options.inputs, output: options.output as ExtensionDataRef[], @@ -525,7 +520,8 @@ export function createExtensionBlueprint< return createExtension({ kind: options.kind, name: args.name, - attachTo: args.attachTo ?? options.attachTo, + attachTo: (args.attachTo ?? + options.attachTo) as ExtensionDefinitionAttachTo, disabled: args.disabled ?? options.disabled, inputs: { ...args.inputs, ...options.inputs }, output: (args.output ?? options.output) as ExtensionDataRef[], diff --git a/packages/frontend-plugin-api/src/wiring/createExtensionInput.test.ts b/packages/frontend-plugin-api/src/wiring/createExtensionInput.test.ts index 084758b793..8511f79b4e 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtensionInput.test.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtensionInput.test.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +import { OpaqueExtensionInput } from '@internal/frontend'; import { createExtensionDataRef } from './createExtensionDataRef'; import { ExtensionInput, createExtensionInput } from './createExtensionInput'; @@ -29,6 +30,7 @@ describe('createExtensionInput', () => { $$type: '@backstage/ExtensionInput', extensionData: [stringDataRef, numberDataRef], config: { singleton: false, optional: false }, + withContext: expect.any(Function), }); const x1: ExtensionInput< @@ -54,6 +56,20 @@ describe('createExtensionInput', () => { unused(x1, x2, x3, x4); }); + it('should attach a context to the input', () => { + const input = createExtensionInput([stringDataRef, numberDataRef]); + const context = { input: 'test1', kind: 'test2', name: 'test3' }; + const inputWithContext = + OpaqueExtensionInput.toInternal(input).withContext(context); + expect(inputWithContext).toEqual({ + $$type: '@backstage/ExtensionInput', + extensionData: [stringDataRef, numberDataRef], + config: { singleton: false, optional: false }, + withContext: expect.any(Function), + context, + }); + }); + it('should create a singleton input', () => { const input = createExtensionInput([stringDataRef, numberDataRef], { singleton: true, @@ -62,6 +78,7 @@ describe('createExtensionInput', () => { $$type: '@backstage/ExtensionInput', extensionData: [stringDataRef, numberDataRef], config: { singleton: true, optional: false }, + withContext: expect.any(Function), }); const x1: ExtensionInput< @@ -96,6 +113,7 @@ describe('createExtensionInput', () => { $$type: '@backstage/ExtensionInput', extensionData: [stringDataRef, numberDataRef], config: { singleton: true, optional: true }, + withContext: expect.any(Function), }); const x1: ExtensionInput< diff --git a/packages/frontend-plugin-api/src/wiring/createExtensionInput.ts b/packages/frontend-plugin-api/src/wiring/createExtensionInput.ts index 7209e6f647..57040c8adf 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtensionInput.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtensionInput.ts @@ -14,17 +14,28 @@ * limitations under the License. */ +import { + ExtensionInputContext, + OpaqueExtensionInput, +} from '@internal/frontend'; import { ExtensionDataRef } from './createExtensionDataRef'; /** @public */ export interface ExtensionInput< - UExtensionData extends ExtensionDataRef, - TConfig extends { singleton: boolean; optional: boolean }, + UExtensionData extends ExtensionDataRef< + unknown, + string, + { optional?: true } + > = ExtensionDataRef, + TConfig extends { singleton: boolean; optional: boolean } = { + singleton: boolean; + optional: boolean; + }, > { - $$type: '@backstage/ExtensionInput'; - extensionData: Array; - config: TConfig; - replaces?: Array<{ id: string; input: string }>; + readonly $$type: '@backstage/ExtensionInput'; + readonly extensionData: Array; + readonly config: TConfig; + readonly replaces?: Array<{ id: string; input: string }>; } /** @public */ @@ -61,8 +72,7 @@ export function createExtensionInput< } } } - return { - $$type: '@backstage/ExtensionInput', + const baseOptions = { extensionData, config: { singleton: Boolean(config?.singleton) as TConfig['singleton'] extends true @@ -73,11 +83,21 @@ export function createExtensionInput< : false, }, replaces: config?.replaces, - } as ExtensionInput< + }; + + function createInstance(parent?: ExtensionInputContext): ExtensionInput< UExtensionData, { singleton: TConfig['singleton'] extends true ? true : false; optional: TConfig['optional'] extends true ? true : false; } - >; + > { + return OpaqueExtensionInput.createInstance(undefined, { + ...baseOptions, + context: parent, + withContext: createInstance, + }); + } + + return createInstance(); } diff --git a/packages/frontend-plugin-api/src/wiring/index.ts b/packages/frontend-plugin-api/src/wiring/index.ts index 45c80d4a90..1bbdf68b13 100644 --- a/packages/frontend-plugin-api/src/wiring/index.ts +++ b/packages/frontend-plugin-api/src/wiring/index.ts @@ -18,8 +18,8 @@ export { coreExtensionData } from './coreExtensionData'; export { createExtension, type ExtensionDefinition, + type ExtensionDefinitionAttachTo, type ExtensionDefinitionParameters, - type ExtensionAttachToSpec, type CreateExtensionOptions, type ResolvedExtensionInput, type ResolvedExtensionInputs, @@ -54,7 +54,11 @@ export { type FrontendFeatureLoader, type CreateFrontendFeatureLoaderOptions, } from './createFrontendFeatureLoader'; -export { type Extension } from './resolveExtensionDefinition'; +export { + type Extension, + type ExtensionAttachTo, + type ExtensionAttachToSpec, +} from './resolveExtensionDefinition'; export { type ExtensionDataContainer, type FeatureFlagConfig, diff --git a/packages/frontend-plugin-api/src/wiring/resolveExtensionDefinition.test.ts b/packages/frontend-plugin-api/src/wiring/resolveExtensionDefinition.test.ts index a74d908024..d8b90d69f3 100644 --- a/packages/frontend-plugin-api/src/wiring/resolveExtensionDefinition.test.ts +++ b/packages/frontend-plugin-api/src/wiring/resolveExtensionDefinition.test.ts @@ -14,11 +14,21 @@ * limitations under the License. */ +import { + createExtensionDataRef, + createExtensionInput, +} from '@backstage/frontend-plugin-api'; import { ExtensionDefinition } from './createExtension'; import { ResolveExtensionId, resolveExtensionDefinition, } from './resolveExtensionDefinition'; +import { + OpaqueExtensionDefinition, + OpaqueExtensionInput, +} from '@internal/frontend'; + +const testDataRef = createExtensionDataRef().with({ id: 'test' }); describe('resolveExtensionDefinition', () => { const baseDef = { @@ -26,6 +36,7 @@ describe('resolveExtensionDefinition', () => { T: undefined as any, version: 'v2', attachTo: { id: '', input: '' }, + inputs: {}, disabled: false, override: () => ({} as ExtensionDefinition), }; @@ -60,6 +71,111 @@ describe('resolveExtensionDefinition', () => { 'Extension must declare an explicit namespace or name as it could not be resolved from context, kind=undefined namespace=undefined name=undefined', ); }); + + it('should resolve extension input references', () => { + const baseInpuf = OpaqueExtensionInput.toInternal( + createExtensionInput([testDataRef]), + ); + expect( + resolveExtensionDefinition( + OpaqueExtensionDefinition.toInternal({ + ...baseDef, + attachTo: baseInpuf.withContext({ + kind: 'parent', + name: 'example', + input: 'children', + }), + }), + { namespace: 'test' }, + ).attachTo, + ).toEqual({ + id: 'parent:test/example', + input: 'children', + }); + + expect( + resolveExtensionDefinition( + OpaqueExtensionDefinition.toInternal({ + ...baseDef, + attachTo: baseInpuf.withContext({ + name: 'example', + input: 'children', + }), + }), + { namespace: 'test' }, + ).attachTo, + ).toEqual({ + id: 'test/example', + input: 'children', + }); + + expect( + resolveExtensionDefinition( + OpaqueExtensionDefinition.toInternal({ + ...baseDef, + attachTo: baseInpuf.withContext({ + kind: 'parent', + input: 'children', + }), + }), + { namespace: 'test' }, + ).attachTo, + ).toEqual({ + id: 'parent:test', + input: 'children', + }); + + expect( + resolveExtensionDefinition( + OpaqueExtensionDefinition.toInternal({ + ...baseDef, + attachTo: baseInpuf.withContext({ + input: 'children', + }), + }), + { namespace: 'test' }, + ).attachTo, + ).toEqual({ + id: 'test', + input: 'children', + }); + + expect( + resolveExtensionDefinition( + OpaqueExtensionDefinition.toInternal({ + ...baseDef, + attachTo: [ + baseInpuf.withContext({ + kind: 'k1', + input: 'children', + }), + baseInpuf.withContext({ + kind: 'k2', + input: 'children', + }), + baseInpuf.withContext({ + kind: 'k3', + input: 'children', + }), + ], + }), + { namespace: 'test' }, + ).attachTo, + ).toEqual([ + { + id: 'k1:test', + input: 'children', + }, + { + id: 'k2:test', + input: 'children', + }, + { + id: 'k3:test', + input: 'children', + }, + ]); + }); }); describe('old resolveExtensionDefinition', () => { @@ -68,6 +184,7 @@ describe('old resolveExtensionDefinition', () => { T: undefined as any, version: 'v1', attachTo: { id: '', input: '' }, + inputs: {}, disabled: false, override: () => ({} as ExtensionDefinition), }; diff --git a/packages/frontend-plugin-api/src/wiring/resolveExtensionDefinition.ts b/packages/frontend-plugin-api/src/wiring/resolveExtensionDefinition.ts index a5b6859e1a..f99d586e87 100644 --- a/packages/frontend-plugin-api/src/wiring/resolveExtensionDefinition.ts +++ b/packages/frontend-plugin-api/src/wiring/resolveExtensionDefinition.ts @@ -16,7 +16,7 @@ import { ApiHolder, AppNode } from '../apis'; import { - ExtensionAttachToSpec, + ExtensionDefinitionAttachTo, ExtensionDefinition, ExtensionDefinitionParameters, ResolvedExtensionInputs, @@ -24,7 +24,21 @@ import { import { PortableSchema } from '../schema'; import { ExtensionInput } from './createExtensionInput'; import { ExtensionDataRef, ExtensionDataValue } from './createExtensionDataRef'; -import { OpaqueExtensionDefinition } from '@internal/frontend'; +import { + OpaqueExtensionDefinition, + OpaqueExtensionInput, +} from '@internal/frontend'; + +/** @public */ +export type ExtensionAttachTo = + | { id: string; input: string } + | Array<{ id: string; input: string }>; + +/** + * @deprecated Use {@link ExtensionAttachTo} instead. + * @public + */ +export type ExtensionAttachToSpec = ExtensionAttachTo; /** @public */ export interface Extension { @@ -68,22 +82,14 @@ export type InternalExtension = Extension< } | { readonly version: 'v2'; - readonly inputs: { - [inputName in string]: ExtensionInput< - ExtensionDataRef, - { optional: boolean; singleton: boolean } - >; - }; + readonly inputs: { [inputName in string]: ExtensionInput }; readonly output: Array; factory(options: { apis: ApiHolder; node: AppNode; config: TConfig; inputs: ResolvedExtensionInputs<{ - [inputName in string]: ExtensionInput< - ExtensionDataRef, - { optional: boolean; singleton: boolean } - >; + [inputName in string]: ExtensionInput; }>; }): Iterable>; } @@ -126,24 +132,11 @@ export type ResolveExtensionId< : never : never; -/** @internal */ -export function resolveExtensionDefinition< - T extends ExtensionDefinitionParameters, ->( - definition: ExtensionDefinition, - context?: { namespace?: string }, -): Extension { - const internalDefinition = OpaqueExtensionDefinition.toInternal(definition); - const { - name, - kind, - namespace: _skip1, - override: _skip2, - ...rest - } = internalDefinition; - - const namespace = internalDefinition.namespace ?? context?.namespace; - +function resolveExtensionId( + kind?: string, + namespace?: string, + name?: string, +): string { const namePart = name && namespace ? `${namespace}/${name}` : namespace || name; if (!namePart) { @@ -152,10 +145,75 @@ export function resolveExtensionDefinition< ); } - const id = kind ? `${kind}:${namePart}` : namePart; + return kind ? `${kind}:${namePart}` : namePart; +} + +function resolveAttachTo( + attachTo: ExtensionDefinitionAttachTo, + namespace?: string, +): ExtensionAttachToSpec { + const resolveSpec = ( + spec: Exclude>, + ): { id: string; input: string } => { + if (OpaqueExtensionInput.isType(spec)) { + const { context } = OpaqueExtensionInput.toInternal(spec); + if (!context) { + throw new Error( + 'Invalid input object without a parent extension used as attachment point', + ); + } + return { + id: resolveExtensionId(context.kind, namespace, context.name), + input: context.input, + }; + } + if ('relative' in spec && spec.relative) { + return { + id: resolveExtensionId( + spec.relative.kind, + namespace, + spec.relative.name, + ), + input: spec.input, + }; + } + if ('id' in spec) { + return { id: spec.id, input: spec.input }; + } + throw new Error('Invalid attachment point specification'); + }; + + if (Array.isArray(attachTo)) { + return attachTo.map(resolveSpec); + } + + return resolveSpec(attachTo); +} + +/** @internal */ +export function resolveExtensionDefinition< + T extends ExtensionDefinitionParameters, +>( + definition: ExtensionDefinition, + context?: { namespace?: string }, +): Extension { + const internalDefinition = OpaqueExtensionDefinition.toInternal(definition); + + const { + name, + kind, + namespace: internalNamespace, + override: _skip2, + attachTo, + ...rest + } = internalDefinition; + + const namespace = internalNamespace ?? context?.namespace; + const id = resolveExtensionId(kind, namespace, name); return { ...rest, + attachTo: resolveAttachTo(attachTo, namespace), $$type: '@backstage/Extension', version: internalDefinition.version, id, diff --git a/packages/frontend-plugin-api/src/wiring/resolveInputOverrides.ts b/packages/frontend-plugin-api/src/wiring/resolveInputOverrides.ts index 8ffec893ee..b46bb426fa 100644 --- a/packages/frontend-plugin-api/src/wiring/resolveInputOverrides.ts +++ b/packages/frontend-plugin-api/src/wiring/resolveInputOverrides.ts @@ -19,7 +19,6 @@ import { Expand } from '@backstage/types'; import { ResolvedExtensionInput } from './createExtension'; import { createExtensionDataContainer } from '@internal/frontend'; import { - ExtensionDataRef, ExtensionDataRefToValue, ExtensionDataValue, } from './createExtensionDataRef'; @@ -28,16 +27,8 @@ import { ExtensionDataContainer } from './types'; /** @ignore */ export type ResolvedInputValueOverrides< - TInputs extends { - [inputName in string]: ExtensionInput< - ExtensionDataRef, - { optional: boolean; singleton: boolean } - >; - } = { - [inputName in string]: ExtensionInput< - ExtensionDataRef, - { optional: boolean; singleton: boolean } - >; + TInputs extends { [inputName in string]: ExtensionInput } = { + [inputName in string]: ExtensionInput; }, > = Expand< { @@ -90,12 +81,7 @@ function expectItem(value: T | T[]): T { /** @internal */ export function resolveInputOverrides( - declaredInputs?: { - [inputName in string]: ExtensionInput< - ExtensionDataRef, - { optional: boolean; singleton: boolean } - >; - }, + declaredInputs?: { [inputName in string]: ExtensionInput }, inputs?: { [KName in string]?: | ({ node: AppNode } & ExtensionDataContainer) diff --git a/packages/frontend-test-utils/CHANGELOG.md b/packages/frontend-test-utils/CHANGELOG.md index 0c198ecc1b..1fa18a1696 100644 --- a/packages/frontend-test-utils/CHANGELOG.md +++ b/packages/frontend-test-utils/CHANGELOG.md @@ -1,5 +1,33 @@ # @backstage/frontend-test-utils +## 0.4.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-app-api@0.13.2-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/frontend-plugin-api@0.12.2-next.0 + - @backstage/test-utils@1.7.13-next.0 + - @backstage/types@1.2.2 + - @backstage/version-bridge@1.0.11 + - @backstage/plugin-app@0.3.2-next.0 + +## 0.4.0 + +### Minor Changes + +- c41dd80: **BREAKING**: Removed the `extensions` option from `renderInTestApp`. If you need to pass extensions to the test app, use the new `renderTestApp` utility instead. + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-app@0.3.1 + - @backstage/frontend-plugin-api@0.12.1 + - @backstage/config@1.3.5 + - @backstage/frontend-app-api@0.13.1 + - @backstage/test-utils@1.7.12 + ## 0.3.7-next.1 ### Patch Changes diff --git a/packages/frontend-test-utils/package.json b/packages/frontend-test-utils/package.json index 3241bc0ce5..18ab54d197 100644 --- a/packages/frontend-test-utils/package.json +++ b/packages/frontend-test-utils/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/frontend-test-utils", - "version": "0.3.7-next.1", + "version": "0.4.1-next.0", "backstage": { "role": "web-library" }, diff --git a/packages/frontend-test-utils/report.api.md b/packages/frontend-test-utils/report.api.md index db31c082ea..b99cf843d4 100644 --- a/packages/frontend-test-utils/report.api.md +++ b/packages/frontend-test-utils/report.api.md @@ -29,6 +29,7 @@ import { RouteRef } from '@backstage/frontend-plugin-api'; import { TestApiProvider } from '@backstage/test-utils'; import { TestApiProviderProps } from '@backstage/test-utils'; import { TestApiRegistry } from '@backstage/test-utils'; +import { testingLibraryDomTypesQueries } from '@testing-library/dom/types/queries'; import { withLogCollector } from '@backstage/test-utils'; export { ApiMock }; @@ -119,6 +120,19 @@ export function renderInTestApp( options?: TestAppOptions, ): RenderResult; +// @public +export function renderTestApp( + options: RenderTestAppOptions, +): RenderResult; + +// @public +export type RenderTestAppOptions = { + config?: JsonObject; + extensions?: ExtensionDefinition[]; + features?: FrontendFeature[]; + initialRouteEntries?: string[]; +}; + export { TestApiProvider }; export { TestApiProviderProps }; @@ -131,7 +145,6 @@ export type TestAppOptions = { [path: string]: RouteRef; }; config?: JsonObject; - extensions?: ExtensionDefinition[]; features?: FrontendFeature[]; initialRouteEntries?: string[]; }; diff --git a/packages/frontend-test-utils/src/app/index.ts b/packages/frontend-test-utils/src/app/index.ts index 6c6ef26e4c..9db67dbb49 100644 --- a/packages/frontend-test-utils/src/app/index.ts +++ b/packages/frontend-test-utils/src/app/index.ts @@ -21,3 +21,4 @@ export { } from './createExtensionTester'; export { renderInTestApp, type TestAppOptions } from './renderInTestApp'; +export { renderTestApp, type RenderTestAppOptions } from './renderTestApp'; diff --git a/packages/frontend-test-utils/src/app/renderInTestApp.tsx b/packages/frontend-test-utils/src/app/renderInTestApp.tsx index 80818509da..747229e8d6 100644 --- a/packages/frontend-test-utils/src/app/renderInTestApp.tsx +++ b/packages/frontend-test-utils/src/app/renderInTestApp.tsx @@ -67,11 +67,6 @@ export type TestAppOptions = { */ config?: JsonObject; - /** - * Additional extensions to add to the test app. - */ - extensions?: ExtensionDefinition[]; - /** * Additional features to add to the test app. */ @@ -107,6 +102,12 @@ const appPluginOverride = appPlugin.withOverrides({ appPlugin.getExtension('sign-in-page:app').override({ disabled: true, }), + appPlugin.getExtension('app/layout').override({ + disabled: true, + }), + appPlugin.getExtension('app/routes').override({ + disabled: true, + }), appPlugin.getExtension('app/nav').override({ output: [coreExtensionData.reactElement], factory(_originalFactory, { inputs }) { @@ -147,13 +148,10 @@ export function renderInTestApp( ): RenderResult { const extensions: Array = [ createExtension({ - attachTo: { id: 'app/routes', input: 'routes' }, - output: [coreExtensionData.reactElement, coreExtensionData.routePath], + attachTo: { id: 'app/root', input: 'children' }, + output: [coreExtensionData.reactElement], factory: () => { - return [ - coreExtensionData.reactElement(element), - coreExtensionData.routePath('/'), - ]; + return [coreExtensionData.reactElement(element)]; }, }), RouterBlueprint.make({ @@ -190,10 +188,6 @@ export function renderInTestApp( } } - if (options?.extensions) { - extensions.push(...options.extensions); - } - const features: FrontendFeature[] = [ createFrontendPlugin({ pluginId: 'test', diff --git a/packages/frontend-test-utils/src/app/renderTestApp.tsx b/packages/frontend-test-utils/src/app/renderTestApp.tsx new file mode 100644 index 0000000000..e8d90cadd6 --- /dev/null +++ b/packages/frontend-test-utils/src/app/renderTestApp.tsx @@ -0,0 +1,115 @@ +/* + * Copyright 2025 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { createSpecializedApp } from '@backstage/frontend-app-api'; +import { + coreExtensionData, + createFrontendPlugin, + ExtensionDefinition, + FrontendFeature, + RouterBlueprint, +} from '@backstage/frontend-plugin-api'; +import { render } from '@testing-library/react'; +import appPlugin from '@backstage/plugin-app'; +import { JsonObject } from '@backstage/types'; +import { ConfigReader } from '@backstage/config'; +import { MemoryRouter } from 'react-router-dom'; + +const DEFAULT_MOCK_CONFIG = { + app: { baseUrl: 'http://localhost:3000' }, + backend: { baseUrl: 'http://localhost:7007' }, +}; + +/** + * Options for `renderTestApp`. + * + * @public + */ +export type RenderTestAppOptions = { + /** + * Additional configuration passed to the app when rendering elements inside it. + */ + config?: JsonObject; + /** + * Additional extensions to add to the test app. + */ + extensions?: ExtensionDefinition[]; + + /** + * Additional features to add to the test app. + */ + features?: FrontendFeature[]; + + /** + * Initial route entries to use for the router. + */ + initialRouteEntries?: string[]; +}; + +const appPluginOverride = appPlugin.withOverrides({ + extensions: [ + appPlugin.getExtension('sign-in-page:app').override({ + disabled: true, + }), + ], +}); + +/** + * Renders the provided extensions inside a Backstage app, returning the same + * utilities as `@testing-library/react` `render` function. + * + * @public + */ +export function renderTestApp(options: RenderTestAppOptions) { + const extensions = [ + RouterBlueprint.make({ + params: { + component: ({ children }) => ( + + {children} + + ), + }, + }), + ...(options.extensions ?? []), + ]; + + const features: FrontendFeature[] = [ + createFrontendPlugin({ + pluginId: 'test', + extensions, + }), + appPluginOverride, + ]; + + if (options.features) { + features.push(...options.features); + } + + const app = createSpecializedApp({ + features, + config: ConfigReader.fromConfigs([ + { + context: 'render-config', + data: options?.config ?? DEFAULT_MOCK_CONFIG, + }, + ]), + }); + + return render( + app.tree.root.instance!.getData(coreExtensionData.reactElement), + ); +} diff --git a/packages/integration-aws-node/CHANGELOG.md b/packages/integration-aws-node/CHANGELOG.md index 830f38632e..d49f70955d 100644 --- a/packages/integration-aws-node/CHANGELOG.md +++ b/packages/integration-aws-node/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/integration-aws-node +## 0.1.19-next.0 + +### Patch Changes + +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/config@1.3.6-next.0 + - @backstage/errors@1.2.7 + +## 0.1.18 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.5 + ## 0.1.18-next.0 ### Patch Changes diff --git a/packages/integration-aws-node/package.json b/packages/integration-aws-node/package.json index 10855b7f54..e3cd09cde0 100644 --- a/packages/integration-aws-node/package.json +++ b/packages/integration-aws-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/integration-aws-node", - "version": "0.1.18-next.0", + "version": "0.1.19-next.0", "description": "Helpers for fetching AWS account credentials", "backstage": { "role": "node-library" diff --git a/packages/integration-aws-node/src/DefaultAwsCredentialsManager.ts b/packages/integration-aws-node/src/DefaultAwsCredentialsManager.ts index b54a4fda26..a02fea1b1c 100644 --- a/packages/integration-aws-node/src/DefaultAwsCredentialsManager.ts +++ b/packages/integration-aws-node/src/DefaultAwsCredentialsManager.ts @@ -199,14 +199,22 @@ export class DefaultAwsCredentialsManager implements AwsCredentialsManager { ); } + private readonly accountCredentialProviders: Map< + string, + AwsCredentialProvider + >; + private readonly accountDefaults: AwsIntegrationDefaultAccountConfig; + private readonly mainAccountCredentialProvider: AwsCredentialProvider; + private constructor( - private readonly accountCredentialProviders: Map< - string, - AwsCredentialProvider - >, - private readonly accountDefaults: AwsIntegrationDefaultAccountConfig, - private readonly mainAccountCredentialProvider: AwsCredentialProvider, - ) {} + accountCredentialProviders: Map, + accountDefaults: AwsIntegrationDefaultAccountConfig, + mainAccountCredentialProvider: AwsCredentialProvider, + ) { + this.accountCredentialProviders = accountCredentialProviders; + this.accountDefaults = accountDefaults; + this.mainAccountCredentialProvider = mainAccountCredentialProvider; + } /** * Returns an {@link AwsCredentialProvider} for a given AWS account. diff --git a/packages/integration-react/CHANGELOG.md b/packages/integration-react/CHANGELOG.md index adf06f88bd..6956f04b08 100644 --- a/packages/integration-react/CHANGELOG.md +++ b/packages/integration-react/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/integration-react +## 1.2.12-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-plugin-api@1.11.2-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/integration@1.18.2-next.0 + +## 1.2.11 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.18.1 + - @backstage/config@1.3.5 + - @backstage/core-plugin-api@1.11.1 + ## 1.2.11-next.1 ### Patch Changes diff --git a/packages/integration-react/package.json b/packages/integration-react/package.json index 410500c520..ca9dce00d9 100644 --- a/packages/integration-react/package.json +++ b/packages/integration-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/integration-react", - "version": "1.2.11-next.1", + "version": "1.2.12-next.0", "description": "Frontend package for managing integrations towards external systems", "backstage": { "role": "web-library" diff --git a/packages/integration/CHANGELOG.md b/packages/integration/CHANGELOG.md index e6546099f2..bcc2a938ae 100644 --- a/packages/integration/CHANGELOG.md +++ b/packages/integration/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/integration +## 1.18.2-next.0 + +### Patch Changes + +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/config@1.3.6-next.0 + - @backstage/errors@1.2.7 + +## 1.18.1 + +### Patch Changes + +- d772b51: remove host from azure blob storage integration type +- 84443f1: Adds config definitions for Azure Blob Storage. +- Updated dependencies + - @backstage/config@1.3.5 + ## 1.18.1-next.1 ### Patch Changes diff --git a/packages/integration/package.json b/packages/integration/package.json index 2dbb4a32e8..45928cd59b 100644 --- a/packages/integration/package.json +++ b/packages/integration/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/integration", - "version": "1.18.1-next.1", + "version": "1.18.2-next.0", "description": "Helpers for managing integrations towards external systems", "backstage": { "role": "common-library" diff --git a/packages/integration/src/github/SingleInstanceGithubCredentialsProvider.ts b/packages/integration/src/github/SingleInstanceGithubCredentialsProvider.ts index eaaabe8b0f..d2c1e37a65 100644 --- a/packages/integration/src/github/SingleInstanceGithubCredentialsProvider.ts +++ b/packages/integration/src/github/SingleInstanceGithubCredentialsProvider.ts @@ -279,10 +279,16 @@ export class SingleInstanceGithubCredentialsProvider ); }; + private readonly githubAppCredentialsMux: GithubAppCredentialsMux; + private readonly token?: string; + private constructor( - private readonly githubAppCredentialsMux: GithubAppCredentialsMux, - private readonly token?: string, - ) {} + githubAppCredentialsMux: GithubAppCredentialsMux, + token?: string, + ) { + this.githubAppCredentialsMux = githubAppCredentialsMux; + this.token = token; + } /** * Returns {@link GithubCredentials} for a given URL. diff --git a/packages/repo-tools/CHANGELOG.md b/packages/repo-tools/CHANGELOG.md index d8272e383a..d9ef90fe21 100644 --- a/packages/repo-tools/CHANGELOG.md +++ b/packages/repo-tools/CHANGELOG.md @@ -1,5 +1,46 @@ # @backstage/repo-tools +## 0.16.0-next.2 + +### Minor Changes + +- 11c61f2: The `package-docs` command will now automatically use a `typedoc.json` file if one exists at the root of your project. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.2 + +## 0.15.4-next.1 + +### Patch Changes + +- 8f56eae: Updated knip-reports to detect dependencies in dev/alpha pattern +- Updated dependencies + - @backstage/backend-plugin-api@1.5.0-next.1 + +## 0.15.4-next.0 + +### Patch Changes + +- 05f60e1: Refactored constructor parameter properties to explicit property declarations for compatibility with TypeScript's `erasableSyntaxOnly` setting. This internal refactoring maintains all existing functionality while ensuring TypeScript compilation compatibility. +- Updated dependencies + - @backstage/config-loader@1.10.6-next.0 + - @backstage/cli-node@0.2.15-next.0 + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/backend-plugin-api@1.4.5-next.0 + - @backstage/cli-common@0.1.15 + - @backstage/errors@1.2.7 + +## 0.15.3 + +### Patch Changes + +- 1359279: Fixed an issue with the OpenAPI generated client and server where import/export statements were duplicated. +- Updated dependencies + - @backstage/config-loader@1.10.5 + - @backstage/backend-plugin-api@1.4.4 + ## 0.15.3-next.0 ### Patch Changes diff --git a/packages/repo-tools/package.json b/packages/repo-tools/package.json index a536a95f4c..b250018017 100644 --- a/packages/repo-tools/package.json +++ b/packages/repo-tools/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/repo-tools", - "version": "0.15.3-next.0", + "version": "0.16.0-next.2", "description": "CLI for Backstage repo tooling ", "backstage": { "role": "cli" diff --git a/packages/repo-tools/src/commands/knip-reports/knip-extractor.ts b/packages/repo-tools/src/commands/knip-reports/knip-extractor.ts index 68bb0a9835..af3e32d2f4 100644 --- a/packages/repo-tools/src/commands/knip-reports/knip-extractor.ts +++ b/packages/repo-tools/src/commands/knip-reports/knip-extractor.ts @@ -58,7 +58,7 @@ async function generateKnipConfig({ knipConfigPath }: KnipConfigOptions) { workspaces: { '.': {}, '{packages,plugins}/*': { - entry: ['dev/index.{ts,tsx}', 'src/index.{ts,tsx}'], + entry: ['dev/**/*.{ts,tsx}', 'src/index.{ts,tsx}'], ignore: [ '.eslintrc.js', 'config.d.ts', diff --git a/packages/repo-tools/src/commands/package-docs/Cache.ts b/packages/repo-tools/src/commands/package-docs/Cache.ts index 487db81347..ec0811b77a 100644 --- a/packages/repo-tools/src/commands/package-docs/Cache.ts +++ b/packages/repo-tools/src/commands/package-docs/Cache.ts @@ -41,12 +41,19 @@ const cacheEntrySchema = z.object({ export class PackageDocsCache { // A map of package directory to package hash. private keyCache: Map; + private readonly lockfile: Lockfile; + // A map of package directory to cache entry. + private readonly cache: Map; + private readonly baseDirectory: string; + constructor( - private readonly lockfile: Lockfile, - // A map of package directory to cache entry. - private readonly cache: Map, - private readonly baseDirectory: string, + lockfile: Lockfile, + cache: Map, + baseDirectory: string, ) { + this.lockfile = lockfile; + this.cache = cache; + this.baseDirectory = baseDirectory; this.keyCache = new Map(); } static async loadAsync(baseDirectory: string, lockfile: Lockfile) { diff --git a/packages/repo-tools/src/commands/package-docs/command.ts b/packages/repo-tools/src/commands/package-docs/command.ts index 174a1d949d..28255193f0 100644 --- a/packages/repo-tools/src/commands/package-docs/command.ts +++ b/packages/repo-tools/src/commands/package-docs/command.ts @@ -23,6 +23,7 @@ import { mkdirp } from 'fs-extra'; import { PackageDocsCache } from './Cache'; import { Lockfile } from '@backstage/cli-node'; import { glob } from 'glob'; +import { existsSync } from 'fs'; const limit = pLimit(8); @@ -220,6 +221,9 @@ export default async function packageDocs(paths: string[] = [], opts: any) { ...HIGHLIGHT_LANGUAGES.flatMap(e => ['--highlightLanguages', e]), '--out', cliPaths.resolveTargetRoot('type-docs'), + ...(existsSync(cliPaths.resolveTargetRoot('typedoc.json')) + ? ['--options', cliPaths.resolveTargetRoot('typedoc.json')] + : []), ].join(' '), { cwd: cliPaths.targetRoot, diff --git a/packages/repo-tools/src/commands/package/schema/openapi/generate/client.ts b/packages/repo-tools/src/commands/package/schema/openapi/generate/client.ts index edcb49a9be..8a8817c721 100644 --- a/packages/repo-tools/src/commands/package/schema/openapi/generate/client.ts +++ b/packages/repo-tools/src/commands/package/schema/openapi/generate/client.ts @@ -28,6 +28,7 @@ import { getPathToCurrentOpenApiSpec, toGeneratorAdditionalProperties, } from '../../../../../lib/openapi/helpers'; +import { deduplicateImports } from '../../../../../lib/openapi/dedupe-imports'; async function generate( outputDirectory: string, @@ -101,6 +102,14 @@ async function generate( }); } + // Deduplicate imports in generated files + const generatedFiles = await fs.readdir(resolvedOutputDirectory); + for (const file of generatedFiles) { + if (file.endsWith('.ts')) { + deduplicateImports(resolve(resolvedOutputDirectory, file)); + } + } + fs.removeSync(resolve(resolvedOutputDirectory, '.openapi-generator-ignore')); fs.rmSync(resolve(resolvedOutputDirectory, '.openapi-generator'), { diff --git a/packages/repo-tools/src/commands/package/schema/openapi/generate/server.ts b/packages/repo-tools/src/commands/package/schema/openapi/generate/server.ts index c51b63cf54..130c93cf71 100644 --- a/packages/repo-tools/src/commands/package/schema/openapi/generate/server.ts +++ b/packages/repo-tools/src/commands/package/schema/openapi/generate/server.ts @@ -14,24 +14,25 @@ * limitations under the License. */ +import { resolvePackagePath } from '@backstage/backend-plugin-api'; import chalk from 'chalk'; -import { resolve, dirname, join } from 'path'; +import fs from 'fs-extra'; import YAML from 'js-yaml'; +import { dirname, join, resolve } from 'path'; +import { exec } from '../../../../../lib/exec'; import { OLD_SCHEMA_PATH, OPENAPI_IGNORE_FILES, OUTPUT_PATH, TS_SCHEMA_PATH, } from '../../../../../lib/openapi/constants'; -import { paths as cliPaths } from '../../../../../lib/paths'; -import fs from 'fs-extra'; -import { exec } from '../../../../../lib/exec'; -import { resolvePackagePath } from '@backstage/backend-plugin-api'; +import { deduplicateImports } from '../../../../../lib/openapi/dedupe-imports'; import { getPathToCurrentOpenApiSpec, getRelativePathToFile, toGeneratorAdditionalProperties, } from '../../../../../lib/openapi/helpers'; +import { paths as cliPaths } from '../../../../../lib/paths'; async function generateSpecFile() { const openapiPath = await getPathToCurrentOpenApiSpec(); @@ -133,6 +134,14 @@ async function generate( }, ); + // Deduplicate imports in generated files + const generatedFiles = await fs.readdir(resolvedOutputDirectory); + for (const file of generatedFiles) { + if (file.endsWith('.ts')) { + deduplicateImports(resolve(resolvedOutputDirectory, file)); + } + } + await exec( `yarn backstage-cli package lint --fix ${resolvedOutputDirectory}`, [], diff --git a/packages/repo-tools/src/lib/openapi/dedupe-imports.ts b/packages/repo-tools/src/lib/openapi/dedupe-imports.ts new file mode 100644 index 0000000000..c398a43e82 --- /dev/null +++ b/packages/repo-tools/src/lib/openapi/dedupe-imports.ts @@ -0,0 +1,74 @@ +/* + * Copyright 2025 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import fs from 'fs'; + +/** + * Removes duplicate import and export statements from TypeScript/JavaScript files. + * Preserves different import syntaxes, multi-line imports, and code structure. + * + * @param filePath - Absolute path to the file to deduplicate + */ +export function deduplicateImports(filePath: string): void { + const content = fs.readFileSync(filePath, 'utf8'); + const lines = content.split('\n'); + const seen = new Set(); + const result: string[] = []; + let i = 0; + + while (i < lines.length) { + const line = lines[i]; + const trimmed = line.trim(); + + // Handle import/export statements + if (trimmed.startsWith('import ') || trimmed.startsWith('export ')) { + let fullStatement = line; + let endIndex = i; + + // Check if multi-line (has { but no }) + if (trimmed.includes('{') && !trimmed.includes('}')) { + // Collect full multi-line statement + while (endIndex < lines.length && !lines[endIndex].includes('}')) { + endIndex++; + } + if (endIndex < lines.length) { + fullStatement = lines.slice(i, endIndex + 1).join('\n'); + } + } + + // Normalize for comparison (remove comments, extra spaces) + const normalized = fullStatement + .replace(/\/\/.*$/gm, '') + .replace(/\s+/g, ' ') + .trim(); + + if (!seen.has(normalized)) { + seen.add(normalized); + // Add all lines of the statement + for (let j = i; j <= endIndex; j++) { + result.push(lines[j]); + } + } + + i = endIndex + 1; + } else { + result.push(line); + i++; + } + } + + fs.writeFileSync(filePath, result.join('\n')); +} diff --git a/packages/repo-tools/src/lib/openapi/helpers.ts b/packages/repo-tools/src/lib/openapi/helpers.ts index 868623b874..0eaa090ac4 100644 --- a/packages/repo-tools/src/lib/openapi/helpers.ts +++ b/packages/repo-tools/src/lib/openapi/helpers.ts @@ -14,14 +14,13 @@ * limitations under the License. */ -import { pathExists } from 'fs-extra'; -import { paths } from '../paths'; -import { YAML_SCHEMA_PATH } from './constants'; -import { resolve } from 'path'; +import Parser from '@apidevtools/swagger-parser'; +import fs, { pathExists } from 'fs-extra'; import YAML from 'js-yaml'; import { cloneDeep } from 'lodash'; -import Parser from '@apidevtools/swagger-parser'; -import fs from 'fs-extra'; +import { resolve } from 'path'; +import { paths } from '../paths'; +import { YAML_SCHEMA_PATH } from './constants'; export const getPathToFile = async (directory: string, filename: string) => { return resolve(directory, filename); diff --git a/packages/scaffolder-internal/CHANGELOG.md b/packages/scaffolder-internal/CHANGELOG.md index e636e2ccc8..3b3a6f7c8c 100644 --- a/packages/scaffolder-internal/CHANGELOG.md +++ b/packages/scaffolder-internal/CHANGELOG.md @@ -1,5 +1,21 @@ # @internal/scaffolder +## 0.0.15-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-react@1.19.3-next.0 + - @backstage/frontend-plugin-api@0.12.2-next.0 + +## 0.0.14 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-react@1.19.2 + - @backstage/frontend-plugin-api@0.12.1 + ## 0.0.14-next.0 ### Patch Changes diff --git a/packages/scaffolder-internal/package.json b/packages/scaffolder-internal/package.json index 9f490a8b62..256ec5b5e3 100644 --- a/packages/scaffolder-internal/package.json +++ b/packages/scaffolder-internal/package.json @@ -1,6 +1,6 @@ { "name": "@internal/scaffolder", - "version": "0.0.14-next.0", + "version": "0.0.15-next.0", "backstage": { "role": "web-library", "inline": true diff --git a/packages/techdocs-cli-embedded-app/CHANGELOG.md b/packages/techdocs-cli-embedded-app/CHANGELOG.md index 54f526a1f2..3fe53ec2d6 100644 --- a/packages/techdocs-cli-embedded-app/CHANGELOG.md +++ b/packages/techdocs-cli-embedded-app/CHANGELOG.md @@ -1,5 +1,76 @@ # techdocs-cli-embedded-app +## 0.2.114-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/ui@0.9.0-next.2 + - @backstage/plugin-techdocs@1.16.0-next.1 + - @backstage/plugin-catalog@1.32.0-next.1 + - @backstage/cli@0.34.5-next.1 + - @backstage/core-components@0.18.3-next.1 + - @backstage/core-plugin-api@1.11.2-next.1 + - @backstage/core-app-api@1.19.2-next.1 + +## 0.2.114-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/ui@0.9.0-next.1 + +## 0.2.114-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/cli@0.34.5-next.0 + - @backstage/ui@0.8.2-next.0 + - @backstage/plugin-techdocs@1.15.2-next.0 + - @backstage/core-app-api@1.19.2-next.0 + - @backstage/core-plugin-api@1.11.2-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/core-components@0.18.3-next.0 + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/app-defaults@1.7.2-next.0 + - @backstage/integration-react@1.2.12-next.0 + - @backstage/test-utils@1.7.13-next.0 + - @backstage/theme@0.7.0 + - @backstage/plugin-catalog@1.31.5-next.0 + - @backstage/plugin-techdocs-react@1.3.5-next.0 + +## 0.2.113 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.18.2 + - @backstage/cli@0.34.4 + - @backstage/config@1.3.5 + - @backstage/theme@0.7.0 + - @backstage/plugin-catalog@1.31.4 + - @backstage/app-defaults@1.7.1 + - @backstage/test-utils@1.7.12 + - @backstage/plugin-techdocs@1.15.1 + - @backstage/plugin-techdocs-react@1.3.4 + - @backstage/core-app-api@1.19.1 + - @backstage/core-plugin-api@1.11.1 + - @backstage/integration-react@1.2.11 + +## 0.2.113-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/cli@0.34.4-next.2 + - @backstage/core-components@0.18.2-next.2 + - @backstage/theme@0.6.9-next.0 + - @backstage/app-defaults@1.7.1-next.2 + - @backstage/test-utils@1.7.12-next.1 + - @backstage/plugin-techdocs@1.15.1-next.2 + - @backstage/plugin-techdocs-react@1.3.4-next.1 + ## 0.2.113-next.1 ### Patch Changes diff --git a/packages/techdocs-cli-embedded-app/app-config.yaml b/packages/techdocs-cli-embedded-app/app-config.yaml index 6ede05b587..c8dfa1683c 100644 --- a/packages/techdocs-cli-embedded-app/app-config.yaml +++ b/packages/techdocs-cli-embedded-app/app-config.yaml @@ -7,3 +7,7 @@ backend: techdocs: builder: 'external' + sanitizer: + # Allow live reload locally. Added in this config to avoid updating the main techdocs plugin. + allowedCustomElementTagNameRegExp: '^live-reload$' + allowedCustomElementAttributeNameRegExp: '^live-reload-(epoch|request-id)$' diff --git a/packages/techdocs-cli-embedded-app/package.json b/packages/techdocs-cli-embedded-app/package.json index 951a3df06e..024d8039cd 100644 --- a/packages/techdocs-cli-embedded-app/package.json +++ b/packages/techdocs-cli-embedded-app/package.json @@ -1,6 +1,6 @@ { "name": "techdocs-cli-embedded-app", - "version": "0.2.113-next.1", + "version": "0.2.114-next.2", "backstage": { "role": "frontend" }, @@ -45,6 +45,7 @@ "@backstage/plugin-techdocs-react": "workspace:^", "@backstage/test-utils": "workspace:^", "@backstage/theme": "workspace:^", + "@backstage/ui": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "history": "^5.0.0", diff --git a/packages/techdocs-cli-embedded-app/public/index.html b/packages/techdocs-cli-embedded-app/public/index.html index 18bb3f50a9..196c711417 100644 --- a/packages/techdocs-cli-embedded-app/public/index.html +++ b/packages/techdocs-cli-embedded-app/public/index.html @@ -4,6 +4,7 @@ + @@ -71,6 +80,7 @@ const routes = ( > {techDocsPage} + diff --git a/packages/techdocs-cli-embedded-app/src/LiveReloadAddon.test.tsx b/packages/techdocs-cli-embedded-app/src/LiveReloadAddon.test.tsx new file mode 100644 index 0000000000..1a80def9e9 --- /dev/null +++ b/packages/techdocs-cli-embedded-app/src/LiveReloadAddon.test.tsx @@ -0,0 +1,111 @@ +/* + * Copyright 2025 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { render } from '@testing-library/react'; +import { TechDocsLiveReload } from './LiveReloadAddon'; + +jest.mock('@backstage/plugin-techdocs-react', () => ({ + useShadowRootElements: jest.fn(() => [ + { + querySelector: jest.fn((selector: string) => { + if (selector === 'live-reload') { + return { + getAttribute: (name: string) => { + if (name === 'live-reload-epoch') return '10'; + if (name === 'live-reload-request-id') return '1'; + return null; + }, + }; + } + return null; + }), + }, + ]), +})); + +describe('TechDocsLiveReload', () => { + const originalXHR = global.XMLHttpRequest; + let originalLocation: Location; + let openSpy: jest.Mock; + let sendSpy: jest.Mock; + + beforeEach(() => { + originalLocation = window.location; + openSpy = jest.fn(); + sendSpy = jest.fn(function (this: any) { + // simulate long-poll response that does NOT trigger reload (epoch unchanged) + setTimeout(() => { + (this as any).status = 200; + (this as any).responseText = '10'; + (this as any).onloadend?.call(this); + }, 0); + }); + + class MockXHR { + onloadend: ((this: any) => void) | null = null; + status = 0; + responseText = ''; + open = openSpy; + send = sendSpy as any; + abort = jest.fn(); + } + + global.XMLHttpRequest = MockXHR as any; + + // Replace window.location with a mutable object for tests + delete (window as any).location; + (window as any).location = { ...originalLocation, reload: jest.fn() }; + jest.spyOn(window, 'addEventListener').mockImplementation(() => {}); + jest.spyOn(window, 'removeEventListener').mockImplementation(() => {}); + Object.defineProperty(document, 'visibilityState', { + value: 'visible', + configurable: true, + }); + }); + + afterEach(() => { + global.XMLHttpRequest = originalXHR; + jest.restoreAllMocks(); + // restore original window.location + delete (window as any).location; + (window as any).location = originalLocation; + }); + + it('polls livereload endpoint and does not reload when epoch unchanged', async () => { + const reloadSpy = window.location.reload as unknown as jest.Mock; + render(); + expect(openSpy).toHaveBeenCalledWith('GET', '/.livereload/10/1'); + // give microtask queue a tick + await new Promise(res => setTimeout(res, 0)); + expect(reloadSpy).not.toHaveBeenCalled(); + }); + + it('reloads when server epoch increases', async () => { + const reloadSpy = window.location.reload as unknown as jest.Mock; + + sendSpy.mockImplementation(function (this: any) { + setTimeout(() => { + (this as any).status = 200; + (this as any).responseText = '11'; + (this as any).onloadend?.call(this); + }, 0); + }); + + render(); + await new Promise(res => setTimeout(res, 0)); + expect(reloadSpy).toHaveBeenCalled(); + }); +}); diff --git a/packages/techdocs-cli-embedded-app/src/LiveReloadAddon.tsx b/packages/techdocs-cli-embedded-app/src/LiveReloadAddon.tsx new file mode 100644 index 0000000000..905a339c72 --- /dev/null +++ b/packages/techdocs-cli-embedded-app/src/LiveReloadAddon.tsx @@ -0,0 +1,121 @@ +/* + * Copyright 2025 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { useShadowRootElements } from '@backstage/plugin-techdocs-react'; +import { useEffect, useRef } from 'react'; + +interface TechDocsLiveReloadProps { + /** Whether to enable livereload (default: true in development) */ + enabled?: boolean; +} + +/** + * LiveReload addon for Techdocs CLI. + * + * Support mkdocs built-in livereload, in a TechDocs CLI preview environment. + * See https://github.com/backstage/backstage/issues/30514 for more details. + */ +export const TechDocsLiveReload = ({ + enabled = true, +}: TechDocsLiveReloadProps) => { + const body = useShadowRootElements(['body']); + const reqRef = useRef(null); + const timeoutRef = useRef(null); + const LIVE_RELOAD_ELEMENT = 'live-reload'; + const LIVE_RELOAD_ATTR_EPOCH = 'live-reload-epoch'; + const LIVE_RELOAD_ATTR_REQUEST_ID = 'live-reload-request-id'; + const CLI_LIVERELOAD_PATH = '/.livereload'; + + useEffect(() => { + if (!enabled || !body[0]) { + return undefined; + } + + const liveReloadElement = body[0].querySelector(LIVE_RELOAD_ELEMENT); + + if (!liveReloadElement) { + return undefined; + } + + const epoch = parseInt( + liveReloadElement.getAttribute(LIVE_RELOAD_ATTR_EPOCH) || '0', + 10, + ); + const requestId = parseInt( + liveReloadElement.getAttribute(LIVE_RELOAD_ATTR_REQUEST_ID) || '0', + 10, + ); + + if (!epoch || !requestId) { + return undefined; + } + + const livereloadUrl = CLI_LIVERELOAD_PATH; + + const poll = () => { + reqRef.current = new XMLHttpRequest(); + reqRef.current.onloadend = function handleLoadEnd(this: XMLHttpRequest) { + if (parseFloat(this.responseText) > epoch) { + window.location.reload(); + } else { + timeoutRef.current = setTimeout(poll, this.status === 200 ? 0 : 3000); + } + }; + reqRef.current.open('GET', `${livereloadUrl}/${epoch}/${requestId}`); + reqRef.current.send(); + }; + + const stop = () => { + if (reqRef.current) { + reqRef.current.abort(); + reqRef.current = null; + } + if (timeoutRef.current) { + clearTimeout(timeoutRef.current); + timeoutRef.current = null; + } + }; + + // Stop when tab is inactive + const handleVisibilityChange = () => { + if (document.visibilityState === 'visible') { + poll(); + } else { + stop(); + } + }; + + const handleBeforeUnload = () => { + stop(); + }; + + // Start polling if page is visible + if (document.visibilityState === 'visible') { + poll(); + } + + document.addEventListener('visibilitychange', handleVisibilityChange); + window.addEventListener('beforeunload', handleBeforeUnload); + + return () => { + stop(); + document.removeEventListener('visibilitychange', handleVisibilityChange); + window.removeEventListener('beforeunload', handleBeforeUnload); + }; + }, [body, enabled]); + + return null; +}; diff --git a/packages/techdocs-cli-embedded-app/src/components/TechDocsPage/TechDocsPage.tsx b/packages/techdocs-cli-embedded-app/src/components/TechDocsPage/TechDocsPage.tsx index 479ad3161d..dfe728da86 100644 --- a/packages/techdocs-cli-embedded-app/src/components/TechDocsPage/TechDocsPage.tsx +++ b/packages/techdocs-cli-embedded-app/src/components/TechDocsPage/TechDocsPage.tsx @@ -49,15 +49,15 @@ const useStyles = makeStyles((theme: Theme) => ({ }, })); -enum Themes { - LIGHT = 'light', - DARK = 'dark', -} +const Themes = { + LIGHT: 'light', + DARK: 'dark', +} as const; export const TechDocsThemeToggle = () => { const appThemeApi = useApi(appThemeApiRef); const classes = useStyles(); - const [theme, setTheme] = useState( + const [theme, setTheme] = useState<(typeof Themes)[keyof typeof Themes]>( appThemeApi.getActiveThemeId() === Themes.DARK ? Themes.DARK : Themes.LIGHT, ); diff --git a/packages/techdocs-cli-embedded-app/src/index.tsx b/packages/techdocs-cli-embedded-app/src/index.tsx index 81b80e9bcc..f50db079b0 100644 --- a/packages/techdocs-cli-embedded-app/src/index.tsx +++ b/packages/techdocs-cli-embedded-app/src/index.tsx @@ -16,6 +16,7 @@ import '@backstage/cli/asset-types'; import ReactDOM from 'react-dom/client'; +import '@backstage/ui/css/styles.css'; import App from './App'; ReactDOM.createRoot(document.getElementById('root')!).render(); diff --git a/packages/techdocs-cli/CHANGELOG.md b/packages/techdocs-cli/CHANGELOG.md index 2fe1e1101a..13413f46c8 100644 --- a/packages/techdocs-cli/CHANGELOG.md +++ b/packages/techdocs-cli/CHANGELOG.md @@ -1,5 +1,30 @@ # @techdocs/cli +## 1.10.1-next.0 + +### Patch Changes + +- c2a2017: Fix for missing styles due to move to BUI. +- Updated dependencies + - @backstage/backend-defaults@0.13.1-next.0 + - @backstage/plugin-techdocs-node@1.13.9-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/catalog-model@1.7.6-next.0 + - @backstage/cli-common@0.1.15 + +## 1.10.0 + +### Minor Changes + +- 43afbe5: Techdocs CLI serve supports automatic refresh, relying on `mkdocs` `watch` feature. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.13.0 + - @backstage/config@1.3.5 + - @backstage/plugin-techdocs-node@1.13.8 + ## 1.9.9-next.1 ### Patch Changes diff --git a/packages/techdocs-cli/package.json b/packages/techdocs-cli/package.json index b46d0bc540..5f978adada 100644 --- a/packages/techdocs-cli/package.json +++ b/packages/techdocs-cli/package.json @@ -1,6 +1,6 @@ { "name": "@techdocs/cli", - "version": "1.9.9-next.1", + "version": "1.10.2-next.0", "description": "Utility CLI for managing TechDocs sites in Backstage.", "backstage": { "role": "cli" diff --git a/packages/techdocs-cli/src/lib/httpServer.ts b/packages/techdocs-cli/src/lib/httpServer.ts index c38bc86d7a..d7e8f62ea0 100644 --- a/packages/techdocs-cli/src/lib/httpServer.ts +++ b/packages/techdocs-cli/src/lib/httpServer.ts @@ -18,6 +18,10 @@ import serveHandler from 'serve-handler'; import http from 'http'; import httpProxy from 'http-proxy'; import { createLogger } from './utility'; +import { + proxyHtmlWithLivereloadInjection, + proxyMkdocsLivereload, +} from './livereload'; export default class HTTPServer { private readonly proxyEndpoint: string; @@ -59,8 +63,8 @@ export default class HTTPServer { const proxyHandler = this.createProxy(); const server = http.createServer( (request: http.IncomingMessage, response: http.ServerResponse) => { - // This endpoind is used by the frontend to issue a cookie for the user. - // But the MkDocs server doesn't expose it as a the Backestage backend does. + // This endpoint is used by the frontend to issue a cookie for the user. + // But the MkDocs server doesn't expose it as a the Backstage backend does. // So we need to fake it here to prevent 404 errors. if (request.url === '/api/techdocs/.backstage/auth/v1/cookie') { const oneHourInMilliseconds = 60 * 60 * 1000; @@ -72,6 +76,19 @@ export default class HTTPServer { } if (request.url?.startsWith(this.proxyEndpoint)) { + // Handle HTML files with livereload parameter injection + if (request.url?.endsWith('.html')) { + proxyHtmlWithLivereloadInjection({ + request, + response, + mkdocsTargetAddress: this.mkdocsTargetAddress, + proxyEndpoint: this.proxyEndpoint, + onError: (error: Error) => reject(error), + }); + return; + } + + // Handle non-HTML files with regular proxy const [proxy, forwardPath] = proxyHandler(request); proxy.on('error', (error: Error) => { @@ -93,6 +110,17 @@ export default class HTTPServer { return; } + // This endpoint is used by the frontend to pass livereload requests to the mkdocs server. + if (request.url?.startsWith('/.livereload')) { + proxyMkdocsLivereload({ + request, + response, + mkdocsTargetAddress: this.mkdocsTargetAddress, + onError: (error: Error) => reject(error), + }); + return; + } + serveHandler(request, response, { public: this.backstageBundleDir, trailingSlash: true, diff --git a/packages/techdocs-cli/src/lib/livereload.test.ts b/packages/techdocs-cli/src/lib/livereload.test.ts new file mode 100644 index 0000000000..b21251c45a --- /dev/null +++ b/packages/techdocs-cli/src/lib/livereload.test.ts @@ -0,0 +1,180 @@ +/* + * Copyright 2025 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import http from 'http'; +import { + injectLivereloadParameters, + proxyHtmlWithLivereloadInjection, + proxyMkdocsLivereload, +} from './livereload'; + +// Note: This mock returns a singleton proxy object so tests can access the +// registered event handlers (e.g. `proxyRes`) from code under test. +jest.mock('http-proxy', () => { + const handlers: Record = {}; + const fakeProxy = { + on: jest.fn((event: string, cb: Function) => { + handlers[event] = cb; + }), + web: jest.fn((_req: unknown, _res: unknown) => { + // no-op; tests will manually trigger handlers['proxyRes'] when needed + }), + __handlers: handlers, + }; + const create = jest.fn(() => fakeProxy); + return { + __esModule: true, + default: { createProxyServer: create }, + createProxyServer: create, + }; +}); + +describe('livereload helpers', () => { + describe('injectLivereloadParameters', () => { + it('injects live-reload element when mkdocs script is present', () => { + const html = + ''; + const result = injectLivereloadParameters(html); + expect(result).toContain(''); + }); + + it('returns original html when mkdocs script is absent', () => { + const html = '

    No livereload

    '; + const result = injectLivereloadParameters(html); + expect(result).toBe(html); + }); + }); + + describe('proxyHtmlWithLivereloadInjection', () => { + it('injects parameters into HTML responses and sets CORS headers', () => { + const { createProxyServer } = jest.requireMock('http-proxy') as any; + const proxy = createProxyServer(); + + const req = { + url: '/api/techdocs/some/path/index.html', + } as unknown as http.IncomingMessage; + const headers: Record = {}; + const res = { + setHeader: (k: string, v: any) => { + headers[k] = String(v); + }, + end: jest.fn(), + } as unknown as http.ServerResponse; + + proxyHtmlWithLivereloadInjection({ + request: req, + response: res, + mkdocsTargetAddress: 'http://localhost:8000', + proxyEndpoint: '/api/techdocs/', + onError: () => {}, + }); + + // Simulate mkdocs proxy response with HTML + const proxyRes: any = { + headers: { 'content-type': 'text/html; charset=utf-8' }, + on: (event: string, cb: Function) => { + if (event === 'data') { + cb(''); + } + if (event === 'end') { + cb(); + } + }, + pipe: jest.fn(), + }; + + (proxy as any).__handlers.proxyRes(proxyRes, {} as any, res); + + expect(res.end).toHaveBeenCalled(); + const injectedHtml = (res.end as jest.Mock).mock.calls[0][0] as string; + expect(injectedHtml).toContain(' { + const { createProxyServer } = jest.requireMock('http-proxy') as any; + const proxy = createProxyServer(); + + const req = { + url: '/api/techdocs/some/path/asset.css', + } as unknown as http.IncomingMessage; + const headers: Record = {}; + const res = { + setHeader: (k: string, v: any) => { + headers[k] = String(v); + }, + end: jest.fn(), + } as unknown as http.ServerResponse; + + proxyHtmlWithLivereloadInjection({ + request: req, + response: res, + mkdocsTargetAddress: 'http://localhost:8000', + proxyEndpoint: '/api/techdocs/', + onError: () => {}, + }); + + const proxyRes: any = { + headers: { 'content-type': 'text/css' }, + on: jest.fn(), + pipe: jest.fn(), + }; + + (proxy as any).__handlers.proxyRes(proxyRes, {} as any, res); + + expect(res.end).not.toHaveBeenCalled(); + expect(proxyRes.pipe).toHaveBeenCalledWith(res); + expect(headers['Access-Control-Allow-Origin']).toBe('*'); + expect(headers['Access-Control-Allow-Methods']).toBe('GET, OPTIONS'); + }); + }); + + describe('proxyMkdocsLivereload', () => { + it('rewrites path and sets CORS headers', () => { + const { createProxyServer } = jest.requireMock('http-proxy') as any; + const proxy = createProxyServer(); + + const req = { + url: '/.livereload/1/2', + } as unknown as http.IncomingMessage; + const headers: Record = {}; + const res = { + setHeader: (k: string, v: any) => { + headers[k] = String(v); + }, + } as unknown as http.ServerResponse; + + proxyMkdocsLivereload({ + request: req, + response: res, + mkdocsTargetAddress: 'http://localhost:8000', + onError: () => {}, + }); + + expect(req.url).toBe('/livereload/1/2'); + expect(headers['Access-Control-Allow-Origin']).toBe('*'); + expect(headers['Access-Control-Allow-Methods']).toBe('GET, OPTIONS'); + expect(headers['Access-Control-Allow-Headers']).toBe('Content-Type'); + expect((proxy as any).web).toHaveBeenCalled(); + }); + }); +}); diff --git a/packages/techdocs-cli/src/lib/livereload.ts b/packages/techdocs-cli/src/lib/livereload.ts new file mode 100644 index 0000000000..c108971b82 --- /dev/null +++ b/packages/techdocs-cli/src/lib/livereload.ts @@ -0,0 +1,175 @@ +/* + * Copyright 2025 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import http from 'http'; +import httpProxy from 'http-proxy'; + +/** + * Livereload support for techdocs-cli. + * + * Context: + * - MkDocs implements autoreload using a long-poll endpoint `/livereload` and a script that injects + * a call like: `livereload(epoch, request_id)`, where `epoch` is derived from Python's + * `time.monotonic()`. + * - Node.js monotonic clocks (`process.hrtime`/`performance.now`) are not compatible with Python's + * value across processes and platforms. We therefore CANNOT reliably re-create the same epoch on + * the frontend or in this CLI, and must read the values produced by MkDocs itself. + * - The MkDocs script tag is removed by DOM sanitization (DomPurify) in TechDocs, so we can't rely + * on the script being present in the embedded app. To bridge this, we extract the parameters on + * the server side while proxying HTML and inject them as a safe custom element that survives + * sanitization: ``. + * - The frontend addon reads that element and polls `/.livereload` (served by techdocs-cli), which + * this module maps to MkDocs `/livereload` with permissive CORS headers. + * - Quality-of-life: if extraction fails or the endpoint is unavailable, normal docs still work. + * + * See issue for background and rationale: https://github.com/backstage/backstage/issues/30514 + */ + +const LIVE_RELOAD_ELEMENT = 'live-reload'; +const LIVE_RELOAD_ATTR_EPOCH = 'live-reload-epoch'; +const LIVE_RELOAD_ATTR_REQUEST_ID = 'live-reload-request-id'; +const CLI_LIVERELOAD_PATH = '/.livereload'; +const MKDOCS_LIVERELOAD_PATH = '/livereload'; +const CONTENT_TYPE_HTML = 'text/html'; +const HEADER_CONTENT_LENGTH = 'content-length'; + +const BODY_START_RE = /]*>/; +// Matches mkdocs injected call livereload(epoch, requestId) +const MKDOCS_LIVERELOAD_CALL_RE = /livereload\(\s*(\d+)\s*,\s*(\d+)\s*\)\s*;?/; + +/** + * Extract livereload parameters from mkdocs HTML and inject them as a custom element. + * The injected element will later be read by the frontend addon even after DOM sanitization. + * + * Note: + * - we don't add to because of DomPurify sanitization. + * - we add close to the body opening to avoid reading too far into the body. + * - we should use streamed injection to improve performance. + */ +export function injectLivereloadParameters(html: string): string { + const livereloadMatch = html.match(MKDOCS_LIVERELOAD_CALL_RE); + + // If we couldn't find livereload parameters, return original HTML untouched. + if (!livereloadMatch) { + return html; + } + + const [, epoch, requestId] = livereloadMatch; + // Insert a minimal custom element that the frontend addon can discover post-sanitization. + // Note: embedded app needs a custom config to allow the element to survive sanitization. + const liveReloadTag = `<${LIVE_RELOAD_ELEMENT} ${LIVE_RELOAD_ATTR_EPOCH}="${epoch}" ${LIVE_RELOAD_ATTR_REQUEST_ID}="${requestId}">`; + + // Naively find where to insert the livereload tag. + const bodyStart = html.match(BODY_START_RE); + const bodyStartIndex = bodyStart?.index ?? 0; + const bodyStartLength = bodyStart?.[0]?.length ?? 0; + if (bodyStartIndex === 0 || bodyStartLength === 0) { + return html; + } + const bodyEndIndex = bodyStartIndex + bodyStartLength; + + return html.slice(0, bodyEndIndex) + liveReloadTag + html.slice(bodyEndIndex); +} + +function setCorsHeaders(response: http.ServerResponse) { + response.setHeader('Access-Control-Allow-Origin', '*'); + response.setHeader('Access-Control-Allow-Methods', 'GET, OPTIONS'); +} + +/** + * Proxies a mkdocs HTML response, injecting livereload parameters into the HTML body. + */ +export function proxyHtmlWithLivereloadInjection(options: { + request: http.IncomingMessage; + response: http.ServerResponse; + mkdocsTargetAddress: string; + proxyEndpoint: string; + onError: (error: Error) => void; +}): void { + const { request, response, mkdocsTargetAddress, proxyEndpoint, onError } = + options; + + const htmlProxy = httpProxy.createProxyServer({ + target: mkdocsTargetAddress, + selfHandleResponse: true, + }); + + htmlProxy.on('error', onError); + + // Intercept HTML responses to inject `` + htmlProxy.on('proxyRes', (proxyRes, _req, res) => { + const contentType = proxyRes.headers['content-type']; + const contentEncoding = proxyRes.headers['content-encoding']; + const isHtml = + contentType && + typeof contentType === 'string' && + contentType.startsWith(CONTENT_TYPE_HTML); + if (isHtml && !contentEncoding) { + const chunks: Buffer[] = []; + proxyRes.on('data', (chunk: Buffer | string) => { + chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)); + }); + proxyRes.on('end', () => { + const body = Buffer.concat(chunks).toString('utf8'); + const modifiedHtml = injectLivereloadParameters(body); + res.statusCode = (proxyRes.statusCode as number | undefined) ?? 200; + Object.keys(proxyRes.headers).forEach(key => { + if (key.toLowerCase() !== HEADER_CONTENT_LENGTH) { + res.setHeader(key, proxyRes.headers[key]!); + } + }); + setCorsHeaders(res); + res.end(modifiedHtml); + }); + } else { + res.statusCode = (proxyRes.statusCode as number | undefined) ?? 200; + Object.keys(proxyRes.headers).forEach(key => { + res.setHeader(key, proxyRes.headers[key]!); + }); + setCorsHeaders(res); + proxyRes.pipe(res); + } + }); + + const forwardPath = + request.url?.replace(new RegExp(`^${proxyEndpoint}`, 'i'), '') || ''; + request.url = forwardPath; + htmlProxy.web(request, response); +} + +/** + * Proxies mkdocs livereload long-polling requests, mapping the CLI path to mkdocs path. + */ +export function proxyMkdocsLivereload(options: { + request: http.IncomingMessage; + response: http.ServerResponse; + mkdocsTargetAddress: string; + onError: (error: Error) => void; +}): void { + const { request, response, mkdocsTargetAddress, onError } = options; + + const proxy = httpProxy.createProxyServer({ target: mkdocsTargetAddress }); + proxy.on('error', onError); + + setCorsHeaders(response); + response.setHeader('Access-Control-Allow-Headers', 'Content-Type'); + + request.url = request.url?.replace( + CLI_LIVERELOAD_PATH, + MKDOCS_LIVERELOAD_PATH, + ); + proxy.web(request, response); +} diff --git a/packages/test-utils/CHANGELOG.md b/packages/test-utils/CHANGELOG.md index 8425798421..7beeed27c1 100644 --- a/packages/test-utils/CHANGELOG.md +++ b/packages/test-utils/CHANGELOG.md @@ -1,5 +1,37 @@ # @backstage/test-utils +## 1.7.13-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-react@0.4.38-next.0 + - @backstage/core-app-api@1.19.2-next.0 + - @backstage/core-plugin-api@1.11.2-next.0 + - @backstage/config@1.3.6-next.0 + - @backstage/theme@0.7.0 + - @backstage/types@1.2.2 + - @backstage/plugin-permission-common@0.9.3-next.0 + +## 1.7.12 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.5 + - @backstage/theme@0.7.0 + - @backstage/core-app-api@1.19.1 + - @backstage/core-plugin-api@1.11.1 + - @backstage/plugin-permission-common@0.9.2 + - @backstage/plugin-permission-react@0.4.37 + +## 1.7.12-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/theme@0.6.9-next.0 + ## 1.7.12-next.0 ### Patch Changes diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index 6e914f3b50..b40fa8a0d8 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/test-utils", - "version": "1.7.12-next.0", + "version": "1.7.13-next.0", "description": "Utilities to test Backstage plugins and apps.", "backstage": { "role": "web-library" diff --git a/packages/theme/CHANGELOG.md b/packages/theme/CHANGELOG.md index 9cb3a6527b..1b5b158102 100644 --- a/packages/theme/CHANGELOG.md +++ b/packages/theme/CHANGELOG.md @@ -1,5 +1,27 @@ # @backstage/theme +## 0.7.0 + +### Minor Changes + +- 865bce8: **BREAKING**: Removed the built-in `CssBaseline` from `UnifiedThemeProvider`. If your Backstage instance looks broken after this update, you likely forgot to add our new Backstage UI global CSS. To do that, please import `@backstage/ui/css/styles.css` in `packages/app/src/index.tsx`: + + ```tsx + import '@backstage/ui/css/styles.css'; + ``` + + This change also removes the `noCssBaseline` prop, which became redundant. + +### Patch Changes + +- d5cbdba: The `UnifiedThemeProvider` now coordinates theme attributes on the document `body` in case multiple theme providers are rendered. + +## 0.6.9-next.0 + +### Patch Changes + +- d5cbdba: The `UnifiedThemeProvider` now coordinates theme attributes on the document `body` in case multiple theme providers are rendered. + ## 0.6.8 ### Patch Changes diff --git a/packages/theme/package.json b/packages/theme/package.json index 3699d858a4..cb7d5c12ce 100644 --- a/packages/theme/package.json +++ b/packages/theme/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/theme", - "version": "0.6.8", + "version": "0.7.0", "description": "material-ui theme for use with Backstage.", "backstage": { "role": "web-library" diff --git a/packages/theme/report.api.md b/packages/theme/report.api.md index ebdae12a05..18ac96ecec 100644 --- a/packages/theme/report.api.md +++ b/packages/theme/report.api.md @@ -459,8 +459,6 @@ export interface UnifiedThemeProviderProps { // (undocumented) children: ReactNode; // (undocumented) - noCssBaseline?: boolean; - // (undocumented) theme: UnifiedTheme; } ``` diff --git a/packages/theme/src/unified/UnifiedThemeProvider.tsx b/packages/theme/src/unified/UnifiedThemeProvider.tsx index a80e36cf35..d0886cd146 100644 --- a/packages/theme/src/unified/UnifiedThemeProvider.tsx +++ b/packages/theme/src/unified/UnifiedThemeProvider.tsx @@ -14,8 +14,7 @@ * limitations under the License. */ -import { ReactNode, useEffect } from 'react'; -import CssBaseline from '@material-ui/core/CssBaseline'; +import { ReactNode } from 'react'; import { ThemeProvider, StylesProvider, @@ -38,7 +37,6 @@ import { unstable_ClassNameGenerator as ClassNameGenerator } from '@mui/material export interface UnifiedThemeProviderProps { children: ReactNode; theme: UnifiedTheme; - noCssBaseline?: boolean; } /** @@ -58,6 +56,8 @@ const generateV4ClassName = createGenerateClassName({ productionPrefix: 'jss4-', }); +import { useApplyThemeAttributes } from './useApplyThemeAttributes'; + /** * Provides themes for all Material UI versions supported by the provided unified theme. * @@ -66,35 +66,18 @@ const generateV4ClassName = createGenerateClassName({ export function UnifiedThemeProvider( props: UnifiedThemeProviderProps, ): JSX.Element { - const { children, theme, noCssBaseline = false } = props; + const { children, theme } = props; const v4Theme = theme.getTheme('v4') as Mui4Theme; const v5Theme = theme.getTheme('v5') as Mui5Theme; - const themeMode = v4Theme ? v4Theme.palette.type : v5Theme?.palette.mode; - const themeName = 'backstage'; - useEffect(() => { - document.body.setAttribute('data-theme-mode', themeMode); - document.body.setAttribute('data-theme-name', themeName); - - return () => { - document.body.removeAttribute('data-theme-mode'); - document.body.removeAttribute('data-theme-name'); - }; - }, [themeMode, themeName]); - - let cssBaseline: JSX.Element | undefined = undefined; - if (!noCssBaseline) { - cssBaseline = ; - } - - let result = ( - <> - {cssBaseline} - {children} - + useApplyThemeAttributes( + v4Theme ? v4Theme.palette.type : v5Theme?.palette.mode, + 'backstage', ); + let result = children as JSX.Element; + if (v4Theme) { result = ( diff --git a/packages/theme/src/unified/useApplyThemeAttributes.test.tsx b/packages/theme/src/unified/useApplyThemeAttributes.test.tsx new file mode 100644 index 0000000000..b6c1ec1852 --- /dev/null +++ b/packages/theme/src/unified/useApplyThemeAttributes.test.tsx @@ -0,0 +1,60 @@ +/* + * Copyright 2025 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import '@testing-library/jest-dom'; +import { renderHook } from '@testing-library/react'; +import { useApplyThemeAttributes } from './useApplyThemeAttributes'; + +describe('useApplyThemeAttributes', () => { + beforeEach(() => { + document.body.removeAttribute('data-unified-theme-stack'); + document.body.removeAttribute('data-theme-mode'); + document.body.removeAttribute('data-theme-name'); + }); + + it('pushes attributes on mount and pops on unmount', () => { + const { unmount } = renderHook(() => + useApplyThemeAttributes('light', 'one'), + ); + expect(document.body.getAttribute('data-theme-mode')).toBe('light'); + expect(document.body.getAttribute('data-theme-name')).toBe('one'); + expect( + JSON.parse(document.body.getAttribute('data-unified-theme-stack') || '[]') + .length, + ).toBe(1); + + unmount(); + expect(document.body.getAttribute('data-theme-mode')).toBeNull(); + expect(document.body.getAttribute('data-theme-name')).toBeNull(); + expect(document.body.getAttribute('data-unified-theme-stack')).toBeNull(); + }); + + it('stacks multiple mounts and applies top-most', () => { + const r1 = renderHook(() => useApplyThemeAttributes('light', 'one')); + const r2 = renderHook(() => useApplyThemeAttributes('dark', 'two')); + + expect(document.body.getAttribute('data-theme-mode')).toBe('dark'); + expect(document.body.getAttribute('data-theme-name')).toBe('two'); + + r2.unmount(); + expect(document.body.getAttribute('data-theme-mode')).toBe('light'); + expect(document.body.getAttribute('data-theme-name')).toBe('one'); + + r1.unmount(); + expect(document.body.getAttribute('data-theme-mode')).toBeNull(); + expect(document.body.getAttribute('data-theme-name')).toBeNull(); + }); +}); diff --git a/packages/theme/src/unified/useApplyThemeAttributes.ts b/packages/theme/src/unified/useApplyThemeAttributes.ts new file mode 100644 index 0000000000..8295d69ec6 --- /dev/null +++ b/packages/theme/src/unified/useApplyThemeAttributes.ts @@ -0,0 +1,72 @@ +/* + * Copyright 2025 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { useEffect } from 'react'; + +type ThemeFrame = { mode?: string; name?: string }; + +const STACK_ATTR = 'data-unified-theme-stack'; +const MODE_ATTR = 'data-theme-mode'; +const NAME_ATTR = 'data-theme-name'; + +function mutateAttrStack(mutator: (stack: ThemeFrame[]) => void) { + const stack = (() => { + const raw = document.body.getAttribute(STACK_ATTR); + if (!raw) { + return [] as ThemeFrame[]; + } + try { + const parsed = JSON.parse(raw); + return Array.isArray(parsed) ? (parsed as ThemeFrame[]) : []; + } catch { + return [] as ThemeFrame[]; + } + })(); + + mutator(stack); + + if (stack.length === 0) { + document.body.removeAttribute(STACK_ATTR); + } else { + document.body.setAttribute(STACK_ATTR, JSON.stringify(stack)); + } + + const top = stack[stack.length - 1]; + if (top?.mode) { + document.body.setAttribute(MODE_ATTR, String(top.mode)); + } else { + document.body.removeAttribute(MODE_ATTR); + } + if (top?.name) { + document.body.setAttribute(NAME_ATTR, String(top.name)); + } else { + document.body.removeAttribute(NAME_ATTR); + } +} + +export function useApplyThemeAttributes(themeMode: string, themeName: string) { + useEffect(() => { + mutateAttrStack(stack => { + stack.push({ mode: themeMode, name: themeName }); + }); + + return () => { + mutateAttrStack(stack => { + stack.pop(); + }); + }; + }, [themeMode, themeName]); +} diff --git a/packages/ui/CHANGELOG.md b/packages/ui/CHANGELOG.md index 4c9d6f833b..7347058a0c 100644 --- a/packages/ui/CHANGELOG.md +++ b/packages/ui/CHANGELOG.md @@ -1,5 +1,233 @@ # @backstage/ui +## 0.9.0-next.3 + +### Minor Changes + +- 83c100e: **BREAKING**: Removed `Collapsible` component. Migrate to `Accordion` or use React Aria `Disclosure`. + + ## Migration Path 1: Accordion (Opinionated Styled Component) + + Accordion provides preset styling with a similar component structure. + + ```diff + - import { Collapsible } from '@backstage/ui'; + + import { Accordion, AccordionTrigger, AccordionPanel } from '@backstage/ui'; + + - + - } /> + - Content + - + + + + + + + Content + + + ``` + + CSS classes: `.bui-CollapsibleRoot` → `.bui-Accordion`, `.bui-CollapsibleTrigger` → `.bui-AccordionTrigger` (now on heading element), `.bui-CollapsiblePanel` → `.bui-AccordionPanel` + + ## Migration Path 2: React Aria Disclosure (Full Customization) + + For custom styling without preset styles: + + ```tsx + import { Disclosure, Button, DisclosurePanel } from 'react-aria-components'; + + + + Content + ; + ``` + +- 816af0f: **BREAKING**: The `SelectProps` interface now accepts a generic type parameter for selection mode. + + Added searchable and multiple selection support to Select component. The component now accepts `searchable`, `selectionMode`, and `searchPlaceholder` props to enable filtering and multi-selection modes. + + Migration: If you're using `SelectProps` type directly, update from `SelectProps` to `SelectProps<'single' | 'multiple'>`. Component usage remains backward compatible. + +### Patch Changes + +- 35a3614: Fixed CSS issues in Select component including popover width constraints, focus outline behavior, and overflow handling. +- 01476f0: Improved visual consistency of PasswordField, SearchField, and MenuAutocomplete components. +- 836b0c7: Fixed dialog backdrop appearance in dark mode. +- 6d35a6b: Removed `@base-ui-components/react` dependency as all components now use React Aria Components. +- 7839e7b: Added `loading` prop to Button and ButtonIcon components for displaying spinner during async operations. +- a00fb88: Fixed Table Row component to properly support opening links in new tabs via right-click or Cmd+Click when using the href prop. + +## 0.9.0-next.2 + +### Minor Changes + +- 539cf26: **BREAKING**: Migrated Avatar component from Base UI to custom implementation with size changes: + + - Base UI-specific props are no longer supported + - Size values have been updated: + - New `x-small` size added (1.25rem / 20px) + - `small` size unchanged (1.5rem / 24px) + - `medium` size unchanged (2rem / 32px, default) + - `large` size **changed from 3rem to 2.5rem** (40px) + - New `x-large` size added (3rem / 48px) + + Migration: + + ```diff + # Remove Base UI-specific props + - + + + + # Update large size usage to x-large for same visual size + - + + + ``` + + Added `purpose` prop for accessibility control (`'informative'` or `'decoration'`). + +- 134151f: Fixing styles on SearchField in Backstage UI after migration to CSS modules. `SearchField` has now its own set of class names. We previously used class names from `TextField` but this approach was creating some confusion so going forward in your theme you'll be able to theme `TextField` and `SearchField` separately. + +### Patch Changes + +- d01de00: Fix broken external links in Backstage UI Header component. +- deaa427: Fixed Text component to prevent `truncate` prop from being spread to the underlying DOM element. +- 1059f95: Improved the Link component structure in Backstage UI. +- 6874094: Migrated CellProfile component from Base UI Avatar to Backstage UI Avatar component. +- 719d772: Avatar components in x-small and small sizes now display only one initial instead of two, improving readability at smaller dimensions. +- 3b18d80: Fixed RadioGroup radio button ellipse distortion by preventing flex shrink and grow. +- e16ece5: Set the color-scheme property depending on theme + +## 0.9.0-next.1 + +### Minor Changes + +- 5c614ff: **BREAKING**: Migrated Checkbox component from Base UI to React Aria Components. + + API changes required: + + - `checked` → `isSelected` + - `defaultChecked` → `defaultSelected` + - `disabled` → `isDisabled` + - `required` → `isRequired` + - `label` prop removed - use `children` instead + - CSS: `bui-CheckboxLabel` class removed + - Data attribute: `data-checked` → `data-selected` + - Use without label is no longer supported + + Migration examples: + + Before: + + ```tsx + + ``` + + After: + + ```tsx + + Accept terms + + ``` + + Before: + + ```tsx + + ``` + + After: + + ```tsx + Option + ``` + + Before: + + ```tsx + + ``` + + After: + + ```tsx + + Accessible label + + ``` + +- b78fc45: **BREAKING**: Changed className prop behavior to augment default styles instead of being ignored or overriding them. + + Affected components: + + - Menu, MenuListBox, MenuAutocomplete, MenuAutocompleteListbox, MenuItem, MenuListBoxItem, MenuSection, MenuSeparator + - Switch + - Skeleton + - FieldLabel + - Header, HeaderToolbar + - HeaderPage + - Tabs, TabList, Tab, TabPanel + + If you were passing custom className values to any of these components that relied on the previous behavior, you may need to adjust your styles to account for the default classes now being applied alongside your custom classes. + +### Patch Changes + +- ff9f0c3: Enable tree-shaking of imports other than `*.css`. +- 1ef3ca4: Added new VisuallyHidden component for hiding content visually while keeping it accessible to screen readers. + +## 0.8.2-next.0 + +### Patch Changes + +- 26c6a78: Fix default text color in Backstage UI +- dac851f: Fix the default font size in Backstage UI. +- 3c0ea67: Fix CSS layer ordering in Backstage UI to make sure component styles are loaded after tokens and base declarations. +- 4eb455c: Fix font smoothing as default in Backstage UI. +- 00bfb83: Fix default font wight and font family in Backstage UI. + +## 0.8.0 + +### Minor Changes + +- 9acc1d6: **BREAKING**: Added a new `PasswordField` component. As part of this change, the `password` and `search` types have been removed from `TextField`. +- b0d11b5: **BREAKING** Restructure Backstage UI component styling to use CSS Modules instead of pure CSS. We don't expect this to be an issue in practice but it is important to call out that all styles are now loaded through CSS modules with generated class names. We are still providing fixed class names for all components to allow anyone to style their Backstage instance. +- 0c53517: **BREAKING** The ScrollArea component has been removed from Backstage UI because it did not meet our accessibility standards. +- 7b319c5: **BREAKING** Remove Icon component in Backstage UI. This component was creating issue for tree-shaking. It is recommended to use icons from @remixicon/react until we found a better alternative in Backstage UI. + +### Patch Changes + +- 2591b42: Adding a new Dialog component to Backstage UI. +- 827340f: remove default selection of tab +- 5dc17cc: Fix margin utility classes in Backstage UI. +- 85faee0: Fix scroll jumping when opening menu in Backstage UI. +- 3c921c5: Making href mandatory in tabs that are part of a Header component +- df7d2cf: Update react-aria-components to version 1.13.0 +- 507ee55: Fix table sorting icon position in Backstage UI. +- 8b7c3c9: Add new `virtualized`, `maxWidth` and `maxHeight` props to `Menu`, `MenuListBox`, `MenuAutocomplete` and `MenuAutocompleteListBox` to allow for virtalization of long lists inside menus. +- b940062: Added support for data attributes in ``, ``, ``, and `` components, ensuring they are correctly applied to the rendered elements. +- 206c801: Cleaning up Backstage UI props definitions as well as removing ScrollArea in Card to improve accessibility. +- 5c21e45: Add react router for internal routing for ButtonLinks +- 865bce8: Added a background color default on the body +- af4d9b4: We are restructuring our CSS to have a better layer structure. +- 9a47125: Improved SearchField component flex layout and animations. Fixed SearchField behavior in Header components by switching from width-based transitions to flex-basis transitions for better responsive behavior. Added new Storybook stories to test SearchField integration with Header component. +- 9781815: Remove auto selection of tabs for tabs that all have href defined +- 4adbb03: Avoid overriding onChange when spreading props +- f6dff5b: Using react router for internal links in the Menu component + +## 0.7.2-next.2 + +### Patch Changes + +- 3c921c5: Making href mandatory in tabs that are part of a Header component +- 5c21e45: Add react router for internal routing for ButtonLinks +- 9781815: Remove auto selection of tabs for tabs that all have href defined +- f6dff5b: Using react router for internal links in the Menu component + +## 0.7.2-next.1 + +### Patch Changes + +- a9b88be: Enable tooltips on disabled buttons with automatic wrapper +- 4adbb03: Avoid overriding onChange when spreading props + ## 0.7.2-next.0 ### Patch Changes diff --git a/packages/ui/css/styles.css b/packages/ui/css/styles.css index 8aa99175b3..c96eda05c9 100644 --- a/packages/ui/css/styles.css +++ b/packages/ui/css/styles.css @@ -1,4 +1,143 @@ +@layer tokens, base, components, utilities; + /*! modern-normalize v3.0.1 | MIT License | https://github.com/sindresorhus/modern-normalize */ +@layer tokens { + :root { + --bui-font-regular: system-ui; + --bui-font-monospace: ui-monospace, "Menlo", "Monaco", "Consolas", "Liberation Mono", "Courier New", monospace; + --bui-font-weight-regular: 400; + --bui-font-weight-bold: 600; + --bui-font-size-1: .625rem; + --bui-font-size-2: .75rem; + --bui-font-size-3: .875rem; + --bui-font-size-4: 1rem; + --bui-font-size-5: 1.25rem; + --bui-font-size-6: 1.5rem; + --bui-font-size-7: 2rem; + --bui-font-size-8: 3rem; + --bui-font-size-9: 4rem; + --bui-font-size-10: 5.75rem; + --bui-space: .25rem; + --bui-space-0_5: calc(var(--bui-space) * .5); + --bui-space-1: var(--bui-space); + --bui-space-1_5: calc(var(--bui-space) * 1.5); + --bui-space-2: calc(var(--bui-space) * 2); + --bui-space-3: calc(var(--bui-space) * 3); + --bui-space-4: calc(var(--bui-space) * 4); + --bui-space-5: calc(var(--bui-space) * 5); + --bui-space-6: calc(var(--bui-space) * 6); + --bui-space-7: calc(var(--bui-space) * 7); + --bui-space-8: calc(var(--bui-space) * 8); + --bui-space-9: calc(var(--bui-space) * 9); + --bui-space-10: calc(var(--bui-space) * 10); + --bui-space-11: calc(var(--bui-space) * 11); + --bui-space-12: calc(var(--bui-space) * 12); + --bui-space-13: calc(var(--bui-space) * 13); + --bui-space-14: calc(var(--bui-space) * 14); + --bui-radius-1: calc(.125rem); + --bui-radius-2: calc(.25rem); + --bui-radius-3: calc(.5rem); + --bui-radius-4: calc(.75rem); + --bui-radius-5: calc(1rem); + --bui-radius-6: calc(1.25rem); + --bui-radius-full: 9999px; + --bui-black: #000; + --bui-white: #fff; + --bui-gray-1: #f8f8f8; + --bui-gray-2: #ececec; + --bui-gray-3: #d9d9d9; + --bui-gray-4: #c1c1c1; + --bui-gray-5: #9e9e9e; + --bui-gray-6: #8c8c8c; + --bui-gray-7: #757575; + --bui-gray-8: #595959; + --bui-bg: var(--bui-gray-1); + --bui-bg-surface-1: var(--bui-white); + --bui-bg-surface-2: var(--bui-gray-2); + --bui-bg-solid: #1f5493; + --bui-bg-solid-hover: #163a66; + --bui-bg-solid-pressed: #0f2b4e; + --bui-bg-solid-disabled: #ebebeb; + --bui-bg-tint: transparent; + --bui-bg-tint-hover: #1f549366; + --bui-bg-tint-pressed: #1f549399; + --bui-bg-tint-disabled: #ebebeb; + --bui-bg-danger: #feebe7; + --bui-bg-warning: #fff2b2; + --bui-bg-success: #e6f6eb; + --bui-fg-primary: var(--bui-black); + --bui-fg-secondary: var(--bui-gray-7); + --bui-fg-link: #1f5493; + --bui-fg-link-hover: #1f2d5c; + --bui-fg-disabled: #9e9e9e; + --bui-fg-solid: var(--bui-white); + --bui-fg-solid-disabled: #9c9c9c; + --bui-fg-tint: #1f5493; + --bui-fg-tint-disabled: var(--bui-gray-5); + --bui-fg-danger: #e22b2b; + --bui-fg-warning: #e36d05; + --bui-fg-success: #1db954; + --bui-border: #0000001a; + --bui-border-hover: #0003; + --bui-border-pressed: #0006; + --bui-border-disabled: #0000001a; + --bui-border-danger: #f87a7a; + --bui-border-warning: #e36d05; + --bui-border-success: #53db83; + --bui-ring: #1f5493; + --bui-scrollbar: #a0a0a03b; + --bui-scrollbar-thumb: #a0a0a0; + --bui-animate-pulse: pulse 2s cubic-bezier(.4, 0, .6, 1) infinite; + } + + [data-theme-mode="dark"] { + --bui-gray-1: #191919; + --bui-gray-2: #242424; + --bui-gray-3: #373737; + --bui-gray-4: #464646; + --bui-gray-5: #575757; + --bui-gray-6: #7b7b7b; + --bui-gray-7: #9e9e9e; + --bui-gray-8: #b4b4b4; + --bui-bg: #333; + --bui-bg-surface-1: #424242; + --bui-bg-surface-2: var(--bui-gray-2); + --bui-bg-solid: #9cc9ff; + --bui-bg-solid-hover: #83b9fd; + --bui-bg-solid-pressed: #83b9fd; + --bui-bg-solid-disabled: #222; + --bui-bg-tint: transparent; + --bui-bg-tint-hover: #9cc9ff1f; + --bui-bg-tint-pressed: #9cc9ff29; + --bui-bg-tint-disabled: transparent; + --bui-bg-danger: #3b1219; + --bui-bg-warning: #302008; + --bui-bg-success: #132d21; + --bui-fg-primary: var(--bui-white); + --bui-fg-secondary: var(--bui-gray-7); + --bui-fg-link: #9cc9ff; + --bui-fg-link-hover: #7eb5f7; + --bui-fg-disabled: var(--bui-gray-7); + --bui-fg-solid: #101821; + --bui-fg-solid-disabled: var(--bui-gray-5); + --bui-fg-tint: #9cc9ff; + --bui-fg-tint-disabled: var(--bui-gray-5); + --bui-fg-danger: #e22b2b; + --bui-fg-warning: #e36d05; + --bui-fg-success: #1db954; + --bui-border: #ffffff1f; + --bui-border-hover: #fff6; + --bui-border-pressed: #ffffff80; + --bui-border-disabled: #fff3; + --bui-border-danger: #f87a7a; + --bui-border-warning: #e36d05; + --bui-border-success: #53db83; + --bui-ring: #1f5493; + --bui-scrollbar: #3636363a; + --bui-scrollbar-thumb: #575757; + } +} + @layer base { *, :before, :after { box-sizing: border-box; @@ -87,10929 +226,9035 @@ summary { display: list-item; } -} -.bui-p { - padding: var(--p); -} - -.bui-p-0\.5 { - padding: var(--bui-space-0_5); -} - -.bui-p-1 { - padding: var(--bui-space-1); -} - -.bui-p-1\.5 { - padding: var(--bui-space-1_5); -} - -.bui-p-2 { - padding: var(--bui-space-2); -} - -.bui-p-3 { - padding: var(--bui-space-3); -} - -.bui-p-4 { - padding: var(--bui-space-4); -} - -.bui-p-5 { - padding: var(--bui-space-5); -} - -.bui-p-6 { - padding: var(--bui-space-6); -} - -.bui-p-7 { - padding: var(--bui-space-7); -} - -.bui-p-8 { - padding: var(--bui-space-8); -} - -.bui-p-9 { - padding: var(--bui-space-9); -} - -.bui-p-10 { - padding: var(--bui-space-10); -} - -.bui-p-11 { - padding: var(--bui-space-11); -} - -.bui-p-12 { - padding: var(--bui-space-12); -} - -.bui-p-13 { - padding: var(--bui-space-13); -} - -.bui-p-14 { - padding: var(--bui-space-14); -} - -@media (width >= 640px) { - .xs\:bui-p { - padding: var(--p-xs); - } - - .xs\:bui-p-0\.5 { - padding: var(--bui-space-0_5); - } - - .xs\:bui-p-1 { - padding: var(--bui-space-1); - } - - .xs\:bui-p-1\.5 { - padding: var(--bui-space-1_5); - } - - .xs\:bui-p-2 { - padding: var(--bui-space-2); - } - - .xs\:bui-p-3 { - padding: var(--bui-space-3); - } - - .xs\:bui-p-4 { - padding: var(--bui-space-4); - } - - .xs\:bui-p-5 { - padding: var(--bui-space-5); - } - - .xs\:bui-p-6 { - padding: var(--bui-space-6); - } - - .xs\:bui-p-7 { - padding: var(--bui-space-7); - } - - .xs\:bui-p-8 { - padding: var(--bui-space-8); - } - - .xs\:bui-p-9 { - padding: var(--bui-space-9); - } - - .xs\:bui-p-10 { - padding: var(--bui-space-10); - } - - .xs\:bui-p-11 { - padding: var(--bui-space-11); - } - - .xs\:bui-p-12 { - padding: var(--bui-space-12); - } - - .xs\:bui-p-13 { - padding: var(--bui-space-13); - } - - .xs\:bui-p-14 { - padding: var(--bui-space-14); - } -} - -@media (width >= 768px) { - .sm\:bui-p { - padding: var(--p-sm); - } - - .sm\:bui-p-0\.5 { - padding: var(--bui-space-0_5); - } - - .sm\:bui-p-1 { - padding: var(--bui-space-1); - } - - .sm\:bui-p-1\.5 { - padding: var(--bui-space-1_5); - } - - .sm\:bui-p-2 { - padding: var(--bui-space-2); - } - - .sm\:bui-p-3 { - padding: var(--bui-space-3); - } - - .sm\:bui-p-4 { - padding: var(--bui-space-4); - } - - .sm\:bui-p-5 { - padding: var(--bui-space-5); - } - - .sm\:bui-p-6 { - padding: var(--bui-space-6); - } - - .sm\:bui-p-7 { - padding: var(--bui-space-7); - } - - .sm\:bui-p-8 { - padding: var(--bui-space-8); - } - - .sm\:bui-p-9 { - padding: var(--bui-space-9); - } - - .sm\:bui-p-10 { - padding: var(--bui-space-10); - } - - .sm\:bui-p-11 { - padding: var(--bui-space-11); - } - - .sm\:bui-p-12 { - padding: var(--bui-space-12); - } - - .sm\:bui-p-13 { - padding: var(--bui-space-13); - } - - .sm\:bui-p-14 { - padding: var(--bui-space-14); - } -} - -@media (width >= 1024px) { - .md\:bui-p { - padding: var(--p-md); - } - - .md\:bui-p-0\.5 { - padding: var(--bui-space-0_5); - } - - .md\:bui-p-1 { - padding: var(--bui-space-1); - } - - .md\:bui-p-1\.5 { - padding: var(--bui-space-1_5); - } - - .md\:bui-p-2 { - padding: var(--bui-space-2); - } - - .md\:bui-p-3 { - padding: var(--bui-space-3); - } - - .md\:bui-p-4 { - padding: var(--bui-space-4); - } - - .md\:bui-p-5 { - padding: var(--bui-space-5); - } - - .md\:bui-p-6 { - padding: var(--bui-space-6); - } - - .md\:bui-p-7 { - padding: var(--bui-space-7); - } - - .md\:bui-p-8 { - padding: var(--bui-space-8); - } - - .md\:bui-p-9 { - padding: var(--bui-space-9); - } - - .md\:bui-p-10 { - padding: var(--bui-space-10); - } - - .md\:bui-p-11 { - padding: var(--bui-space-11); - } - - .md\:bui-p-12 { - padding: var(--bui-space-12); - } - - .md\:bui-p-13 { - padding: var(--bui-space-13); - } - - .md\:bui-p-14 { - padding: var(--bui-space-14); - } -} - -@media (width >= 1280px) { - .lg\:bui-p { - padding: var(--p-lg); - } - - .lg\:bui-p-0\.5 { - padding: var(--bui-space-0_5); - } - - .lg\:bui-p-1 { - padding: var(--bui-space-1); - } - - .lg\:bui-p-1\.5 { - padding: var(--bui-space-1_5); - } - - .lg\:bui-p-2 { - padding: var(--bui-space-2); - } - - .lg\:bui-p-3 { - padding: var(--bui-space-3); - } - - .lg\:bui-p-4 { - padding: var(--bui-space-4); - } - - .lg\:bui-p-5 { - padding: var(--bui-space-5); - } - - .lg\:bui-p-6 { - padding: var(--bui-space-6); - } - - .lg\:bui-p-7 { - padding: var(--bui-space-7); - } - - .lg\:bui-p-8 { - padding: var(--bui-space-8); - } - - .lg\:bui-p-9 { - padding: var(--bui-space-9); - } - - .lg\:bui-p-10 { - padding: var(--bui-space-10); - } - - .lg\:bui-p-11 { - padding: var(--bui-space-11); - } - - .lg\:bui-p-12 { - padding: var(--bui-space-12); - } - - .lg\:bui-p-13 { - padding: var(--bui-space-13); - } - - .lg\:bui-p-14 { - padding: var(--bui-space-14); - } -} - -@media (width >= 1536px) { - .xl\:bui-p { - padding: var(--p-xl); - } - - .xl\:bui-p-0\.5 { - padding: var(--bui-space-0_5); - } - - .xl\:bui-p-1 { - padding: var(--bui-space-1); - } - - .xl\:bui-p-1\.5 { - padding: var(--bui-space-1_5); - } - - .xl\:bui-p-2 { - padding: var(--bui-space-2); - } - - .xl\:bui-p-3 { - padding: var(--bui-space-3); - } - - .xl\:bui-p-4 { - padding: var(--bui-space-4); - } - - .xl\:bui-p-5 { - padding: var(--bui-space-5); - } - - .xl\:bui-p-6 { - padding: var(--bui-space-6); - } - - .xl\:bui-p-7 { - padding: var(--bui-space-7); - } - - .xl\:bui-p-8 { - padding: var(--bui-space-8); - } - - .xl\:bui-p-9 { - padding: var(--bui-space-9); - } - - .xl\:bui-p-10 { - padding: var(--bui-space-10); - } - - .xl\:bui-p-11 { - padding: var(--bui-space-11); - } - - .xl\:bui-p-12 { - padding: var(--bui-space-12); - } - - .xl\:bui-p-13 { - padding: var(--bui-space-13); - } - - .xl\:bui-p-14 { - padding: var(--bui-space-14); - } -} - -.bui-pl { - padding-left: var(--pl); -} - -.bui-pl-0\.5 { - padding-left: var(--bui-space-0_5); -} - -.bui-pl-1 { - padding-left: var(--bui-space-1); -} - -.bui-pl-1\.5 { - padding-left: var(--bui-space-1_5); -} - -.bui-pl-2 { - padding-left: var(--bui-space-2); -} - -.bui-pl-3 { - padding-left: var(--bui-space-3); -} - -.bui-pl-4 { - padding-left: var(--bui-space-4); -} - -.bui-pl-5 { - padding-left: var(--bui-space-5); -} - -.bui-pl-6 { - padding-left: var(--bui-space-6); -} - -.bui-pl-7 { - padding-left: var(--bui-space-7); -} - -.bui-pl-8 { - padding-left: var(--bui-space-8); -} - -.bui-pl-9 { - padding-left: var(--bui-space-9); -} - -.bui-pl-10 { - padding-left: var(--bui-space-10); -} - -.bui-pl-11 { - padding-left: var(--bui-space-11); -} - -.bui-pl-12 { - padding-left: var(--bui-space-12); -} - -.bui-pl-13 { - padding-left: var(--bui-space-13); -} - -.bui-pl-14 { - padding-left: var(--bui-space-14); -} - -@media (width >= 640px) { - .xs\:bui-pl { - padding-left: var(--pl-xs); - } - - .xs\:bui-pl-0\.5 { - padding-left: var(--bui-space-0_5); - } - - .xs\:bui-pl-1 { - padding-left: var(--bui-space-1); - } - - .xs\:bui-pl-1\.5 { - padding-left: var(--bui-space-1_5); - } - - .xs\:bui-pl-2 { - padding-left: var(--bui-space-2); - } - - .xs\:bui-pl-3 { - padding-left: var(--bui-space-3); - } - - .xs\:bui-pl-4 { - padding-left: var(--bui-space-4); - } - - .xs\:bui-pl-5 { - padding-left: var(--bui-space-5); - } - - .xs\:bui-pl-6 { - padding-left: var(--bui-space-6); - } - - .xs\:bui-pl-7 { - padding-left: var(--bui-space-7); - } - - .xs\:bui-pl-8 { - padding-left: var(--bui-space-8); - } - - .xs\:bui-pl-9 { - padding-left: var(--bui-space-9); - } - - .xs\:bui-pl-10 { - padding-left: var(--bui-space-10); - } - - .xs\:bui-pl-11 { - padding-left: var(--bui-space-11); - } - - .xs\:bui-pl-12 { - padding-left: var(--bui-space-12); - } - - .xs\:bui-pl-13 { - padding-left: var(--bui-space-13); - } - - .xs\:bui-pl-14 { - padding-left: var(--bui-space-14); - } -} - -@media (width >= 768px) { - .sm\:bui-pl { - padding-left: var(--pl-sm); - } - - .sm\:bui-pl-0\.5 { - padding-left: var(--bui-space-0_5); - } - - .sm\:bui-pl-1 { - padding-left: var(--bui-space-1); - } - - .sm\:bui-pl-1\.5 { - padding-left: var(--bui-space-1_5); - } - - .sm\:bui-pl-2 { - padding-left: var(--bui-space-2); - } - - .sm\:bui-pl-3 { - padding-left: var(--bui-space-3); - } - - .sm\:bui-pl-4 { - padding-left: var(--bui-space-4); - } - - .sm\:bui-pl-5 { - padding-left: var(--bui-space-5); - } - - .sm\:bui-pl-6 { - padding-left: var(--bui-space-6); - } - - .sm\:bui-pl-7 { - padding-left: var(--bui-space-7); - } - - .sm\:bui-pl-8 { - padding-left: var(--bui-space-8); - } - - .sm\:bui-pl-9 { - padding-left: var(--bui-space-9); - } - - .sm\:bui-pl-10 { - padding-left: var(--bui-space-10); - } - - .sm\:bui-pl-11 { - padding-left: var(--bui-space-11); - } - - .sm\:bui-pl-12 { - padding-left: var(--bui-space-12); - } - - .sm\:bui-pl-13 { - padding-left: var(--bui-space-13); - } - - .sm\:bui-pl-14 { - padding-left: var(--bui-space-14); - } -} - -@media (width >= 1024px) { - .md\:bui-pl { - padding-left: var(--pl-md); - } - - .md\:bui-pl-0\.5 { - padding-left: var(--bui-space-0_5); - } - - .md\:bui-pl-1 { - padding-left: var(--bui-space-1); - } - - .md\:bui-pl-1\.5 { - padding-left: var(--bui-space-1_5); - } - - .md\:bui-pl-2 { - padding-left: var(--bui-space-2); - } - - .md\:bui-pl-3 { - padding-left: var(--bui-space-3); - } - - .md\:bui-pl-4 { - padding-left: var(--bui-space-4); - } - - .md\:bui-pl-5 { - padding-left: var(--bui-space-5); - } - - .md\:bui-pl-6 { - padding-left: var(--bui-space-6); - } - - .md\:bui-pl-7 { - padding-left: var(--bui-space-7); - } - - .md\:bui-pl-8 { - padding-left: var(--bui-space-8); - } - - .md\:bui-pl-9 { - padding-left: var(--bui-space-9); - } - - .md\:bui-pl-10 { - padding-left: var(--bui-space-10); - } - - .md\:bui-pl-11 { - padding-left: var(--bui-space-11); - } - - .md\:bui-pl-12 { - padding-left: var(--bui-space-12); - } - - .md\:bui-pl-13 { - padding-left: var(--bui-space-13); - } - - .md\:bui-pl-14 { - padding-left: var(--bui-space-14); - } -} - -@media (width >= 1280px) { - .lg\:bui-pl { - padding-left: var(--pl-lg); - } - - .lg\:bui-pl-0\.5 { - padding-left: var(--bui-space-0_5); - } - - .lg\:bui-pl-1 { - padding-left: var(--bui-space-1); - } - - .lg\:bui-pl-1\.5 { - padding-left: var(--bui-space-1_5); - } - - .lg\:bui-pl-2 { - padding-left: var(--bui-space-2); - } - - .lg\:bui-pl-3 { - padding-left: var(--bui-space-3); - } - - .lg\:bui-pl-4 { - padding-left: var(--bui-space-4); - } - - .lg\:bui-pl-5 { - padding-left: var(--bui-space-5); - } - - .lg\:bui-pl-6 { - padding-left: var(--bui-space-6); - } - - .lg\:bui-pl-7 { - padding-left: var(--bui-space-7); - } - - .lg\:bui-pl-8 { - padding-left: var(--bui-space-8); - } - - .lg\:bui-pl-9 { - padding-left: var(--bui-space-9); - } - - .lg\:bui-pl-10 { - padding-left: var(--bui-space-10); - } - - .lg\:bui-pl-11 { - padding-left: var(--bui-space-11); - } - - .lg\:bui-pl-12 { - padding-left: var(--bui-space-12); - } - - .lg\:bui-pl-13 { - padding-left: var(--bui-space-13); - } - - .lg\:bui-pl-14 { - padding-left: var(--bui-space-14); - } -} - -@media (width >= 1536px) { - .xl\:bui-pl { - padding-left: var(--pl-xl); - } - - .xl\:bui-pl-0\.5 { - padding-left: var(--bui-space-0_5); - } - - .xl\:bui-pl-1 { - padding-left: var(--bui-space-1); - } - - .xl\:bui-pl-1\.5 { - padding-left: var(--bui-space-1_5); - } - - .xl\:bui-pl-2 { - padding-left: var(--bui-space-2); - } - - .xl\:bui-pl-3 { - padding-left: var(--bui-space-3); - } - - .xl\:bui-pl-4 { - padding-left: var(--bui-space-4); - } - - .xl\:bui-pl-5 { - padding-left: var(--bui-space-5); - } - - .xl\:bui-pl-6 { - padding-left: var(--bui-space-6); - } - - .xl\:bui-pl-7 { - padding-left: var(--bui-space-7); - } - - .xl\:bui-pl-8 { - padding-left: var(--bui-space-8); - } - - .xl\:bui-pl-9 { - padding-left: var(--bui-space-9); - } - - .xl\:bui-pl-10 { - padding-left: var(--bui-space-10); - } - - .xl\:bui-pl-11 { - padding-left: var(--bui-space-11); - } - - .xl\:bui-pl-12 { - padding-left: var(--bui-space-12); - } - - .xl\:bui-pl-13 { - padding-left: var(--bui-space-13); - } - - .xl\:bui-pl-14 { - padding-left: var(--bui-space-14); - } -} - -.bui-pr { - padding-right: var(--pr); -} - -.bui-pr-0\.5 { - padding-right: var(--bui-space-0_5); -} - -.bui-pr-1 { - padding-right: var(--bui-space-1); -} - -.bui-pr-1\.5 { - padding-right: var(--bui-space-1_5); -} - -.bui-pr-2 { - padding-right: var(--bui-space-2); -} - -.bui-pr-3 { - padding-right: var(--bui-space-3); -} - -.bui-pr-4 { - padding-right: var(--bui-space-4); -} - -.bui-pr-5 { - padding-right: var(--bui-space-5); -} - -.bui-pr-6 { - padding-right: var(--bui-space-6); -} - -.bui-pr-7 { - padding-right: var(--bui-space-7); -} - -.bui-pr-8 { - padding-right: var(--bui-space-8); -} - -.bui-pr-9 { - padding-right: var(--bui-space-9); -} - -.bui-pr-10 { - padding-right: var(--bui-space-10); -} - -.bui-pr-11 { - padding-right: var(--bui-space-11); -} - -.bui-pr-12 { - padding-right: var(--bui-space-12); -} - -.bui-pr-13 { - padding-right: var(--bui-space-13); -} - -.bui-pr-14 { - padding-right: var(--bui-space-14); -} - -@media (width >= 640px) { - .xs\:bui-pr { - padding-right: var(--pr-xs); - } - - .xs\:bui-pr-0\.5 { - padding-right: var(--bui-space-0_5); - } - - .xs\:bui-pr-1 { - padding-right: var(--bui-space-1); - } - - .xs\:bui-pr-1\.5 { - padding-right: var(--bui-space-1_5); - } - - .xs\:bui-pr-2 { - padding-right: var(--bui-space-2); - } - - .xs\:bui-pr-3 { - padding-right: var(--bui-space-3); - } - - .xs\:bui-pr-4 { - padding-right: var(--bui-space-4); - } - - .xs\:bui-pr-5 { - padding-right: var(--bui-space-5); - } - - .xs\:bui-pr-6 { - padding-right: var(--bui-space-6); - } - - .xs\:bui-pr-7 { - padding-right: var(--bui-space-7); - } - - .xs\:bui-pr-8 { - padding-right: var(--bui-space-8); - } - - .xs\:bui-pr-9 { - padding-right: var(--bui-space-9); - } - - .xs\:bui-pr-10 { - padding-right: var(--bui-space-10); - } - - .xs\:bui-pr-11 { - padding-right: var(--bui-space-11); - } - - .xs\:bui-pr-12 { - padding-right: var(--bui-space-12); - } - - .xs\:bui-pr-13 { - padding-right: var(--bui-space-13); - } - - .xs\:bui-pr-14 { - padding-right: var(--bui-space-14); - } -} - -@media (width >= 768px) { - .sm\:bui-pr { - padding-right: var(--pr-sm); - } - - .sm\:bui-pr-0\.5 { - padding-right: var(--bui-space-0_5); - } - - .sm\:bui-pr-1 { - padding-right: var(--bui-space-1); - } - - .sm\:bui-pr-1\.5 { - padding-right: var(--bui-space-1_5); - } - - .sm\:bui-pr-2 { - padding-right: var(--bui-space-2); - } - - .sm\:bui-pr-3 { - padding-right: var(--bui-space-3); - } - - .sm\:bui-pr-4 { - padding-right: var(--bui-space-4); - } - - .sm\:bui-pr-5 { - padding-right: var(--bui-space-5); - } - - .sm\:bui-pr-6 { - padding-right: var(--bui-space-6); - } - - .sm\:bui-pr-7 { - padding-right: var(--bui-space-7); - } - - .sm\:bui-pr-8 { - padding-right: var(--bui-space-8); - } - - .sm\:bui-pr-9 { - padding-right: var(--bui-space-9); - } - - .sm\:bui-pr-10 { - padding-right: var(--bui-space-10); - } - - .sm\:bui-pr-11 { - padding-right: var(--bui-space-11); - } - - .sm\:bui-pr-12 { - padding-right: var(--bui-space-12); - } - - .sm\:bui-pr-13 { - padding-right: var(--bui-space-13); - } - - .sm\:bui-pr-14 { - padding-right: var(--bui-space-14); - } -} - -@media (width >= 1024px) { - .md\:bui-pr { - padding-right: var(--pr-md); - } - - .md\:bui-pr-0\.5 { - padding-right: var(--bui-space-0_5); - } - - .md\:bui-pr-1 { - padding-right: var(--bui-space-1); - } - - .md\:bui-pr-1\.5 { - padding-right: var(--bui-space-1_5); - } - - .md\:bui-pr-2 { - padding-right: var(--bui-space-2); - } - - .md\:bui-pr-3 { - padding-right: var(--bui-space-3); - } - - .md\:bui-pr-4 { - padding-right: var(--bui-space-4); - } - - .md\:bui-pr-5 { - padding-right: var(--bui-space-5); - } - - .md\:bui-pr-6 { - padding-right: var(--bui-space-6); - } - - .md\:bui-pr-7 { - padding-right: var(--bui-space-7); - } - - .md\:bui-pr-8 { - padding-right: var(--bui-space-8); - } - - .md\:bui-pr-9 { - padding-right: var(--bui-space-9); - } - - .md\:bui-pr-10 { - padding-right: var(--bui-space-10); - } - - .md\:bui-pr-11 { - padding-right: var(--bui-space-11); - } - - .md\:bui-pr-12 { - padding-right: var(--bui-space-12); - } - - .md\:bui-pr-13 { - padding-right: var(--bui-space-13); - } - - .md\:bui-pr-14 { - padding-right: var(--bui-space-14); - } -} - -@media (width >= 1280px) { - .lg\:bui-pr { - padding-right: var(--pr-lg); - } - - .lg\:bui-pr-0\.5 { - padding-right: var(--bui-space-0_5); - } - - .lg\:bui-pr-1 { - padding-right: var(--bui-space-1); - } - - .lg\:bui-pr-1\.5 { - padding-right: var(--bui-space-1_5); - } - - .lg\:bui-pr-2 { - padding-right: var(--bui-space-2); - } - - .lg\:bui-pr-3 { - padding-right: var(--bui-space-3); - } - - .lg\:bui-pr-4 { - padding-right: var(--bui-space-4); - } - - .lg\:bui-pr-5 { - padding-right: var(--bui-space-5); - } - - .lg\:bui-pr-6 { - padding-right: var(--bui-space-6); - } - - .lg\:bui-pr-7 { - padding-right: var(--bui-space-7); - } - - .lg\:bui-pr-8 { - padding-right: var(--bui-space-8); - } - - .lg\:bui-pr-9 { - padding-right: var(--bui-space-9); - } - - .lg\:bui-pr-10 { - padding-right: var(--bui-space-10); - } - - .lg\:bui-pr-11 { - padding-right: var(--bui-space-11); - } - - .lg\:bui-pr-12 { - padding-right: var(--bui-space-12); - } - - .lg\:bui-pr-13 { - padding-right: var(--bui-space-13); - } - - .lg\:bui-pr-14 { - padding-right: var(--bui-space-14); - } -} - -@media (width >= 1536px) { - .xl\:bui-pr { - padding-right: var(--pr-xl); - } - - .xl\:bui-pr-0\.5 { - padding-right: var(--bui-space-0_5); - } - - .xl\:bui-pr-1 { - padding-right: var(--bui-space-1); - } - - .xl\:bui-pr-1\.5 { - padding-right: var(--bui-space-1_5); - } - - .xl\:bui-pr-2 { - padding-right: var(--bui-space-2); - } - - .xl\:bui-pr-3 { - padding-right: var(--bui-space-3); - } - - .xl\:bui-pr-4 { - padding-right: var(--bui-space-4); - } - - .xl\:bui-pr-5 { - padding-right: var(--bui-space-5); - } - - .xl\:bui-pr-6 { - padding-right: var(--bui-space-6); - } - - .xl\:bui-pr-7 { - padding-right: var(--bui-space-7); - } - - .xl\:bui-pr-8 { - padding-right: var(--bui-space-8); - } - - .xl\:bui-pr-9 { - padding-right: var(--bui-space-9); - } - - .xl\:bui-pr-10 { - padding-right: var(--bui-space-10); - } - - .xl\:bui-pr-11 { - padding-right: var(--bui-space-11); - } - - .xl\:bui-pr-12 { - padding-right: var(--bui-space-12); - } - - .xl\:bui-pr-13 { - padding-right: var(--bui-space-13); - } - - .xl\:bui-pr-14 { - padding-right: var(--bui-space-14); - } -} - -.bui-pt { - padding-top: var(--pt); -} - -.bui-pt-0\.5 { - padding-top: var(--bui-space-0_5); -} - -.bui-pt-1 { - padding-top: var(--bui-space-1); -} - -.bui-pt-1\.5 { - padding-top: var(--bui-space-1_5); -} - -.bui-pt-2 { - padding-top: var(--bui-space-2); -} - -.bui-pt-3 { - padding-top: var(--bui-space-3); -} - -.bui-pt-4 { - padding-top: var(--bui-space-4); -} - -.bui-pt-5 { - padding-top: var(--bui-space-5); -} - -.bui-pt-6 { - padding-top: var(--bui-space-6); -} - -.bui-pt-7 { - padding-top: var(--bui-space-7); -} - -.bui-pt-8 { - padding-top: var(--bui-space-8); -} - -.bui-pt-9 { - padding-top: var(--bui-space-9); -} - -.bui-pt-10 { - padding-top: var(--bui-space-10); -} - -.bui-pt-11 { - padding-top: var(--bui-space-11); -} - -.bui-pt-12 { - padding-top: var(--bui-space-12); -} - -.bui-pt-13 { - padding-top: var(--bui-space-13); -} - -.bui-pt-14 { - padding-top: var(--bui-space-14); -} - -@media (width >= 640px) { - .xs\:bui-pt { - padding-top: var(--pt-xs); - } - - .xs\:bui-pt-0\.5 { - padding-top: var(--bui-space-0_5); - } - - .xs\:bui-pt-1 { - padding-top: var(--bui-space-1); - } - - .xs\:bui-pt-1\.5 { - padding-top: var(--bui-space-1_5); - } - - .xs\:bui-pt-2 { - padding-top: var(--bui-space-2); - } - - .xs\:bui-pt-3 { - padding-top: var(--bui-space-3); - } - - .xs\:bui-pt-4 { - padding-top: var(--bui-space-4); - } - - .xs\:bui-pt-5 { - padding-top: var(--bui-space-5); - } - - .xs\:bui-pt-6 { - padding-top: var(--bui-space-6); - } - - .xs\:bui-pt-7 { - padding-top: var(--bui-space-7); - } - - .xs\:bui-pt-8 { - padding-top: var(--bui-space-8); - } - - .xs\:bui-pt-9 { - padding-top: var(--bui-space-9); - } - - .xs\:bui-pt-10 { - padding-top: var(--bui-space-10); - } - - .xs\:bui-pt-11 { - padding-top: var(--bui-space-11); - } - - .xs\:bui-pt-12 { - padding-top: var(--bui-space-12); - } - - .xs\:bui-pt-13 { - padding-top: var(--bui-space-13); - } - - .xs\:bui-pt-14 { - padding-top: var(--bui-space-14); - } -} - -@media (width >= 768px) { - .sm\:bui-pt { - padding-top: var(--pt-sm); - } - - .sm\:bui-pt-0\.5 { - padding-top: var(--bui-space-0_5); - } - - .sm\:bui-pt-1 { - padding-top: var(--bui-space-1); - } - - .sm\:bui-pt-1\.5 { - padding-top: var(--bui-space-1_5); - } - - .sm\:bui-pt-2 { - padding-top: var(--bui-space-2); - } - - .sm\:bui-pt-3 { - padding-top: var(--bui-space-3); - } - - .sm\:bui-pt-4 { - padding-top: var(--bui-space-4); - } - - .sm\:bui-pt-5 { - padding-top: var(--bui-space-5); - } - - .sm\:bui-pt-6 { - padding-top: var(--bui-space-6); - } - - .sm\:bui-pt-7 { - padding-top: var(--bui-space-7); - } - - .sm\:bui-pt-8 { - padding-top: var(--bui-space-8); - } - - .sm\:bui-pt-9 { - padding-top: var(--bui-space-9); - } - - .sm\:bui-pt-10 { - padding-top: var(--bui-space-10); - } - - .sm\:bui-pt-11 { - padding-top: var(--bui-space-11); - } - - .sm\:bui-pt-12 { - padding-top: var(--bui-space-12); - } - - .sm\:bui-pt-13 { - padding-top: var(--bui-space-13); - } - - .sm\:bui-pt-14 { - padding-top: var(--bui-space-14); - } -} - -@media (width >= 1024px) { - .md\:bui-pt { - padding-top: var(--pt-md); - } - - .md\:bui-pt-0\.5 { - padding-top: var(--bui-space-0_5); - } - - .md\:bui-pt-1 { - padding-top: var(--bui-space-1); - } - - .md\:bui-pt-1\.5 { - padding-top: var(--bui-space-1_5); - } - - .md\:bui-pt-2 { - padding-top: var(--bui-space-2); - } - - .md\:bui-pt-3 { - padding-top: var(--bui-space-3); - } - - .md\:bui-pt-4 { - padding-top: var(--bui-space-4); - } - - .md\:bui-pt-5 { - padding-top: var(--bui-space-5); - } - - .md\:bui-pt-6 { - padding-top: var(--bui-space-6); - } - - .md\:bui-pt-7 { - padding-top: var(--bui-space-7); - } - - .md\:bui-pt-8 { - padding-top: var(--bui-space-8); - } - - .md\:bui-pt-9 { - padding-top: var(--bui-space-9); - } - - .md\:bui-pt-10 { - padding-top: var(--bui-space-10); - } - - .md\:bui-pt-11 { - padding-top: var(--bui-space-11); - } - - .md\:bui-pt-12 { - padding-top: var(--bui-space-12); - } - - .md\:bui-pt-13 { - padding-top: var(--bui-space-13); - } - - .md\:bui-pt-14 { - padding-top: var(--bui-space-14); - } -} - -@media (width >= 1280px) { - .lg\:bui-pt { - padding-top: var(--pt-lg); - } - - .lg\:bui-pt-0\.5 { - padding-top: var(--bui-space-0_5); - } - - .lg\:bui-pt-1 { - padding-top: var(--bui-space-1); - } - - .lg\:bui-pt-1\.5 { - padding-top: var(--bui-space-1_5); - } - - .lg\:bui-pt-2 { - padding-top: var(--bui-space-2); - } - - .lg\:bui-pt-3 { - padding-top: var(--bui-space-3); - } - - .lg\:bui-pt-4 { - padding-top: var(--bui-space-4); - } - - .lg\:bui-pt-5 { - padding-top: var(--bui-space-5); - } - - .lg\:bui-pt-6 { - padding-top: var(--bui-space-6); - } - - .lg\:bui-pt-7 { - padding-top: var(--bui-space-7); - } - - .lg\:bui-pt-8 { - padding-top: var(--bui-space-8); - } - - .lg\:bui-pt-9 { - padding-top: var(--bui-space-9); - } - - .lg\:bui-pt-10 { - padding-top: var(--bui-space-10); - } - - .lg\:bui-pt-11 { - padding-top: var(--bui-space-11); - } - - .lg\:bui-pt-12 { - padding-top: var(--bui-space-12); - } - - .lg\:bui-pt-13 { - padding-top: var(--bui-space-13); - } - - .lg\:bui-pt-14 { - padding-top: var(--bui-space-14); - } -} - -@media (width >= 1536px) { - .xl\:bui-pt { - padding-top: var(--pt-xl); - } - - .xl\:bui-pt-0\.5 { - padding-top: var(--bui-space-0_5); - } - - .xl\:bui-pt-1 { - padding-top: var(--bui-space-1); - } - - .xl\:bui-pt-1\.5 { - padding-top: var(--bui-space-1_5); - } - - .xl\:bui-pt-2 { - padding-top: var(--bui-space-2); - } - - .xl\:bui-pt-3 { - padding-top: var(--bui-space-3); - } - - .xl\:bui-pt-4 { - padding-top: var(--bui-space-4); - } - - .xl\:bui-pt-5 { - padding-top: var(--bui-space-5); - } - - .xl\:bui-pt-6 { - padding-top: var(--bui-space-6); - } - - .xl\:bui-pt-7 { - padding-top: var(--bui-space-7); - } - - .xl\:bui-pt-8 { - padding-top: var(--bui-space-8); - } - - .xl\:bui-pt-9 { - padding-top: var(--bui-space-9); - } - - .xl\:bui-pt-10 { - padding-top: var(--bui-space-10); - } - - .xl\:bui-pt-11 { - padding-top: var(--bui-space-11); - } - - .xl\:bui-pt-12 { - padding-top: var(--bui-space-12); - } - - .xl\:bui-pt-13 { - padding-top: var(--bui-space-13); - } - - .xl\:bui-pt-14 { - padding-top: var(--bui-space-14); - } -} - -.bui-pb { - padding-bottom: var(--pb); -} - -.bui-pb-0\.5 { - padding-bottom: var(--bui-space-0_5); -} - -.bui-pb-1 { - padding-bottom: var(--bui-space-1); -} - -.bui-pb-1\.5 { - padding-bottom: var(--bui-space-1_5); -} - -.bui-pb-2 { - padding-bottom: var(--bui-space-2); -} - -.bui-pb-3 { - padding-bottom: var(--bui-space-3); -} - -.bui-pb-4 { - padding-bottom: var(--bui-space-4); -} - -.bui-pb-5 { - padding-bottom: var(--bui-space-5); -} - -.bui-pb-6 { - padding-bottom: var(--bui-space-6); -} - -.bui-pb-7 { - padding-bottom: var(--bui-space-7); -} - -.bui-pb-8 { - padding-bottom: var(--bui-space-8); -} - -.bui-pb-9 { - padding-bottom: var(--bui-space-9); -} - -.bui-pb-10 { - padding-bottom: var(--bui-space-10); -} - -.bui-pb-11 { - padding-bottom: var(--bui-space-11); -} - -.bui-pb-12 { - padding-bottom: var(--bui-space-12); -} - -.bui-pb-13 { - padding-bottom: var(--bui-space-13); -} - -.bui-pb-14 { - padding-bottom: var(--bui-space-14); -} - -@media (width >= 640px) { - .xs\:bui-pb { - padding-bottom: var(--pb-xs); - } - - .xs\:bui-pb-0\.5 { - padding-bottom: var(--bui-space-0_5); - } - - .xs\:bui-pb-1 { - padding-bottom: var(--bui-space-1); - } - - .xs\:bui-pb-1\.5 { - padding-bottom: var(--bui-space-1_5); - } - - .xs\:bui-pb-2 { - padding-bottom: var(--bui-space-2); - } - - .xs\:bui-pb-3 { - padding-bottom: var(--bui-space-3); - } - - .xs\:bui-pb-4 { - padding-bottom: var(--bui-space-4); - } - - .xs\:bui-pb-5 { - padding-bottom: var(--bui-space-5); - } - - .xs\:bui-pb-6 { - padding-bottom: var(--bui-space-6); - } - - .xs\:bui-pb-7 { - padding-bottom: var(--bui-space-7); - } - - .xs\:bui-pb-8 { - padding-bottom: var(--bui-space-8); - } - - .xs\:bui-pb-9 { - padding-bottom: var(--bui-space-9); - } - - .xs\:bui-pb-10 { - padding-bottom: var(--bui-space-10); - } - - .xs\:bui-pb-11 { - padding-bottom: var(--bui-space-11); - } - - .xs\:bui-pb-12 { - padding-bottom: var(--bui-space-12); - } - - .xs\:bui-pb-13 { - padding-bottom: var(--bui-space-13); - } - - .xs\:bui-pb-14 { - padding-bottom: var(--bui-space-14); - } -} - -@media (width >= 768px) { - .sm\:bui-pb { - padding-bottom: var(--pb-sm); - } - - .sm\:bui-pb-0\.5 { - padding-bottom: var(--bui-space-0_5); - } - - .sm\:bui-pb-1 { - padding-bottom: var(--bui-space-1); - } - - .sm\:bui-pb-1\.5 { - padding-bottom: var(--bui-space-1_5); - } - - .sm\:bui-pb-2 { - padding-bottom: var(--bui-space-2); - } - - .sm\:bui-pb-3 { - padding-bottom: var(--bui-space-3); - } - - .sm\:bui-pb-4 { - padding-bottom: var(--bui-space-4); - } - - .sm\:bui-pb-5 { - padding-bottom: var(--bui-space-5); - } - - .sm\:bui-pb-6 { - padding-bottom: var(--bui-space-6); - } - - .sm\:bui-pb-7 { - padding-bottom: var(--bui-space-7); - } - - .sm\:bui-pb-8 { - padding-bottom: var(--bui-space-8); - } - - .sm\:bui-pb-9 { - padding-bottom: var(--bui-space-9); - } - - .sm\:bui-pb-10 { - padding-bottom: var(--bui-space-10); - } - - .sm\:bui-pb-11 { - padding-bottom: var(--bui-space-11); - } - - .sm\:bui-pb-12 { - padding-bottom: var(--bui-space-12); - } - - .sm\:bui-pb-13 { - padding-bottom: var(--bui-space-13); - } - - .sm\:bui-pb-14 { - padding-bottom: var(--bui-space-14); - } -} - -@media (width >= 1024px) { - .md\:bui-pb { - padding-bottom: var(--pb-md); - } - - .md\:bui-pb-0\.5 { - padding-bottom: var(--bui-space-0_5); - } - - .md\:bui-pb-1 { - padding-bottom: var(--bui-space-1); - } - - .md\:bui-pb-1\.5 { - padding-bottom: var(--bui-space-1_5); - } - - .md\:bui-pb-2 { - padding-bottom: var(--bui-space-2); - } - - .md\:bui-pb-3 { - padding-bottom: var(--bui-space-3); - } - - .md\:bui-pb-4 { - padding-bottom: var(--bui-space-4); - } - - .md\:bui-pb-5 { - padding-bottom: var(--bui-space-5); - } - - .md\:bui-pb-6 { - padding-bottom: var(--bui-space-6); - } - - .md\:bui-pb-7 { - padding-bottom: var(--bui-space-7); - } - - .md\:bui-pb-8 { - padding-bottom: var(--bui-space-8); - } - - .md\:bui-pb-9 { - padding-bottom: var(--bui-space-9); - } - - .md\:bui-pb-10 { - padding-bottom: var(--bui-space-10); - } - - .md\:bui-pb-11 { - padding-bottom: var(--bui-space-11); - } - - .md\:bui-pb-12 { - padding-bottom: var(--bui-space-12); - } - - .md\:bui-pb-13 { - padding-bottom: var(--bui-space-13); - } - - .md\:bui-pb-14 { - padding-bottom: var(--bui-space-14); - } -} - -@media (width >= 1280px) { - .lg\:bui-pb { - padding-bottom: var(--pb-lg); - } - - .lg\:bui-pb-0\.5 { - padding-bottom: var(--bui-space-0_5); - } - - .lg\:bui-pb-1 { - padding-bottom: var(--bui-space-1); - } - - .lg\:bui-pb-1\.5 { - padding-bottom: var(--bui-space-1_5); - } - - .lg\:bui-pb-2 { - padding-bottom: var(--bui-space-2); - } - - .lg\:bui-pb-3 { - padding-bottom: var(--bui-space-3); - } - - .lg\:bui-pb-4 { - padding-bottom: var(--bui-space-4); - } - - .lg\:bui-pb-5 { - padding-bottom: var(--bui-space-5); - } - - .lg\:bui-pb-6 { - padding-bottom: var(--bui-space-6); - } - - .lg\:bui-pb-7 { - padding-bottom: var(--bui-space-7); - } - - .lg\:bui-pb-8 { - padding-bottom: var(--bui-space-8); - } - - .lg\:bui-pb-9 { - padding-bottom: var(--bui-space-9); - } - - .lg\:bui-pb-10 { - padding-bottom: var(--bui-space-10); - } - - .lg\:bui-pb-11 { - padding-bottom: var(--bui-space-11); - } - - .lg\:bui-pb-12 { - padding-bottom: var(--bui-space-12); - } - - .lg\:bui-pb-13 { - padding-bottom: var(--bui-space-13); - } - - .lg\:bui-pb-14 { - padding-bottom: var(--bui-space-14); - } -} - -@media (width >= 1536px) { - .xl\:bui-pb { - padding-bottom: var(--pb-xl); - } - - .xl\:bui-pb-0\.5 { - padding-bottom: var(--bui-space-0_5); - } - - .xl\:bui-pb-1 { - padding-bottom: var(--bui-space-1); - } - - .xl\:bui-pb-1\.5 { - padding-bottom: var(--bui-space-1_5); - } - - .xl\:bui-pb-2 { - padding-bottom: var(--bui-space-2); - } - - .xl\:bui-pb-3 { - padding-bottom: var(--bui-space-3); - } - - .xl\:bui-pb-4 { - padding-bottom: var(--bui-space-4); - } - - .xl\:bui-pb-5 { - padding-bottom: var(--bui-space-5); - } - - .xl\:bui-pb-6 { - padding-bottom: var(--bui-space-6); - } - - .xl\:bui-pb-7 { - padding-bottom: var(--bui-space-7); - } - - .xl\:bui-pb-8 { - padding-bottom: var(--bui-space-8); - } - - .xl\:bui-pb-9 { - padding-bottom: var(--bui-space-9); - } - - .xl\:bui-pb-10 { - padding-bottom: var(--bui-space-10); - } - - .xl\:bui-pb-11 { - padding-bottom: var(--bui-space-11); - } - - .xl\:bui-pb-12 { - padding-bottom: var(--bui-space-12); - } - - .xl\:bui-pb-13 { - padding-bottom: var(--bui-space-13); - } - - .xl\:bui-pb-14 { - padding-bottom: var(--bui-space-14); - } -} - -.bui-py { - padding-top: var(--py); - padding-bottom: var(--py); -} - -.bui-py-0\.5 { - padding-top: var(--bui-space-0_5); - padding-bottom: var(--bui-space-0_5); -} - -.bui-py-1 { - padding-top: var(--bui-space-1); - padding-bottom: var(--bui-space-1); -} - -.bui-py-1\.5 { - padding-top: var(--bui-space-1_5); - padding-bottom: var(--bui-space-1_5); -} - -.bui-py-2 { - padding-top: var(--bui-space-2); - padding-bottom: var(--bui-space-2); -} - -.bui-py-3 { - padding-top: var(--bui-space-3); - padding-bottom: var(--bui-space-3); -} - -.bui-py-4 { - padding-top: var(--bui-space-4); - padding-bottom: var(--bui-space-4); -} - -.bui-py-5 { - padding-top: var(--bui-space-5); - padding-bottom: var(--bui-space-5); -} - -.bui-py-6 { - padding-top: var(--bui-space-6); - padding-bottom: var(--bui-space-6); -} - -.bui-py-7 { - padding-top: var(--bui-space-7); - padding-bottom: var(--bui-space-7); -} - -.bui-py-8 { - padding-top: var(--bui-space-8); - padding-bottom: var(--bui-space-8); -} - -.bui-py-9 { - padding-top: var(--bui-space-9); - padding-bottom: var(--bui-space-9); -} - -.bui-py-10 { - padding-top: var(--bui-space-10); - padding-bottom: var(--bui-space-10); -} - -.bui-py-11 { - padding-top: var(--bui-space-11); - padding-bottom: var(--bui-space-11); -} - -.bui-py-12 { - padding-top: var(--bui-space-12); - padding-bottom: var(--bui-space-12); -} - -.bui-py-13 { - padding-top: var(--bui-space-13); - padding-bottom: var(--bui-space-13); -} - -.bui-py-14 { - padding-top: var(--bui-space-14); - padding-bottom: var(--bui-space-14); -} - -@media (width >= 640px) { - .xs\:bui-py { - padding-top: var(--py-xs); - padding-bottom: var(--py-xs); - } - - .xs\:bui-py-0\.5 { - padding-top: var(--bui-space-0_5); - padding-bottom: var(--bui-space-0_5); - } - - .xs\:bui-py-1 { - padding-top: var(--bui-space-1); - padding-bottom: var(--bui-space-1); - } - - .xs\:bui-py-1\.5 { - padding-top: var(--bui-space-1_5); - padding-bottom: var(--bui-space-1_5); - } - - .xs\:bui-py-2 { - padding-top: var(--bui-space-2); - padding-bottom: var(--bui-space-2); - } - - .xs\:bui-py-3 { - padding-top: var(--bui-space-3); - padding-bottom: var(--bui-space-3); - } - - .xs\:bui-py-4 { - padding-top: var(--bui-space-4); - padding-bottom: var(--bui-space-4); - } - - .xs\:bui-py-5 { - padding-top: var(--bui-space-5); - padding-bottom: var(--bui-space-5); - } - - .xs\:bui-py-6 { - padding-top: var(--bui-space-6); - padding-bottom: var(--bui-space-6); - } - - .xs\:bui-py-7 { - padding-top: var(--bui-space-7); - padding-bottom: var(--bui-space-7); - } - - .xs\:bui-py-8 { - padding-top: var(--bui-space-8); - padding-bottom: var(--bui-space-8); - } - - .xs\:bui-py-9 { - padding-top: var(--bui-space-9); - padding-bottom: var(--bui-space-9); - } - - .xs\:bui-py-10 { - padding-top: var(--bui-space-10); - padding-bottom: var(--bui-space-10); - } - - .xs\:bui-py-11 { - padding-top: var(--bui-space-11); - padding-bottom: var(--bui-space-11); - } - - .xs\:bui-py-12 { - padding-top: var(--bui-space-12); - padding-bottom: var(--bui-space-12); - } - - .xs\:bui-py-13 { - padding-top: var(--bui-space-13); - padding-bottom: var(--bui-space-13); - } - - .xs\:bui-py-14 { - padding-top: var(--bui-space-14); - padding-bottom: var(--bui-space-14); - } -} - -@media (width >= 768px) { - .sm\:bui-py { - padding-top: var(--py-sm); - padding-bottom: var(--py-sm); - } - - .sm\:bui-py-0\.5 { - padding-top: var(--bui-space-0_5); - padding-bottom: var(--bui-space-0_5); - } - - .sm\:bui-py-1 { - padding-top: var(--bui-space-1); - padding-bottom: var(--bui-space-1); - } - - .sm\:bui-py-1\.5 { - padding-top: var(--bui-space-1_5); - padding-bottom: var(--bui-space-1_5); - } - - .sm\:bui-py-2 { - padding-top: var(--bui-space-2); - padding-bottom: var(--bui-space-2); - } - - .sm\:bui-py-3 { - padding-top: var(--bui-space-3); - padding-bottom: var(--bui-space-3); - } - - .sm\:bui-py-4 { - padding-top: var(--bui-space-4); - padding-bottom: var(--bui-space-4); - } - - .sm\:bui-py-5 { - padding-top: var(--bui-space-5); - padding-bottom: var(--bui-space-5); - } - - .sm\:bui-py-6 { - padding-top: var(--bui-space-6); - padding-bottom: var(--bui-space-6); - } - - .sm\:bui-py-7 { - padding-top: var(--bui-space-7); - padding-bottom: var(--bui-space-7); - } - - .sm\:bui-py-8 { - padding-top: var(--bui-space-8); - padding-bottom: var(--bui-space-8); - } - - .sm\:bui-py-9 { - padding-top: var(--bui-space-9); - padding-bottom: var(--bui-space-9); - } - - .sm\:bui-py-10 { - padding-top: var(--bui-space-10); - padding-bottom: var(--bui-space-10); - } - - .sm\:bui-py-11 { - padding-top: var(--bui-space-11); - padding-bottom: var(--bui-space-11); - } - - .sm\:bui-py-12 { - padding-top: var(--bui-space-12); - padding-bottom: var(--bui-space-12); - } - - .sm\:bui-py-13 { - padding-top: var(--bui-space-13); - padding-bottom: var(--bui-space-13); - } - - .sm\:bui-py-14 { - padding-top: var(--bui-space-14); - padding-bottom: var(--bui-space-14); - } -} - -@media (width >= 1024px) { - .md\:bui-py { - padding-top: var(--py-md); - padding-bottom: var(--py-md); - } - - .md\:bui-py-0\.5 { - padding-top: var(--bui-space-0_5); - padding-bottom: var(--bui-space-0_5); - } - - .md\:bui-py-1 { - padding-top: var(--bui-space-1); - padding-bottom: var(--bui-space-1); - } - - .md\:bui-py-1\.5 { - padding-top: var(--bui-space-1_5); - padding-bottom: var(--bui-space-1_5); - } - - .md\:bui-py-2 { - padding-top: var(--bui-space-2); - padding-bottom: var(--bui-space-2); - } - - .md\:bui-py-3 { - padding-top: var(--bui-space-3); - padding-bottom: var(--bui-space-3); - } - - .md\:bui-py-4 { - padding-top: var(--bui-space-4); - padding-bottom: var(--bui-space-4); - } - - .md\:bui-py-5 { - padding-top: var(--bui-space-5); - padding-bottom: var(--bui-space-5); - } - - .md\:bui-py-6 { - padding-top: var(--bui-space-6); - padding-bottom: var(--bui-space-6); - } - - .md\:bui-py-7 { - padding-top: var(--bui-space-7); - padding-bottom: var(--bui-space-7); - } - - .md\:bui-py-8 { - padding-top: var(--bui-space-8); - padding-bottom: var(--bui-space-8); - } - - .md\:bui-py-9 { - padding-top: var(--bui-space-9); - padding-bottom: var(--bui-space-9); - } - - .md\:bui-py-10 { - padding-top: var(--bui-space-10); - padding-bottom: var(--bui-space-10); - } - - .md\:bui-py-11 { - padding-top: var(--bui-space-11); - padding-bottom: var(--bui-space-11); - } - - .md\:bui-py-12 { - padding-top: var(--bui-space-12); - padding-bottom: var(--bui-space-12); - } - - .md\:bui-py-13 { - padding-top: var(--bui-space-13); - padding-bottom: var(--bui-space-13); - } - - .md\:bui-py-14 { - padding-top: var(--bui-space-14); - padding-bottom: var(--bui-space-14); - } -} - -@media (width >= 1280px) { - .lg\:bui-py { - padding-top: var(--py-lg); - padding-bottom: var(--py-lg); - } - - .lg\:bui-py-0\.5 { - padding-top: var(--bui-space-0_5); - padding-bottom: var(--bui-space-0_5); - } - - .lg\:bui-py-1 { - padding-top: var(--bui-space-1); - padding-bottom: var(--bui-space-1); - } - - .lg\:bui-py-1\.5 { - padding-top: var(--bui-space-1_5); - padding-bottom: var(--bui-space-1_5); - } - - .lg\:bui-py-2 { - padding-top: var(--bui-space-2); - padding-bottom: var(--bui-space-2); - } - - .lg\:bui-py-3 { - padding-top: var(--bui-space-3); - padding-bottom: var(--bui-space-3); - } - - .lg\:bui-py-4 { - padding-top: var(--bui-space-4); - padding-bottom: var(--bui-space-4); - } - - .lg\:bui-py-5 { - padding-top: var(--bui-space-5); - padding-bottom: var(--bui-space-5); - } - - .lg\:bui-py-6 { - padding-top: var(--bui-space-6); - padding-bottom: var(--bui-space-6); - } - - .lg\:bui-py-7 { - padding-top: var(--bui-space-7); - padding-bottom: var(--bui-space-7); - } - - .lg\:bui-py-8 { - padding-top: var(--bui-space-8); - padding-bottom: var(--bui-space-8); - } - - .lg\:bui-py-9 { - padding-top: var(--bui-space-9); - padding-bottom: var(--bui-space-9); - } - - .lg\:bui-py-10 { - padding-top: var(--bui-space-10); - padding-bottom: var(--bui-space-10); - } - - .lg\:bui-py-11 { - padding-top: var(--bui-space-11); - padding-bottom: var(--bui-space-11); - } - - .lg\:bui-py-12 { - padding-top: var(--bui-space-12); - padding-bottom: var(--bui-space-12); - } - - .lg\:bui-py-13 { - padding-top: var(--bui-space-13); - padding-bottom: var(--bui-space-13); - } - - .lg\:bui-py-14 { - padding-top: var(--bui-space-14); - padding-bottom: var(--bui-space-14); - } -} - -@media (width >= 1536px) { - .xl\:bui-py { - padding-top: var(--py-xl); - padding-bottom: var(--py-xl); - } - - .xl\:bui-py-0\.5 { - padding-top: var(--bui-space-0_5); - padding-bottom: var(--bui-space-0_5); - } - - .xl\:bui-py-1 { - padding-top: var(--bui-space-1); - padding-bottom: var(--bui-space-1); - } - - .xl\:bui-py-1\.5 { - padding-top: var(--bui-space-1_5); - padding-bottom: var(--bui-space-1_5); - } - - .xl\:bui-py-2 { - padding-top: var(--bui-space-2); - padding-bottom: var(--bui-space-2); - } - - .xl\:bui-py-3 { - padding-top: var(--bui-space-3); - padding-bottom: var(--bui-space-3); - } - - .xl\:bui-py-4 { - padding-top: var(--bui-space-4); - padding-bottom: var(--bui-space-4); - } - - .xl\:bui-py-5 { - padding-top: var(--bui-space-5); - padding-bottom: var(--bui-space-5); - } - - .xl\:bui-py-6 { - padding-top: var(--bui-space-6); - padding-bottom: var(--bui-space-6); - } - - .xl\:bui-py-7 { - padding-top: var(--bui-space-7); - padding-bottom: var(--bui-space-7); - } - - .xl\:bui-py-8 { - padding-top: var(--bui-space-8); - padding-bottom: var(--bui-space-8); - } - - .xl\:bui-py-9 { - padding-top: var(--bui-space-9); - padding-bottom: var(--bui-space-9); - } - - .xl\:bui-py-10 { - padding-top: var(--bui-space-10); - padding-bottom: var(--bui-space-10); - } - - .xl\:bui-py-11 { - padding-top: var(--bui-space-11); - padding-bottom: var(--bui-space-11); - } - - .xl\:bui-py-12 { - padding-top: var(--bui-space-12); - padding-bottom: var(--bui-space-12); - } - - .xl\:bui-py-13 { - padding-top: var(--bui-space-13); - padding-bottom: var(--bui-space-13); - } - - .xl\:bui-py-14 { - padding-top: var(--bui-space-14); - padding-bottom: var(--bui-space-14); - } -} - -.bui-px { - padding-left: var(--px); - padding-right: var(--px); -} - -.bui-px-0\.5 { - padding-left: var(--bui-space-0_5); - padding-right: var(--bui-space-0_5); -} - -.bui-px-1 { - padding-left: var(--bui-space-1); - padding-right: var(--bui-space-1); -} - -.bui-px-1\.5 { - padding-left: var(--bui-space-1_5); - padding-right: var(--bui-space-1_5); -} - -.bui-px-2 { - padding-left: var(--bui-space-2); - padding-right: var(--bui-space-2); -} - -.bui-px-3 { - padding-left: var(--bui-space-3); - padding-right: var(--bui-space-3); -} - -.bui-px-4 { - padding-left: var(--bui-space-4); - padding-right: var(--bui-space-4); -} - -.bui-px-5 { - padding-left: var(--bui-space-5); - padding-right: var(--bui-space-5); -} - -.bui-px-6 { - padding-left: var(--bui-space-6); - padding-right: var(--bui-space-6); -} - -.bui-px-7 { - padding-left: var(--bui-space-7); - padding-right: var(--bui-space-7); -} - -.bui-px-8 { - padding-left: var(--bui-space-8); - padding-right: var(--bui-space-8); -} - -.bui-px-9 { - padding-left: var(--bui-space-9); - padding-right: var(--bui-space-9); -} - -.bui-px-10 { - padding-left: var(--bui-space-10); - padding-right: var(--bui-space-10); -} - -.bui-px-11 { - padding-left: var(--bui-space-11); - padding-right: var(--bui-space-11); -} - -.bui-px-12 { - padding-left: var(--bui-space-12); - padding-right: var(--bui-space-12); -} - -.bui-px-13 { - padding-left: var(--bui-space-13); - padding-right: var(--bui-space-13); -} - -.bui-px-14 { - padding-left: var(--bui-space-14); - padding-right: var(--bui-space-14); -} - -@media (width >= 640px) { - .xs\:bui-px { - padding-left: var(--px-xs); - padding-right: var(--px-xs); - } - - .xs\:bui-px-0\.5 { - padding-left: var(--bui-space-0_5); - padding-right: var(--bui-space-0_5); - } - - .xs\:bui-px-1 { - padding-left: var(--bui-space-1); - padding-right: var(--bui-space-1); - } - - .xs\:bui-px-1\.5 { - padding-left: var(--bui-space-1_5); - padding-right: var(--bui-space-1_5); - } - - .xs\:bui-px-2 { - padding-left: var(--bui-space-2); - padding-right: var(--bui-space-2); - } - - .xs\:bui-px-3 { - padding-left: var(--bui-space-3); - padding-right: var(--bui-space-3); - } - - .xs\:bui-px-4 { - padding-left: var(--bui-space-4); - padding-right: var(--bui-space-4); - } - - .xs\:bui-px-5 { - padding-left: var(--bui-space-5); - padding-right: var(--bui-space-5); - } - - .xs\:bui-px-6 { - padding-left: var(--bui-space-6); - padding-right: var(--bui-space-6); - } - - .xs\:bui-px-7 { - padding-left: var(--bui-space-7); - padding-right: var(--bui-space-7); - } - - .xs\:bui-px-8 { - padding-left: var(--bui-space-8); - padding-right: var(--bui-space-8); - } - - .xs\:bui-px-9 { - padding-left: var(--bui-space-9); - padding-right: var(--bui-space-9); - } - - .xs\:bui-px-10 { - padding-left: var(--bui-space-10); - padding-right: var(--bui-space-10); - } - - .xs\:bui-px-11 { - padding-left: var(--bui-space-11); - padding-right: var(--bui-space-11); - } - - .xs\:bui-px-12 { - padding-left: var(--bui-space-12); - padding-right: var(--bui-space-12); - } - - .xs\:bui-px-13 { - padding-left: var(--bui-space-13); - padding-right: var(--bui-space-13); - } - - .xs\:bui-px-14 { - padding-left: var(--bui-space-14); - padding-right: var(--bui-space-14); - } -} - -@media (width >= 768px) { - .sm\:bui-px { - padding-left: var(--px-sm); - padding-right: var(--px-sm); - } - - .sm\:bui-px-0\.5 { - padding-left: var(--bui-space-0_5); - padding-right: var(--bui-space-0_5); - } - - .sm\:bui-px-1 { - padding-left: var(--bui-space-1); - padding-right: var(--bui-space-1); - } - - .sm\:bui-px-1\.5 { - padding-left: var(--bui-space-1_5); - padding-right: var(--bui-space-1_5); - } - - .sm\:bui-px-2 { - padding-left: var(--bui-space-2); - padding-right: var(--bui-space-2); - } - - .sm\:bui-px-3 { - padding-left: var(--bui-space-3); - padding-right: var(--bui-space-3); - } - - .sm\:bui-px-4 { - padding-left: var(--bui-space-4); - padding-right: var(--bui-space-4); - } - - .sm\:bui-px-5 { - padding-left: var(--bui-space-5); - padding-right: var(--bui-space-5); - } - - .sm\:bui-px-6 { - padding-left: var(--bui-space-6); - padding-right: var(--bui-space-6); - } - - .sm\:bui-px-7 { - padding-left: var(--bui-space-7); - padding-right: var(--bui-space-7); - } - - .sm\:bui-px-8 { - padding-left: var(--bui-space-8); - padding-right: var(--bui-space-8); - } - - .sm\:bui-px-9 { - padding-left: var(--bui-space-9); - padding-right: var(--bui-space-9); - } - - .sm\:bui-px-10 { - padding-left: var(--bui-space-10); - padding-right: var(--bui-space-10); - } - - .sm\:bui-px-11 { - padding-left: var(--bui-space-11); - padding-right: var(--bui-space-11); - } - - .sm\:bui-px-12 { - padding-left: var(--bui-space-12); - padding-right: var(--bui-space-12); - } - - .sm\:bui-px-13 { - padding-left: var(--bui-space-13); - padding-right: var(--bui-space-13); - } - - .sm\:bui-px-14 { - padding-left: var(--bui-space-14); - padding-right: var(--bui-space-14); - } -} - -@media (width >= 1024px) { - .md\:bui-px { - padding-left: var(--px-md); - padding-right: var(--px-md); - } - - .md\:bui-px-0\.5 { - padding-left: var(--bui-space-0_5); - padding-right: var(--bui-space-0_5); - } - - .md\:bui-px-1 { - padding-left: var(--bui-space-1); - padding-right: var(--bui-space-1); - } - - .md\:bui-px-1\.5 { - padding-left: var(--bui-space-1_5); - padding-right: var(--bui-space-1_5); - } - - .md\:bui-px-2 { - padding-left: var(--bui-space-2); - padding-right: var(--bui-space-2); - } - - .md\:bui-px-3 { - padding-left: var(--bui-space-3); - padding-right: var(--bui-space-3); - } - - .md\:bui-px-4 { - padding-left: var(--bui-space-4); - padding-right: var(--bui-space-4); - } - - .md\:bui-px-5 { - padding-left: var(--bui-space-5); - padding-right: var(--bui-space-5); - } - - .md\:bui-px-6 { - padding-left: var(--bui-space-6); - padding-right: var(--bui-space-6); - } - - .md\:bui-px-7 { - padding-left: var(--bui-space-7); - padding-right: var(--bui-space-7); - } - - .md\:bui-px-8 { - padding-left: var(--bui-space-8); - padding-right: var(--bui-space-8); - } - - .md\:bui-px-9 { - padding-left: var(--bui-space-9); - padding-right: var(--bui-space-9); - } - - .md\:bui-px-10 { - padding-left: var(--bui-space-10); - padding-right: var(--bui-space-10); - } - - .md\:bui-px-11 { - padding-left: var(--bui-space-11); - padding-right: var(--bui-space-11); - } - - .md\:bui-px-12 { - padding-left: var(--bui-space-12); - padding-right: var(--bui-space-12); - } - - .md\:bui-px-13 { - padding-left: var(--bui-space-13); - padding-right: var(--bui-space-13); - } - - .md\:bui-px-14 { - padding-left: var(--bui-space-14); - padding-right: var(--bui-space-14); - } -} - -@media (width >= 1280px) { - .lg\:bui-px { - padding-left: var(--px-lg); - padding-right: var(--px-lg); - } - - .lg\:bui-px-0\.5 { - padding-left: var(--bui-space-0_5); - padding-right: var(--bui-space-0_5); - } - - .lg\:bui-px-1 { - padding-left: var(--bui-space-1); - padding-right: var(--bui-space-1); - } - - .lg\:bui-px-1\.5 { - padding-left: var(--bui-space-1_5); - padding-right: var(--bui-space-1_5); - } - - .lg\:bui-px-2 { - padding-left: var(--bui-space-2); - padding-right: var(--bui-space-2); - } - - .lg\:bui-px-3 { - padding-left: var(--bui-space-3); - padding-right: var(--bui-space-3); - } - - .lg\:bui-px-4 { - padding-left: var(--bui-space-4); - padding-right: var(--bui-space-4); - } - - .lg\:bui-px-5 { - padding-left: var(--bui-space-5); - padding-right: var(--bui-space-5); - } - - .lg\:bui-px-6 { - padding-left: var(--bui-space-6); - padding-right: var(--bui-space-6); - } - - .lg\:bui-px-7 { - padding-left: var(--bui-space-7); - padding-right: var(--bui-space-7); - } - - .lg\:bui-px-8 { - padding-left: var(--bui-space-8); - padding-right: var(--bui-space-8); - } - - .lg\:bui-px-9 { - padding-left: var(--bui-space-9); - padding-right: var(--bui-space-9); - } - - .lg\:bui-px-10 { - padding-left: var(--bui-space-10); - padding-right: var(--bui-space-10); - } - - .lg\:bui-px-11 { - padding-left: var(--bui-space-11); - padding-right: var(--bui-space-11); - } - - .lg\:bui-px-12 { - padding-left: var(--bui-space-12); - padding-right: var(--bui-space-12); - } - - .lg\:bui-px-13 { - padding-left: var(--bui-space-13); - padding-right: var(--bui-space-13); - } - - .lg\:bui-px-14 { - padding-left: var(--bui-space-14); - padding-right: var(--bui-space-14); - } -} - -@media (width >= 1536px) { - .xl\:bui-px { - padding-left: var(--px-xl); - padding-right: var(--px-xl); - } - - .xl\:bui-px-0\.5 { - padding-left: var(--bui-space-0_5); - padding-right: var(--bui-space-0_5); - } - - .xl\:bui-px-1 { - padding-left: var(--bui-space-1); - padding-right: var(--bui-space-1); - } - - .xl\:bui-px-1\.5 { - padding-left: var(--bui-space-1_5); - padding-right: var(--bui-space-1_5); - } - - .xl\:bui-px-2 { - padding-left: var(--bui-space-2); - padding-right: var(--bui-space-2); - } - - .xl\:bui-px-3 { - padding-left: var(--bui-space-3); - padding-right: var(--bui-space-3); - } - - .xl\:bui-px-4 { - padding-left: var(--bui-space-4); - padding-right: var(--bui-space-4); - } - - .xl\:bui-px-5 { - padding-left: var(--bui-space-5); - padding-right: var(--bui-space-5); - } - - .xl\:bui-px-6 { - padding-left: var(--bui-space-6); - padding-right: var(--bui-space-6); - } - - .xl\:bui-px-7 { - padding-left: var(--bui-space-7); - padding-right: var(--bui-space-7); - } - - .xl\:bui-px-8 { - padding-left: var(--bui-space-8); - padding-right: var(--bui-space-8); - } - - .xl\:bui-px-9 { - padding-left: var(--bui-space-9); - padding-right: var(--bui-space-9); - } - - .xl\:bui-px-10 { - padding-left: var(--bui-space-10); - padding-right: var(--bui-space-10); - } - - .xl\:bui-px-11 { - padding-left: var(--bui-space-11); - padding-right: var(--bui-space-11); - } - - .xl\:bui-px-12 { - padding-left: var(--bui-space-12); - padding-right: var(--bui-space-12); - } - - .xl\:bui-px-13 { - padding-left: var(--bui-space-13); - padding-right: var(--bui-space-13); - } - - .xl\:bui-px-14 { - padding-left: var(--bui-space-14); - padding-right: var(--bui-space-14); - } -} - -.bui-m { - margin: var(--m); -} - -.bui-m-0\.5 { - margin: var(--bui-space-0_5); -} - -.bui-m-1 { - margin: var(--bui-space-1); -} - -.bui-m-1\.5 { - margin: var(--bui-space-1_5); -} - -.bui-m-2 { - margin: var(--bui-space-2); -} - -.bui-m-3 { - margin: var(--bui-space-3); -} - -.bui-m-4 { - margin: var(--bui-space-4); -} - -.bui-m-5 { - margin: var(--bui-space-5); -} - -.bui-m-6 { - margin: var(--bui-space-6); -} - -.bui-m-7 { - margin: var(--bui-space-7); -} - -.bui-m-8 { - margin: var(--bui-space-8); -} - -.bui-m-9 { - margin: var(--bui-space-9); -} - -.bui-m-10 { - margin: var(--bui-space-10); -} - -.bui-m-11 { - margin: var(--bui-space-11); -} - -.bui-m-12 { - margin: var(--bui-space-12); -} - -.bui-m-13 { - margin: var(--bui-space-13); -} - -.bui-m-14 { - margin: var(--bui-space-14); -} - -@media (width >= 640px) { - .xs\:bui-m { - margin: var(--p-xs); - } - - .xs\:bui-m-0\.5 { - margin: var(--bui-space-0_5); - } - - .xs\:bui-m-1 { - margin: var(--bui-space-1); - } - - .xs\:bui-m-1\.5 { - margin: var(--bui-space-1_5); - } - - .xs\:bui-m-2 { - margin: var(--bui-space-2); - } - - .xs\:bui-m-3 { - margin: var(--bui-space-3); - } - - .xs\:bui-m-4 { - margin: var(--bui-space-4); - } - - .xs\:bui-m-5 { - margin: var(--bui-space-5); - } - - .xs\:bui-m-6 { - margin: var(--bui-space-6); - } - - .xs\:bui-m-7 { - margin: var(--bui-space-7); - } - - .xs\:bui-m-8 { - margin: var(--bui-space-8); - } - - .xs\:bui-m-9 { - margin: var(--bui-space-9); - } - - .xs\:bui-m-10 { - margin: var(--bui-space-10); - } - - .xs\:bui-m-11 { - margin: var(--bui-space-11); - } - - .xs\:bui-m-12 { - margin: var(--bui-space-12); - } - - .xs\:bui-m-13 { - margin: var(--bui-space-13); - } - - .xs\:bui-m-14 { - margin: var(--bui-space-14); - } -} - -@media (width >= 768px) { - .sm\:bui-m { - margin: var(--p-sm); - } - - .sm\:bui-m-0\.5 { - margin: var(--bui-space-0_5); - } - - .sm\:bui-m-1 { - margin: var(--bui-space-1); - } - - .sm\:bui-m-1\.5 { - margin: var(--bui-space-1_5); - } - - .sm\:bui-m-2 { - margin: var(--bui-space-2); - } - - .sm\:bui-m-3 { - margin: var(--bui-space-3); - } - - .sm\:bui-m-4 { - margin: var(--bui-space-4); - } - - .sm\:bui-m-5 { - margin: var(--bui-space-5); - } - - .sm\:bui-m-6 { - margin: var(--bui-space-6); - } - - .sm\:bui-m-7 { - margin: var(--bui-space-7); - } - - .sm\:bui-m-8 { - margin: var(--bui-space-8); - } - - .sm\:bui-m-9 { - margin: var(--bui-space-9); - } - - .sm\:bui-m-10 { - margin: var(--bui-space-10); - } - - .sm\:bui-m-11 { - margin: var(--bui-space-11); - } - - .sm\:bui-m-12 { - margin: var(--bui-space-12); - } - - .sm\:bui-m-13 { - margin: var(--bui-space-13); - } - - .sm\:bui-m-14 { - margin: var(--bui-space-14); - } -} - -@media (width >= 1024px) { - .md\:bui-m { - margin: var(--p-md); - } - - .md\:bui-m-0\.5 { - margin: var(--bui-space-0_5); - } - - .md\:bui-m-1 { - margin: var(--bui-space-1); - } - - .md\:bui-m-1\.5 { - margin: var(--bui-space-1_5); - } - - .md\:bui-m-2 { - margin: var(--bui-space-2); - } - - .md\:bui-m-3 { - margin: var(--bui-space-3); - } - - .md\:bui-m-4 { - margin: var(--bui-space-4); - } - - .md\:bui-m-5 { - margin: var(--bui-space-5); - } - - .md\:bui-m-6 { - margin: var(--bui-space-6); - } - - .md\:bui-m-7 { - margin: var(--bui-space-7); - } - - .md\:bui-m-8 { - margin: var(--bui-space-8); - } - - .md\:bui-m-9 { - margin: var(--bui-space-9); - } - - .md\:bui-m-10 { - margin: var(--bui-space-10); - } - - .md\:bui-m-11 { - margin: var(--bui-space-11); - } - - .md\:bui-m-12 { - margin: var(--bui-space-12); - } - - .md\:bui-m-13 { - margin: var(--bui-space-13); - } - - .md\:bui-m-14 { - margin: var(--bui-space-14); - } -} - -@media (width >= 1280px) { - .lg\:bui-m { - margin: var(--p-lg); - } - - .lg\:bui-m-0\.5 { - margin: var(--bui-space-0_5); - } - - .lg\:bui-m-1 { - margin: var(--bui-space-1); - } - - .lg\:bui-m-1\.5 { - margin: var(--bui-space-1_5); - } - - .lg\:bui-m-2 { - margin: var(--bui-space-2); - } - - .lg\:bui-m-3 { - margin: var(--bui-space-3); - } - - .lg\:bui-m-4 { - margin: var(--bui-space-4); - } - - .lg\:bui-m-5 { - margin: var(--bui-space-5); - } - - .lg\:bui-m-6 { - margin: var(--bui-space-6); - } - - .lg\:bui-m-7 { - margin: var(--bui-space-7); - } - - .lg\:bui-m-8 { - margin: var(--bui-space-8); - } - - .lg\:bui-m-9 { - margin: var(--bui-space-9); - } - - .lg\:bui-m-10 { - margin: var(--bui-space-10); - } - - .lg\:bui-m-11 { - margin: var(--bui-space-11); - } - - .lg\:bui-m-12 { - margin: var(--bui-space-12); - } - - .lg\:bui-m-13 { - margin: var(--bui-space-13); - } - - .lg\:bui-m-14 { - margin: var(--bui-space-14); - } -} - -@media (width >= 1536px) { - .xl\:bui-m { - margin: var(--p-xl); - } - - .xl\:bui-m-0\.5 { - margin: var(--bui-space-0_5); - } - - .xl\:bui-m-1 { - margin: var(--bui-space-1); - } - - .xl\:bui-m-1\.5 { - margin: var(--bui-space-1_5); - } - - .xl\:bui-m-2 { - margin: var(--bui-space-2); - } - - .xl\:bui-m-3 { - margin: var(--bui-space-3); - } - - .xl\:bui-m-4 { - margin: var(--bui-space-4); - } - - .xl\:bui-m-5 { - margin: var(--bui-space-5); - } - - .xl\:bui-m-6 { - margin: var(--bui-space-6); - } - - .xl\:bui-m-7 { - margin: var(--bui-space-7); - } - - .xl\:bui-m-8 { - margin: var(--bui-space-8); - } - - .xl\:bui-m-9 { - margin: var(--bui-space-9); - } - - .xl\:bui-m-10 { - margin: var(--bui-space-10); - } - - .xl\:bui-m-11 { - margin: var(--bui-space-11); - } - - .xl\:bui-m-12 { - margin: var(--bui-space-12); - } - - .xl\:bui-m-13 { - margin: var(--bui-space-13); - } - - .xl\:bui-m-14 { - margin: var(--bui-space-14); - } -} - -.bui-ml { - margin-left: var(--ml); -} - -.bui-ml-0\.5 { - margin-left: var(--bui-space-0_5); -} - -.bui-ml-1 { - margin-left: var(--bui-space-1); -} - -.bui-ml-1\.5 { - margin-left: var(--bui-space-1_5); -} - -.bui-ml-2 { - margin-left: var(--bui-space-2); -} - -.bui-ml-3 { - margin-left: var(--bui-space-3); -} - -.bui-ml-4 { - margin-left: var(--bui-space-4); -} - -.bui-ml-5 { - margin-left: var(--bui-space-5); -} - -.bui-ml-6 { - margin-left: var(--bui-space-6); -} - -.bui-ml-7 { - margin-left: var(--bui-space-7); -} - -.bui-ml-8 { - margin-left: var(--bui-space-8); -} - -.bui-ml-9 { - margin-left: var(--bui-space-9); -} - -.bui-ml-10 { - margin-left: var(--bui-space-10); -} - -.bui-ml-11 { - margin-left: var(--bui-space-11); -} - -.bui-ml-12 { - margin-left: var(--bui-space-12); -} - -.bui-ml-13 { - margin-left: var(--bui-space-13); -} - -.bui-ml-14 { - margin-left: var(--bui-space-14); -} - -@media (width >= 640px) { - .xs\:bui-ml { - margin-left: var(--pl-xs); - } - - .xs\:bui-ml-0\.5 { - margin-left: var(--bui-space-0_5); - } - - .xs\:bui-ml-1 { - margin-left: var(--bui-space-1); - } - - .xs\:bui-ml-1\.5 { - margin-left: var(--bui-space-1_5); - } - - .xs\:bui-ml-2 { - margin-left: var(--bui-space-2); - } - - .xs\:bui-ml-3 { - margin-left: var(--bui-space-3); - } - - .xs\:bui-ml-4 { - margin-left: var(--bui-space-4); - } - - .xs\:bui-ml-5 { - margin-left: var(--bui-space-5); - } - - .xs\:bui-ml-6 { - margin-left: var(--bui-space-6); - } - - .xs\:bui-ml-7 { - margin-left: var(--bui-space-7); - } - - .xs\:bui-ml-8 { - margin-left: var(--bui-space-8); - } - - .xs\:bui-ml-9 { - margin-left: var(--bui-space-9); - } - - .xs\:bui-ml-10 { - margin-left: var(--bui-space-10); - } - - .xs\:bui-ml-11 { - margin-left: var(--bui-space-11); - } - - .xs\:bui-ml-12 { - margin-left: var(--bui-space-12); - } - - .xs\:bui-ml-13 { - margin-left: var(--bui-space-13); - } - - .xs\:bui-ml-14 { - margin-left: var(--bui-space-14); - } -} - -@media (width >= 768px) { - .sm\:bui-ml { - margin-left: var(--pl-sm); - } - - .sm\:bui-ml-0\.5 { - margin-left: var(--bui-space-0_5); - } - - .sm\:bui-ml-1 { - margin-left: var(--bui-space-1); - } - - .sm\:bui-ml-1\.5 { - margin-left: var(--bui-space-1_5); - } - - .sm\:bui-ml-2 { - margin-left: var(--bui-space-2); - } - - .sm\:bui-ml-3 { - margin-left: var(--bui-space-3); - } - - .sm\:bui-ml-4 { - margin-left: var(--bui-space-4); - } - - .sm\:bui-ml-5 { - margin-left: var(--bui-space-5); - } - - .sm\:bui-ml-6 { - margin-left: var(--bui-space-6); - } - - .sm\:bui-ml-7 { - margin-left: var(--bui-space-7); - } - - .sm\:bui-ml-8 { - margin-left: var(--bui-space-8); - } - - .sm\:bui-ml-9 { - margin-left: var(--bui-space-9); - } - - .sm\:bui-ml-10 { - margin-left: var(--bui-space-10); - } - - .sm\:bui-ml-11 { - margin-left: var(--bui-space-11); - } - - .sm\:bui-ml-12 { - margin-left: var(--bui-space-12); - } - - .sm\:bui-ml-13 { - margin-left: var(--bui-space-13); - } - - .sm\:bui-ml-14 { - margin-left: var(--bui-space-14); - } -} - -@media (width >= 1024px) { - .md\:bui-ml { - margin-left: var(--pl-md); - } - - .md\:bui-ml-0\.5 { - margin-left: var(--bui-space-0_5); - } - - .md\:bui-ml-1 { - margin-left: var(--bui-space-1); - } - - .md\:bui-ml-1\.5 { - margin-left: var(--bui-space-1_5); - } - - .md\:bui-ml-2 { - margin-left: var(--bui-space-2); - } - - .md\:bui-ml-3 { - margin-left: var(--bui-space-3); - } - - .md\:bui-ml-4 { - margin-left: var(--bui-space-4); - } - - .md\:bui-ml-5 { - margin-left: var(--bui-space-5); - } - - .md\:bui-ml-6 { - margin-left: var(--bui-space-6); - } - - .md\:bui-ml-7 { - margin-left: var(--bui-space-7); - } - - .md\:bui-ml-8 { - margin-left: var(--bui-space-8); - } - - .md\:bui-ml-9 { - margin-left: var(--bui-space-9); - } - - .md\:bui-ml-10 { - margin-left: var(--bui-space-10); - } - - .md\:bui-ml-11 { - margin-left: var(--bui-space-11); - } - - .md\:bui-ml-12 { - margin-left: var(--bui-space-12); - } - - .md\:bui-ml-13 { - margin-left: var(--bui-space-13); - } - - .md\:bui-ml-14 { - margin-left: var(--bui-space-14); - } -} - -@media (width >= 1280px) { - .lg\:bui-ml { - margin-left: var(--pl-lg); - } - - .lg\:bui-ml-0\.5 { - margin-left: var(--bui-space-0_5); - } - - .lg\:bui-ml-1 { - margin-left: var(--bui-space-1); - } - - .lg\:bui-ml-1\.5 { - margin-left: var(--bui-space-1_5); - } - - .lg\:bui-ml-2 { - margin-left: var(--bui-space-2); - } - - .lg\:bui-ml-3 { - margin-left: var(--bui-space-3); - } - - .lg\:bui-ml-4 { - margin-left: var(--bui-space-4); - } - - .lg\:bui-ml-5 { - margin-left: var(--bui-space-5); - } - - .lg\:bui-ml-6 { - margin-left: var(--bui-space-6); - } - - .lg\:bui-ml-7 { - margin-left: var(--bui-space-7); - } - - .lg\:bui-ml-8 { - margin-left: var(--bui-space-8); - } - - .lg\:bui-ml-9 { - margin-left: var(--bui-space-9); - } - - .lg\:bui-ml-10 { - margin-left: var(--bui-space-10); - } - - .lg\:bui-ml-11 { - margin-left: var(--bui-space-11); - } - - .lg\:bui-ml-12 { - margin-left: var(--bui-space-12); - } - - .lg\:bui-ml-13 { - margin-left: var(--bui-space-13); - } - - .lg\:bui-ml-14 { - margin-left: var(--bui-space-14); - } -} - -@media (width >= 1536px) { - .xl\:bui-ml { - margin-left: var(--pl-xl); - } - - .xl\:bui-ml-0\.5 { - margin-left: var(--bui-space-0_5); - } - - .xl\:bui-ml-1 { - margin-left: var(--bui-space-1); - } - - .xl\:bui-ml-1\.5 { - margin-left: var(--bui-space-1_5); - } - - .xl\:bui-ml-2 { - margin-left: var(--bui-space-2); - } - - .xl\:bui-ml-3 { - margin-left: var(--bui-space-3); - } - - .xl\:bui-ml-4 { - margin-left: var(--bui-space-4); - } - - .xl\:bui-ml-5 { - margin-left: var(--bui-space-5); - } - - .xl\:bui-ml-6 { - margin-left: var(--bui-space-6); - } - - .xl\:bui-ml-7 { - margin-left: var(--bui-space-7); - } - - .xl\:bui-ml-8 { - margin-left: var(--bui-space-8); - } - - .xl\:bui-ml-9 { - margin-left: var(--bui-space-9); - } - - .xl\:bui-ml-10 { - margin-left: var(--bui-space-10); - } - - .xl\:bui-ml-11 { - margin-left: var(--bui-space-11); - } - - .xl\:bui-ml-12 { - margin-left: var(--bui-space-12); - } - - .xl\:bui-ml-13 { - margin-left: var(--bui-space-13); - } - - .xl\:bui-ml-14 { - margin-left: var(--bui-space-14); - } -} - -.bui-mr { - margin-right: var(--mr); -} - -.bui-mr-0\.5 { - margin-right: var(--bui-space-0_5); -} - -.bui-mr-1 { - margin-right: var(--bui-space-1); -} - -.bui-mr-1\.5 { - margin-right: var(--bui-space-1_5); -} - -.bui-mr-2 { - margin-right: var(--bui-space-2); -} - -.bui-mr-3 { - margin-right: var(--bui-space-3); -} - -.bui-mr-4 { - margin-right: var(--bui-space-4); -} - -.bui-mr-5 { - margin-right: var(--bui-space-5); -} - -.bui-mr-6 { - margin-right: var(--bui-space-6); -} - -.bui-mr-7 { - margin-right: var(--bui-space-7); -} - -.bui-mr-8 { - margin-right: var(--bui-space-8); -} - -.bui-mr-9 { - margin-right: var(--bui-space-9); -} - -.bui-mr-10 { - margin-right: var(--bui-space-10); -} - -.bui-mr-11 { - margin-right: var(--bui-space-11); -} - -.bui-mr-12 { - margin-right: var(--bui-space-12); -} - -.bui-mr-13 { - margin-right: var(--bui-space-13); -} - -.bui-mr-14 { - margin-right: var(--bui-space-14); -} - -@media (width >= 640px) { - .xs\:bui-mr { - margin-right: var(--pr-xs); - } - - .xs\:bui-mr-0\.5 { - margin-right: var(--bui-space-0_5); - } - - .xs\:bui-mr-1 { - margin-right: var(--bui-space-1); - } - - .xs\:bui-mr-1\.5 { - margin-right: var(--bui-space-1_5); - } - - .xs\:bui-mr-2 { - margin-right: var(--bui-space-2); - } - - .xs\:bui-mr-3 { - margin-right: var(--bui-space-3); - } - - .xs\:bui-mr-4 { - margin-right: var(--bui-space-4); - } - - .xs\:bui-mr-5 { - margin-right: var(--bui-space-5); - } - - .xs\:bui-mr-6 { - margin-right: var(--bui-space-6); - } - - .xs\:bui-mr-7 { - margin-right: var(--bui-space-7); - } - - .xs\:bui-mr-8 { - margin-right: var(--bui-space-8); - } - - .xs\:bui-mr-9 { - margin-right: var(--bui-space-9); - } - - .xs\:bui-mr-10 { - margin-right: var(--bui-space-10); - } - - .xs\:bui-mr-11 { - margin-right: var(--bui-space-11); - } - - .xs\:bui-mr-12 { - margin-right: var(--bui-space-12); - } - - .xs\:bui-mr-13 { - margin-right: var(--bui-space-13); - } - - .xs\:bui-mr-14 { - margin-right: var(--bui-space-14); - } -} - -@media (width >= 768px) { - .sm\:bui-mr { - margin-right: var(--pr-sm); - } - - .sm\:bui-mr-0\.5 { - margin-right: var(--bui-space-0_5); - } - - .sm\:bui-mr-1 { - margin-right: var(--bui-space-1); - } - - .sm\:bui-mr-1\.5 { - margin-right: var(--bui-space-1_5); - } - - .sm\:bui-mr-2 { - margin-right: var(--bui-space-2); - } - - .sm\:bui-mr-3 { - margin-right: var(--bui-space-3); - } - - .sm\:bui-mr-4 { - margin-right: var(--bui-space-4); - } - - .sm\:bui-mr-5 { - margin-right: var(--bui-space-5); - } - - .sm\:bui-mr-6 { - margin-right: var(--bui-space-6); - } - - .sm\:bui-mr-7 { - margin-right: var(--bui-space-7); - } - - .sm\:bui-mr-8 { - margin-right: var(--bui-space-8); - } - - .sm\:bui-mr-9 { - margin-right: var(--bui-space-9); - } - - .sm\:bui-mr-10 { - margin-right: var(--bui-space-10); - } - - .sm\:bui-mr-11 { - margin-right: var(--bui-space-11); - } - - .sm\:bui-mr-12 { - margin-right: var(--bui-space-12); - } - - .sm\:bui-mr-13 { - margin-right: var(--bui-space-13); - } - - .sm\:bui-mr-14 { - margin-right: var(--bui-space-14); - } -} - -@media (width >= 1024px) { - .md\:bui-mr { - margin-right: var(--pr-md); - } - - .md\:bui-mr-0\.5 { - margin-right: var(--bui-space-0_5); - } - - .md\:bui-mr-1 { - margin-right: var(--bui-space-1); - } - - .md\:bui-mr-1\.5 { - margin-right: var(--bui-space-1_5); - } - - .md\:bui-mr-2 { - margin-right: var(--bui-space-2); - } - - .md\:bui-mr-3 { - margin-right: var(--bui-space-3); - } - - .md\:bui-mr-4 { - margin-right: var(--bui-space-4); - } - - .md\:bui-mr-5 { - margin-right: var(--bui-space-5); - } - - .md\:bui-mr-6 { - margin-right: var(--bui-space-6); - } - - .md\:bui-mr-7 { - margin-right: var(--bui-space-7); - } - - .md\:bui-mr-8 { - margin-right: var(--bui-space-8); - } - - .md\:bui-mr-9 { - margin-right: var(--bui-space-9); - } - - .md\:bui-mr-10 { - margin-right: var(--bui-space-10); - } - - .md\:bui-mr-11 { - margin-right: var(--bui-space-11); - } - - .md\:bui-mr-12 { - margin-right: var(--bui-space-12); - } - - .md\:bui-mr-13 { - margin-right: var(--bui-space-13); - } - - .md\:bui-mr-14 { - margin-right: var(--bui-space-14); - } -} - -@media (width >= 1280px) { - .lg\:bui-mr { - margin-right: var(--pr-lg); - } - - .lg\:bui-mr-0\.5 { - margin-right: var(--bui-space-0_5); - } - - .lg\:bui-mr-1 { - margin-right: var(--bui-space-1); - } - - .lg\:bui-mr-1\.5 { - margin-right: var(--bui-space-1_5); - } - - .lg\:bui-mr-2 { - margin-right: var(--bui-space-2); - } - - .lg\:bui-mr-3 { - margin-right: var(--bui-space-3); - } - - .lg\:bui-mr-4 { - margin-right: var(--bui-space-4); - } - - .lg\:bui-mr-5 { - margin-right: var(--bui-space-5); - } - - .lg\:bui-mr-6 { - margin-right: var(--bui-space-6); - } - - .lg\:bui-mr-7 { - margin-right: var(--bui-space-7); - } - - .lg\:bui-mr-8 { - margin-right: var(--bui-space-8); - } - - .lg\:bui-mr-9 { - margin-right: var(--bui-space-9); - } - - .lg\:bui-mr-10 { - margin-right: var(--bui-space-10); - } - - .lg\:bui-mr-11 { - margin-right: var(--bui-space-11); - } - - .lg\:bui-mr-12 { - margin-right: var(--bui-space-12); - } - - .lg\:bui-mr-13 { - margin-right: var(--bui-space-13); - } - - .lg\:bui-mr-14 { - margin-right: var(--bui-space-14); - } -} - -@media (width >= 1536px) { - .xl\:bui-mr { - margin-right: var(--pr-xl); - } - - .xl\:bui-mr-0\.5 { - margin-right: var(--bui-space-0_5); - } - - .xl\:bui-mr-1 { - margin-right: var(--bui-space-1); - } - - .xl\:bui-mr-1\.5 { - margin-right: var(--bui-space-1_5); - } - - .xl\:bui-mr-2 { - margin-right: var(--bui-space-2); - } - - .xl\:bui-mr-3 { - margin-right: var(--bui-space-3); - } - - .xl\:bui-mr-4 { - margin-right: var(--bui-space-4); - } - - .xl\:bui-mr-5 { - margin-right: var(--bui-space-5); - } - - .xl\:bui-mr-6 { - margin-right: var(--bui-space-6); - } - - .xl\:bui-mr-7 { - margin-right: var(--bui-space-7); - } - - .xl\:bui-mr-8 { - margin-right: var(--bui-space-8); - } - - .xl\:bui-mr-9 { - margin-right: var(--bui-space-9); - } - - .xl\:bui-mr-10 { - margin-right: var(--bui-space-10); - } - - .xl\:bui-mr-11 { - margin-right: var(--bui-space-11); - } - - .xl\:bui-mr-12 { - margin-right: var(--bui-space-12); - } - - .xl\:bui-mr-13 { - margin-right: var(--bui-space-13); - } - - .xl\:bui-mr-14 { - margin-right: var(--bui-space-14); - } -} - -.bui-mt { - margin-top: var(--mt); -} - -.bui-mt-0\.5 { - margin-top: var(--bui-space-0_5); -} - -.bui-mt-1 { - margin-top: var(--bui-space-1); -} - -.bui-mt-1\.5 { - margin-top: var(--bui-space-1_5); -} - -.bui-mt-2 { - margin-top: var(--bui-space-2); -} - -.bui-mt-3 { - margin-top: var(--bui-space-3); -} - -.bui-mt-4 { - margin-top: var(--bui-space-4); -} - -.bui-mt-5 { - margin-top: var(--bui-space-5); -} - -.bui-mt-6 { - margin-top: var(--bui-space-6); -} - -.bui-mt-7 { - margin-top: var(--bui-space-7); -} - -.bui-mt-8 { - margin-top: var(--bui-space-8); -} - -.bui-mt-9 { - margin-top: var(--bui-space-9); -} - -.bui-mt-10 { - margin-top: var(--bui-space-10); -} - -.bui-mt-11 { - margin-top: var(--bui-space-11); -} - -.bui-mt-12 { - margin-top: var(--bui-space-12); -} - -.bui-mt-13 { - margin-top: var(--bui-space-13); -} - -.bui-mt-14 { - margin-top: var(--bui-space-14); -} - -@media (width >= 640px) { - .xs\:bui-mt { - margin-top: var(--pt-xs); - } - - .xs\:bui-mt-0\.5 { - margin-top: var(--bui-space-0_5); - } - - .xs\:bui-mt-1 { - margin-top: var(--bui-space-1); - } - - .xs\:bui-mt-1\.5 { - margin-top: var(--bui-space-1_5); - } - - .xs\:bui-mt-2 { - margin-top: var(--bui-space-2); - } - - .xs\:bui-mt-3 { - margin-top: var(--bui-space-3); - } - - .xs\:bui-mt-4 { - margin-top: var(--bui-space-4); - } - - .xs\:bui-mt-5 { - margin-top: var(--bui-space-5); - } - - .xs\:bui-mt-6 { - margin-top: var(--bui-space-6); - } - - .xs\:bui-mt-7 { - margin-top: var(--bui-space-7); - } - - .xs\:bui-mt-8 { - margin-top: var(--bui-space-8); - } - - .xs\:bui-mt-9 { - margin-top: var(--bui-space-9); - } - - .xs\:bui-mt-10 { - margin-top: var(--bui-space-10); - } - - .xs\:bui-mt-11 { - margin-top: var(--bui-space-11); - } - - .xs\:bui-mt-12 { - margin-top: var(--bui-space-12); - } - - .xs\:bui-mt-13 { - margin-top: var(--bui-space-13); - } - - .xs\:bui-mt-14 { - margin-top: var(--bui-space-14); - } -} - -@media (width >= 768px) { - .sm\:bui-mt { - margin-top: var(--pt-sm); - } - - .sm\:bui-mt-0\.5 { - margin-top: var(--bui-space-0_5); - } - - .sm\:bui-mt-1 { - margin-top: var(--bui-space-1); - } - - .sm\:bui-mt-1\.5 { - margin-top: var(--bui-space-1_5); - } - - .sm\:bui-mt-2 { - margin-top: var(--bui-space-2); - } - - .sm\:bui-mt-3 { - margin-top: var(--bui-space-3); - } - - .sm\:bui-mt-4 { - margin-top: var(--bui-space-4); - } - - .sm\:bui-mt-5 { - margin-top: var(--bui-space-5); - } - - .sm\:bui-mt-6 { - margin-top: var(--bui-space-6); - } - - .sm\:bui-mt-7 { - margin-top: var(--bui-space-7); - } - - .sm\:bui-mt-8 { - margin-top: var(--bui-space-8); - } - - .sm\:bui-mt-9 { - margin-top: var(--bui-space-9); - } - - .sm\:bui-mt-10 { - margin-top: var(--bui-space-10); - } - - .sm\:bui-mt-11 { - margin-top: var(--bui-space-11); - } - - .sm\:bui-mt-12 { - margin-top: var(--bui-space-12); - } - - .sm\:bui-mt-13 { - margin-top: var(--bui-space-13); - } - - .sm\:bui-mt-14 { - margin-top: var(--bui-space-14); - } -} - -@media (width >= 1024px) { - .md\:bui-mt { - margin-top: var(--pt-md); - } - - .md\:bui-mt-0\.5 { - margin-top: var(--bui-space-0_5); - } - - .md\:bui-mt-1 { - margin-top: var(--bui-space-1); - } - - .md\:bui-mt-1\.5 { - margin-top: var(--bui-space-1_5); - } - - .md\:bui-mt-2 { - margin-top: var(--bui-space-2); - } - - .md\:bui-mt-3 { - margin-top: var(--bui-space-3); - } - - .md\:bui-mt-4 { - margin-top: var(--bui-space-4); - } - - .md\:bui-mt-5 { - margin-top: var(--bui-space-5); - } - - .md\:bui-mt-6 { - margin-top: var(--bui-space-6); - } - - .md\:bui-mt-7 { - margin-top: var(--bui-space-7); - } - - .md\:bui-mt-8 { - margin-top: var(--bui-space-8); - } - - .md\:bui-mt-9 { - margin-top: var(--bui-space-9); - } - - .md\:bui-mt-10 { - margin-top: var(--bui-space-10); - } - - .md\:bui-mt-11 { - margin-top: var(--bui-space-11); - } - - .md\:bui-mt-12 { - margin-top: var(--bui-space-12); - } - - .md\:bui-mt-13 { - margin-top: var(--bui-space-13); - } - - .md\:bui-mt-14 { - margin-top: var(--bui-space-14); - } -} - -@media (width >= 1280px) { - .lg\:bui-mt { - margin-top: var(--pt-lg); - } - - .lg\:bui-mt-0\.5 { - margin-top: var(--bui-space-0_5); - } - - .lg\:bui-mt-1 { - margin-top: var(--bui-space-1); - } - - .lg\:bui-mt-1\.5 { - margin-top: var(--bui-space-1_5); - } - - .lg\:bui-mt-2 { - margin-top: var(--bui-space-2); - } - - .lg\:bui-mt-3 { - margin-top: var(--bui-space-3); - } - - .lg\:bui-mt-4 { - margin-top: var(--bui-space-4); - } - - .lg\:bui-mt-5 { - margin-top: var(--bui-space-5); - } - - .lg\:bui-mt-6 { - margin-top: var(--bui-space-6); - } - - .lg\:bui-mt-7 { - margin-top: var(--bui-space-7); - } - - .lg\:bui-mt-8 { - margin-top: var(--bui-space-8); - } - - .lg\:bui-mt-9 { - margin-top: var(--bui-space-9); - } - - .lg\:bui-mt-10 { - margin-top: var(--bui-space-10); - } - - .lg\:bui-mt-11 { - margin-top: var(--bui-space-11); - } - - .lg\:bui-mt-12 { - margin-top: var(--bui-space-12); - } - - .lg\:bui-mt-13 { - margin-top: var(--bui-space-13); - } - - .lg\:bui-mt-14 { - margin-top: var(--bui-space-14); - } -} - -@media (width >= 1536px) { - .xl\:bui-mt { - margin-top: var(--pt-xl); - } - - .xl\:bui-mt-0\.5 { - margin-top: var(--bui-space-0_5); - } - - .xl\:bui-mt-1 { - margin-top: var(--bui-space-1); - } - - .xl\:bui-mt-1\.5 { - margin-top: var(--bui-space-1_5); - } - - .xl\:bui-mt-2 { - margin-top: var(--bui-space-2); - } - - .xl\:bui-mt-3 { - margin-top: var(--bui-space-3); - } - - .xl\:bui-mt-4 { - margin-top: var(--bui-space-4); - } - - .xl\:bui-mt-5 { - margin-top: var(--bui-space-5); - } - - .xl\:bui-mt-6 { - margin-top: var(--bui-space-6); - } - - .xl\:bui-mt-7 { - margin-top: var(--bui-space-7); - } - - .xl\:bui-mt-8 { - margin-top: var(--bui-space-8); - } - - .xl\:bui-mt-9 { - margin-top: var(--bui-space-9); - } - - .xl\:bui-mt-10 { - margin-top: var(--bui-space-10); - } - - .xl\:bui-mt-11 { - margin-top: var(--bui-space-11); - } - - .xl\:bui-mt-12 { - margin-top: var(--bui-space-12); - } - - .xl\:bui-mt-13 { - margin-top: var(--bui-space-13); - } - - .xl\:bui-mt-14 { - margin-top: var(--bui-space-14); - } -} - -.bui-mb { - margin-bottom: var(--mb); -} - -.bui-mb-0\.5 { - margin-bottom: var(--bui-space-0_5); -} - -.bui-mb-1 { - margin-bottom: var(--bui-space-1); -} - -.bui-mb-1\.5 { - margin-bottom: var(--bui-space-1_5); -} - -.bui-mb-2 { - margin-bottom: var(--bui-space-2); -} - -.bui-mb-3 { - margin-bottom: var(--bui-space-3); -} - -.bui-mb-4 { - margin-bottom: var(--bui-space-4); -} - -.bui-mb-5 { - margin-bottom: var(--bui-space-5); -} - -.bui-mb-6 { - margin-bottom: var(--bui-space-6); -} - -.bui-mb-7 { - margin-bottom: var(--bui-space-7); -} - -.bui-mb-8 { - margin-bottom: var(--bui-space-8); -} - -.bui-mb-9 { - margin-bottom: var(--bui-space-9); -} - -.bui-mb-10 { - margin-bottom: var(--bui-space-10); -} - -.bui-mb-11 { - margin-bottom: var(--bui-space-11); -} - -.bui-mb-12 { - margin-bottom: var(--bui-space-12); -} - -.bui-mb-13 { - margin-bottom: var(--bui-space-13); -} - -.bui-mb-14 { - margin-bottom: var(--bui-space-14); -} - -@media (width >= 640px) { - .xs\:bui-mb { - margin-bottom: var(--pb-xs); - } - - .xs\:bui-mb-0\.5 { - margin-bottom: var(--bui-space-0_5); - } - - .xs\:bui-mb-1 { - margin-bottom: var(--bui-space-1); - } - - .xs\:bui-mb-1\.5 { - margin-bottom: var(--bui-space-1_5); - } - - .xs\:bui-mb-2 { - margin-bottom: var(--bui-space-2); - } - - .xs\:bui-mb-3 { - margin-bottom: var(--bui-space-3); - } - - .xs\:bui-mb-4 { - margin-bottom: var(--bui-space-4); - } - - .xs\:bui-mb-5 { - margin-bottom: var(--bui-space-5); - } - - .xs\:bui-mb-6 { - margin-bottom: var(--bui-space-6); - } - - .xs\:bui-mb-7 { - margin-bottom: var(--bui-space-7); - } - - .xs\:bui-mb-8 { - margin-bottom: var(--bui-space-8); - } - - .xs\:bui-mb-9 { - margin-bottom: var(--bui-space-9); - } - - .xs\:bui-mb-10 { - margin-bottom: var(--bui-space-10); - } - - .xs\:bui-mb-11 { - margin-bottom: var(--bui-space-11); - } - - .xs\:bui-mb-12 { - margin-bottom: var(--bui-space-12); - } - - .xs\:bui-mb-13 { - margin-bottom: var(--bui-space-13); - } - - .xs\:bui-mb-14 { - margin-bottom: var(--bui-space-14); - } -} - -@media (width >= 768px) { - .sm\:bui-mb { - margin-bottom: var(--pb-sm); - } - - .sm\:bui-mb-0\.5 { - margin-bottom: var(--bui-space-0_5); - } - - .sm\:bui-mb-1 { - margin-bottom: var(--bui-space-1); - } - - .sm\:bui-mb-1\.5 { - margin-bottom: var(--bui-space-1_5); - } - - .sm\:bui-mb-2 { - margin-bottom: var(--bui-space-2); - } - - .sm\:bui-mb-3 { - margin-bottom: var(--bui-space-3); - } - - .sm\:bui-mb-4 { - margin-bottom: var(--bui-space-4); - } - - .sm\:bui-mb-5 { - margin-bottom: var(--bui-space-5); - } - - .sm\:bui-mb-6 { - margin-bottom: var(--bui-space-6); - } - - .sm\:bui-mb-7 { - margin-bottom: var(--bui-space-7); - } - - .sm\:bui-mb-8 { - margin-bottom: var(--bui-space-8); - } - - .sm\:bui-mb-9 { - margin-bottom: var(--bui-space-9); - } - - .sm\:bui-mb-10 { - margin-bottom: var(--bui-space-10); - } - - .sm\:bui-mb-11 { - margin-bottom: var(--bui-space-11); - } - - .sm\:bui-mb-12 { - margin-bottom: var(--bui-space-12); - } - - .sm\:bui-mb-13 { - margin-bottom: var(--bui-space-13); - } - - .sm\:bui-mb-14 { - margin-bottom: var(--bui-space-14); - } -} - -@media (width >= 1024px) { - .md\:bui-mb { - margin-bottom: var(--pb-md); - } - - .md\:bui-mb-0\.5 { - margin-bottom: var(--bui-space-0_5); - } - - .md\:bui-mb-1 { - margin-bottom: var(--bui-space-1); - } - - .md\:bui-mb-1\.5 { - margin-bottom: var(--bui-space-1_5); - } - - .md\:bui-mb-2 { - margin-bottom: var(--bui-space-2); - } - - .md\:bui-mb-3 { - margin-bottom: var(--bui-space-3); - } - - .md\:bui-mb-4 { - margin-bottom: var(--bui-space-4); - } - - .md\:bui-mb-5 { - margin-bottom: var(--bui-space-5); - } - - .md\:bui-mb-6 { - margin-bottom: var(--bui-space-6); - } - - .md\:bui-mb-7 { - margin-bottom: var(--bui-space-7); - } - - .md\:bui-mb-8 { - margin-bottom: var(--bui-space-8); - } - - .md\:bui-mb-9 { - margin-bottom: var(--bui-space-9); - } - - .md\:bui-mb-10 { - margin-bottom: var(--bui-space-10); - } - - .md\:bui-mb-11 { - margin-bottom: var(--bui-space-11); - } - - .md\:bui-mb-12 { - margin-bottom: var(--bui-space-12); - } - - .md\:bui-mb-13 { - margin-bottom: var(--bui-space-13); - } - - .md\:bui-mb-14 { - margin-bottom: var(--bui-space-14); - } -} - -@media (width >= 1280px) { - .lg\:bui-mb { - margin-bottom: var(--pb-lg); - } - - .lg\:bui-mb-0\.5 { - margin-bottom: var(--bui-space-0_5); - } - - .lg\:bui-mb-1 { - margin-bottom: var(--bui-space-1); - } - - .lg\:bui-mb-1\.5 { - margin-bottom: var(--bui-space-1_5); - } - - .lg\:bui-mb-2 { - margin-bottom: var(--bui-space-2); - } - - .lg\:bui-mb-3 { - margin-bottom: var(--bui-space-3); - } - - .lg\:bui-mb-4 { - margin-bottom: var(--bui-space-4); - } - - .lg\:bui-mb-5 { - margin-bottom: var(--bui-space-5); - } - - .lg\:bui-mb-6 { - margin-bottom: var(--bui-space-6); - } - - .lg\:bui-mb-7 { - margin-bottom: var(--bui-space-7); - } - - .lg\:bui-mb-8 { - margin-bottom: var(--bui-space-8); - } - - .lg\:bui-mb-9 { - margin-bottom: var(--bui-space-9); - } - - .lg\:bui-mb-10 { - margin-bottom: var(--bui-space-10); - } - - .lg\:bui-mb-11 { - margin-bottom: var(--bui-space-11); - } - - .lg\:bui-mb-12 { - margin-bottom: var(--bui-space-12); - } - - .lg\:bui-mb-13 { - margin-bottom: var(--bui-space-13); - } - - .lg\:bui-mb-14 { - margin-bottom: var(--bui-space-14); - } -} - -@media (width >= 1536px) { - .xl\:bui-mb { - margin-bottom: var(--pb-xl); - } - - .xl\:bui-mb-0\.5 { - margin-bottom: var(--bui-space-0_5); - } - - .xl\:bui-mb-1 { - margin-bottom: var(--bui-space-1); - } - - .xl\:bui-mb-1\.5 { - margin-bottom: var(--bui-space-1_5); - } - - .xl\:bui-mb-2 { - margin-bottom: var(--bui-space-2); - } - - .xl\:bui-mb-3 { - margin-bottom: var(--bui-space-3); - } - - .xl\:bui-mb-4 { - margin-bottom: var(--bui-space-4); - } - - .xl\:bui-mb-5 { - margin-bottom: var(--bui-space-5); - } - - .xl\:bui-mb-6 { - margin-bottom: var(--bui-space-6); - } - - .xl\:bui-mb-7 { - margin-bottom: var(--bui-space-7); - } - - .xl\:bui-mb-8 { - margin-bottom: var(--bui-space-8); - } - - .xl\:bui-mb-9 { - margin-bottom: var(--bui-space-9); - } - - .xl\:bui-mb-10 { - margin-bottom: var(--bui-space-10); - } - - .xl\:bui-mb-11 { - margin-bottom: var(--bui-space-11); - } - - .xl\:bui-mb-12 { - margin-bottom: var(--bui-space-12); - } - - .xl\:bui-mb-13 { - margin-bottom: var(--bui-space-13); - } - - .xl\:bui-mb-14 { - margin-bottom: var(--bui-space-14); - } -} - -.bui-my { - margin-top: var(--my); - margin-bottom: var(--my); -} - -.bui-my-0\.5 { - margin-top: var(--bui-space-0_5); - margin-bottom: var(--bui-space-0_5); -} - -.bui-my-1 { - margin-top: var(--bui-space-1); - margin-bottom: var(--bui-space-1); -} - -.bui-my-1\.5 { - margin-top: var(--bui-space-1_5); - margin-bottom: var(--bui-space-1_5); -} - -.bui-my-2 { - margin-top: var(--bui-space-2); - margin-bottom: var(--bui-space-2); -} - -.bui-my-3 { - margin-top: var(--bui-space-3); - margin-bottom: var(--bui-space-3); -} - -.bui-my-4 { - margin-top: var(--bui-space-4); - margin-bottom: var(--bui-space-4); -} - -.bui-my-5 { - margin-top: var(--bui-space-5); - margin-bottom: var(--bui-space-5); -} - -.bui-my-6 { - margin-top: var(--bui-space-6); - margin-bottom: var(--bui-space-6); -} - -.bui-my-7 { - margin-top: var(--bui-space-7); - margin-bottom: var(--bui-space-7); -} - -.bui-my-8 { - margin-top: var(--bui-space-8); - margin-bottom: var(--bui-space-8); -} - -.bui-my-9 { - margin-top: var(--bui-space-9); - margin-bottom: var(--bui-space-9); -} - -.bui-my-10 { - margin-top: var(--bui-space-10); - margin-bottom: var(--bui-space-10); -} - -.bui-my-11 { - margin-top: var(--bui-space-11); - margin-bottom: var(--bui-space-11); -} - -.bui-my-12 { - margin-top: var(--bui-space-12); - margin-bottom: var(--bui-space-12); -} - -.bui-my-13 { - margin-top: var(--bui-space-13); - margin-bottom: var(--bui-space-13); -} - -.bui-my-14 { - margin-top: var(--bui-space-14); - margin-bottom: var(--bui-space-14); -} - -@media (width >= 640px) { - .xs\:bui-my { - margin-top: var(--py-xs); - margin-bottom: var(--py-xs); - } - - .xs\:bui-my-0\.5 { - margin-top: var(--bui-space-0_5); - margin-bottom: var(--bui-space-0_5); - } - - .xs\:bui-my-1 { - margin-top: var(--bui-space-1); - margin-bottom: var(--bui-space-1); - } - - .xs\:bui-my-1\.5 { - margin-top: var(--bui-space-1_5); - margin-bottom: var(--bui-space-1_5); - } - - .xs\:bui-my-2 { - margin-top: var(--bui-space-2); - margin-bottom: var(--bui-space-2); - } - - .xs\:bui-my-3 { - margin-top: var(--bui-space-3); - margin-bottom: var(--bui-space-3); - } - - .xs\:bui-my-4 { - margin-top: var(--bui-space-4); - margin-bottom: var(--bui-space-4); - } - - .xs\:bui-my-5 { - margin-top: var(--bui-space-5); - margin-bottom: var(--bui-space-5); - } - - .xs\:bui-my-6 { - margin-top: var(--bui-space-6); - margin-bottom: var(--bui-space-6); - } - - .xs\:bui-my-7 { - margin-top: var(--bui-space-7); - margin-bottom: var(--bui-space-7); - } - - .xs\:bui-my-8 { - margin-top: var(--bui-space-8); - margin-bottom: var(--bui-space-8); - } - - .xs\:bui-my-9 { - margin-top: var(--bui-space-9); - margin-bottom: var(--bui-space-9); - } - - .xs\:bui-my-10 { - margin-top: var(--bui-space-10); - margin-bottom: var(--bui-space-10); - } - - .xs\:bui-my-11 { - margin-top: var(--bui-space-11); - margin-bottom: var(--bui-space-11); - } - - .xs\:bui-my-12 { - margin-top: var(--bui-space-12); - margin-bottom: var(--bui-space-12); - } - - .xs\:bui-my-13 { - margin-top: var(--bui-space-13); - margin-bottom: var(--bui-space-13); - } - - .xs\:bui-my-14 { - margin-top: var(--bui-space-14); - margin-bottom: var(--bui-space-14); - } -} - -@media (width >= 768px) { - .sm\:bui-my { - margin-top: var(--py-sm); - margin-bottom: var(--py-sm); - } - - .sm\:bui-my-0\.5 { - margin-top: var(--bui-space-0_5); - margin-bottom: var(--bui-space-0_5); - } - - .sm\:bui-my-1 { - margin-top: var(--bui-space-1); - margin-bottom: var(--bui-space-1); - } - - .sm\:bui-my-1\.5 { - margin-top: var(--bui-space-1_5); - margin-bottom: var(--bui-space-1_5); - } - - .sm\:bui-my-2 { - margin-top: var(--bui-space-2); - margin-bottom: var(--bui-space-2); - } - - .sm\:bui-my-3 { - margin-top: var(--bui-space-3); - margin-bottom: var(--bui-space-3); - } - - .sm\:bui-my-4 { - margin-top: var(--bui-space-4); - margin-bottom: var(--bui-space-4); - } - - .sm\:bui-my-5 { - margin-top: var(--bui-space-5); - margin-bottom: var(--bui-space-5); - } - - .sm\:bui-my-6 { - margin-top: var(--bui-space-6); - margin-bottom: var(--bui-space-6); - } - - .sm\:bui-my-7 { - margin-top: var(--bui-space-7); - margin-bottom: var(--bui-space-7); - } - - .sm\:bui-my-8 { - margin-top: var(--bui-space-8); - margin-bottom: var(--bui-space-8); - } - - .sm\:bui-my-9 { - margin-top: var(--bui-space-9); - margin-bottom: var(--bui-space-9); - } - - .sm\:bui-my-10 { - margin-top: var(--bui-space-10); - margin-bottom: var(--bui-space-10); - } - - .sm\:bui-my-11 { - margin-top: var(--bui-space-11); - margin-bottom: var(--bui-space-11); - } - - .sm\:bui-my-12 { - margin-top: var(--bui-space-12); - margin-bottom: var(--bui-space-12); - } - - .sm\:bui-my-13 { - margin-top: var(--bui-space-13); - margin-bottom: var(--bui-space-13); - } - - .sm\:bui-my-14 { - margin-top: var(--bui-space-14); - margin-bottom: var(--bui-space-14); - } -} - -@media (width >= 1024px) { - .md\:bui-my { - margin-top: var(--py-md); - margin-bottom: var(--py-md); - } - - .md\:bui-my-0\.5 { - margin-top: var(--bui-space-0_5); - margin-bottom: var(--bui-space-0_5); - } - - .md\:bui-my-1 { - margin-top: var(--bui-space-1); - margin-bottom: var(--bui-space-1); - } - - .md\:bui-my-1\.5 { - margin-top: var(--bui-space-1_5); - margin-bottom: var(--bui-space-1_5); - } - - .md\:bui-my-2 { - margin-top: var(--bui-space-2); - margin-bottom: var(--bui-space-2); - } - - .md\:bui-my-3 { - margin-top: var(--bui-space-3); - margin-bottom: var(--bui-space-3); - } - - .md\:bui-my-4 { - margin-top: var(--bui-space-4); - margin-bottom: var(--bui-space-4); - } - - .md\:bui-my-5 { - margin-top: var(--bui-space-5); - margin-bottom: var(--bui-space-5); - } - - .md\:bui-my-6 { - margin-top: var(--bui-space-6); - margin-bottom: var(--bui-space-6); - } - - .md\:bui-my-7 { - margin-top: var(--bui-space-7); - margin-bottom: var(--bui-space-7); - } - - .md\:bui-my-8 { - margin-top: var(--bui-space-8); - margin-bottom: var(--bui-space-8); - } - - .md\:bui-my-9 { - margin-top: var(--bui-space-9); - margin-bottom: var(--bui-space-9); - } - - .md\:bui-my-10 { - margin-top: var(--bui-space-10); - margin-bottom: var(--bui-space-10); - } - - .md\:bui-my-11 { - margin-top: var(--bui-space-11); - margin-bottom: var(--bui-space-11); - } - - .md\:bui-my-12 { - margin-top: var(--bui-space-12); - margin-bottom: var(--bui-space-12); - } - - .md\:bui-my-13 { - margin-top: var(--bui-space-13); - margin-bottom: var(--bui-space-13); - } - - .md\:bui-my-14 { - margin-top: var(--bui-space-14); - margin-bottom: var(--bui-space-14); - } -} - -@media (width >= 1280px) { - .lg\:bui-my { - margin-top: var(--py-lg); - margin-bottom: var(--py-lg); - } - - .lg\:bui-my-0\.5 { - margin-top: var(--bui-space-0_5); - margin-bottom: var(--bui-space-0_5); - } - - .lg\:bui-my-1 { - margin-top: var(--bui-space-1); - margin-bottom: var(--bui-space-1); - } - - .lg\:bui-my-1\.5 { - margin-top: var(--bui-space-1_5); - margin-bottom: var(--bui-space-1_5); - } - - .lg\:bui-my-2 { - margin-top: var(--bui-space-2); - margin-bottom: var(--bui-space-2); - } - - .lg\:bui-my-3 { - margin-top: var(--bui-space-3); - margin-bottom: var(--bui-space-3); - } - - .lg\:bui-my-4 { - margin-top: var(--bui-space-4); - margin-bottom: var(--bui-space-4); - } - - .lg\:bui-my-5 { - margin-top: var(--bui-space-5); - margin-bottom: var(--bui-space-5); - } - - .lg\:bui-my-6 { - margin-top: var(--bui-space-6); - margin-bottom: var(--bui-space-6); - } - - .lg\:bui-my-7 { - margin-top: var(--bui-space-7); - margin-bottom: var(--bui-space-7); - } - - .lg\:bui-my-8 { - margin-top: var(--bui-space-8); - margin-bottom: var(--bui-space-8); - } - - .lg\:bui-my-9 { - margin-top: var(--bui-space-9); - margin-bottom: var(--bui-space-9); - } - - .lg\:bui-my-10 { - margin-top: var(--bui-space-10); - margin-bottom: var(--bui-space-10); - } - - .lg\:bui-my-11 { - margin-top: var(--bui-space-11); - margin-bottom: var(--bui-space-11); - } - - .lg\:bui-my-12 { - margin-top: var(--bui-space-12); - margin-bottom: var(--bui-space-12); - } - - .lg\:bui-my-13 { - margin-top: var(--bui-space-13); - margin-bottom: var(--bui-space-13); - } - - .lg\:bui-my-14 { - margin-top: var(--bui-space-14); - margin-bottom: var(--bui-space-14); - } -} - -@media (width >= 1536px) { - .xl\:bui-my { - margin-top: var(--py-xl); - margin-bottom: var(--py-xl); - } - - .xl\:bui-my-0\.5 { - margin-top: var(--bui-space-0_5); - margin-bottom: var(--bui-space-0_5); - } - - .xl\:bui-my-1 { - margin-top: var(--bui-space-1); - margin-bottom: var(--bui-space-1); - } - - .xl\:bui-my-1\.5 { - margin-top: var(--bui-space-1_5); - margin-bottom: var(--bui-space-1_5); - } - - .xl\:bui-my-2 { - margin-top: var(--bui-space-2); - margin-bottom: var(--bui-space-2); - } - - .xl\:bui-my-3 { - margin-top: var(--bui-space-3); - margin-bottom: var(--bui-space-3); - } - - .xl\:bui-my-4 { - margin-top: var(--bui-space-4); - margin-bottom: var(--bui-space-4); - } - - .xl\:bui-my-5 { - margin-top: var(--bui-space-5); - margin-bottom: var(--bui-space-5); - } - - .xl\:bui-my-6 { - margin-top: var(--bui-space-6); - margin-bottom: var(--bui-space-6); - } - - .xl\:bui-my-7 { - margin-top: var(--bui-space-7); - margin-bottom: var(--bui-space-7); - } - - .xl\:bui-my-8 { - margin-top: var(--bui-space-8); - margin-bottom: var(--bui-space-8); - } - - .xl\:bui-my-9 { - margin-top: var(--bui-space-9); - margin-bottom: var(--bui-space-9); - } - - .xl\:bui-my-10 { - margin-top: var(--bui-space-10); - margin-bottom: var(--bui-space-10); - } - - .xl\:bui-my-11 { - margin-top: var(--bui-space-11); - margin-bottom: var(--bui-space-11); - } - - .xl\:bui-my-12 { - margin-top: var(--bui-space-12); - margin-bottom: var(--bui-space-12); - } - - .xl\:bui-my-13 { - margin-top: var(--bui-space-13); - margin-bottom: var(--bui-space-13); - } - - .xl\:bui-my-14 { - margin-top: var(--bui-space-14); - margin-bottom: var(--bui-space-14); - } -} - -.bui-mx { - margin-left: var(--mx); - margin-right: var(--mx); -} - -.bui-mx-0\.5 { - margin-left: var(--bui-space-0_5); - margin-right: var(--bui-space-0_5); -} - -.bui-mx-1 { - margin-left: var(--bui-space-1); - margin-right: var(--bui-space-1); -} - -.bui-mx-1\.5 { - margin-left: var(--bui-space-1_5); - margin-right: var(--bui-space-1_5); -} - -.bui-mx-2 { - margin-left: var(--bui-space-2); - margin-right: var(--bui-space-2); -} - -.bui-mx-3 { - margin-left: var(--bui-space-3); - margin-right: var(--bui-space-3); -} - -.bui-mx-4 { - margin-left: var(--bui-space-4); - margin-right: var(--bui-space-4); -} - -.bui-mx-5 { - margin-left: var(--bui-space-5); - margin-right: var(--bui-space-5); -} - -.bui-mx-6 { - margin-left: var(--bui-space-6); - margin-right: var(--bui-space-6); -} - -.bui-mx-7 { - margin-left: var(--bui-space-7); - margin-right: var(--bui-space-7); -} - -.bui-mx-8 { - margin-left: var(--bui-space-8); - margin-right: var(--bui-space-8); -} - -.bui-mx-9 { - margin-left: var(--bui-space-9); - margin-right: var(--bui-space-9); -} - -.bui-mx-10 { - margin-left: var(--bui-space-10); - margin-right: var(--bui-space-10); -} - -.bui-mx-11 { - margin-left: var(--bui-space-11); - margin-right: var(--bui-space-11); -} - -.bui-mx-12 { - margin-left: var(--bui-space-12); - margin-right: var(--bui-space-12); -} - -.bui-mx-13 { - margin-left: var(--bui-space-13); - margin-right: var(--bui-space-13); -} - -.bui-mx-14 { - margin-left: var(--bui-space-14); - margin-right: var(--bui-space-14); -} - -@media (width >= 640px) { - .xs\:bui-mx { - margin-left: var(--px-xs); - margin-right: var(--px-xs); - } - - .xs\:bui-mx-0\.5 { - margin-left: var(--bui-space-0_5); - margin-right: var(--bui-space-0_5); - } - - .xs\:bui-mx-1 { - margin-left: var(--bui-space-1); - margin-right: var(--bui-space-1); - } - - .xs\:bui-mx-1\.5 { - margin-left: var(--bui-space-1_5); - margin-right: var(--bui-space-1_5); - } - - .xs\:bui-mx-2 { - margin-left: var(--bui-space-2); - margin-right: var(--bui-space-2); - } - - .xs\:bui-mx-3 { - margin-left: var(--bui-space-3); - margin-right: var(--bui-space-3); - } - - .xs\:bui-mx-4 { - margin-left: var(--bui-space-4); - margin-right: var(--bui-space-4); - } - - .xs\:bui-mx-5 { - margin-left: var(--bui-space-5); - margin-right: var(--bui-space-5); - } - - .xs\:bui-mx-6 { - margin-left: var(--bui-space-6); - margin-right: var(--bui-space-6); - } - - .xs\:bui-mx-7 { - margin-left: var(--bui-space-7); - margin-right: var(--bui-space-7); - } - - .xs\:bui-mx-8 { - margin-left: var(--bui-space-8); - margin-right: var(--bui-space-8); - } - - .xs\:bui-mx-9 { - margin-left: var(--bui-space-9); - margin-right: var(--bui-space-9); - } - - .xs\:bui-mx-10 { - margin-left: var(--bui-space-10); - margin-right: var(--bui-space-10); - } - - .xs\:bui-mx-11 { - margin-left: var(--bui-space-11); - margin-right: var(--bui-space-11); - } - - .xs\:bui-mx-12 { - margin-left: var(--bui-space-12); - margin-right: var(--bui-space-12); - } - - .xs\:bui-mx-13 { - margin-left: var(--bui-space-13); - margin-right: var(--bui-space-13); - } - - .xs\:bui-mx-14 { - margin-left: var(--bui-space-14); - margin-right: var(--bui-space-14); - } -} - -@media (width >= 768px) { - .sm\:bui-mx { - margin-left: var(--px-sm); - margin-right: var(--px-sm); - } - - .sm\:bui-mx-0\.5 { - margin-left: var(--bui-space-0_5); - margin-right: var(--bui-space-0_5); - } - - .sm\:bui-mx-1 { - margin-left: var(--bui-space-1); - margin-right: var(--bui-space-1); - } - - .sm\:bui-mx-1\.5 { - margin-left: var(--bui-space-1_5); - margin-right: var(--bui-space-1_5); - } - - .sm\:bui-mx-2 { - margin-left: var(--bui-space-2); - margin-right: var(--bui-space-2); - } - - .sm\:bui-mx-3 { - margin-left: var(--bui-space-3); - margin-right: var(--bui-space-3); - } - - .sm\:bui-mx-4 { - margin-left: var(--bui-space-4); - margin-right: var(--bui-space-4); - } - - .sm\:bui-mx-5 { - margin-left: var(--bui-space-5); - margin-right: var(--bui-space-5); - } - - .sm\:bui-mx-6 { - margin-left: var(--bui-space-6); - margin-right: var(--bui-space-6); - } - - .sm\:bui-mx-7 { - margin-left: var(--bui-space-7); - margin-right: var(--bui-space-7); - } - - .sm\:bui-mx-8 { - margin-left: var(--bui-space-8); - margin-right: var(--bui-space-8); - } - - .sm\:bui-mx-9 { - margin-left: var(--bui-space-9); - margin-right: var(--bui-space-9); - } - - .sm\:bui-mx-10 { - margin-left: var(--bui-space-10); - margin-right: var(--bui-space-10); - } - - .sm\:bui-mx-11 { - margin-left: var(--bui-space-11); - margin-right: var(--bui-space-11); - } - - .sm\:bui-mx-12 { - margin-left: var(--bui-space-12); - margin-right: var(--bui-space-12); - } - - .sm\:bui-mx-13 { - margin-left: var(--bui-space-13); - margin-right: var(--bui-space-13); - } - - .sm\:bui-mx-14 { - margin-left: var(--bui-space-14); - margin-right: var(--bui-space-14); - } -} - -@media (width >= 1024px) { - .md\:bui-mx { - margin-left: var(--px-md); - margin-right: var(--px-md); - } - - .md\:bui-mx-0\.5 { - margin-left: var(--bui-space-0_5); - margin-right: var(--bui-space-0_5); - } - - .md\:bui-mx-1 { - margin-left: var(--bui-space-1); - margin-right: var(--bui-space-1); - } - - .md\:bui-mx-1\.5 { - margin-left: var(--bui-space-1_5); - margin-right: var(--bui-space-1_5); - } - - .md\:bui-mx-2 { - margin-left: var(--bui-space-2); - margin-right: var(--bui-space-2); - } - - .md\:bui-mx-3 { - margin-left: var(--bui-space-3); - margin-right: var(--bui-space-3); - } - - .md\:bui-mx-4 { - margin-left: var(--bui-space-4); - margin-right: var(--bui-space-4); - } - - .md\:bui-mx-5 { - margin-left: var(--bui-space-5); - margin-right: var(--bui-space-5); - } - - .md\:bui-mx-6 { - margin-left: var(--bui-space-6); - margin-right: var(--bui-space-6); - } - - .md\:bui-mx-7 { - margin-left: var(--bui-space-7); - margin-right: var(--bui-space-7); - } - - .md\:bui-mx-8 { - margin-left: var(--bui-space-8); - margin-right: var(--bui-space-8); - } - - .md\:bui-mx-9 { - margin-left: var(--bui-space-9); - margin-right: var(--bui-space-9); - } - - .md\:bui-mx-10 { - margin-left: var(--bui-space-10); - margin-right: var(--bui-space-10); - } - - .md\:bui-mx-11 { - margin-left: var(--bui-space-11); - margin-right: var(--bui-space-11); - } - - .md\:bui-mx-12 { - margin-left: var(--bui-space-12); - margin-right: var(--bui-space-12); - } - - .md\:bui-mx-13 { - margin-left: var(--bui-space-13); - margin-right: var(--bui-space-13); - } - - .md\:bui-mx-14 { - margin-left: var(--bui-space-14); - margin-right: var(--bui-space-14); - } -} - -@media (width >= 1280px) { - .lg\:bui-mx { - margin-left: var(--px-lg); - margin-right: var(--px-lg); - } - - .lg\:bui-mx-0\.5 { - margin-left: var(--bui-space-0_5); - margin-right: var(--bui-space-0_5); - } - - .lg\:bui-mx-1 { - margin-left: var(--bui-space-1); - margin-right: var(--bui-space-1); - } - - .lg\:bui-mx-1\.5 { - margin-left: var(--bui-space-1_5); - margin-right: var(--bui-space-1_5); - } - - .lg\:bui-mx-2 { - margin-left: var(--bui-space-2); - margin-right: var(--bui-space-2); - } - - .lg\:bui-mx-3 { - margin-left: var(--bui-space-3); - margin-right: var(--bui-space-3); - } - - .lg\:bui-mx-4 { - margin-left: var(--bui-space-4); - margin-right: var(--bui-space-4); - } - - .lg\:bui-mx-5 { - margin-left: var(--bui-space-5); - margin-right: var(--bui-space-5); - } - - .lg\:bui-mx-6 { - margin-left: var(--bui-space-6); - margin-right: var(--bui-space-6); - } - - .lg\:bui-mx-7 { - margin-left: var(--bui-space-7); - margin-right: var(--bui-space-7); - } - - .lg\:bui-mx-8 { - margin-left: var(--bui-space-8); - margin-right: var(--bui-space-8); - } - - .lg\:bui-mx-9 { - margin-left: var(--bui-space-9); - margin-right: var(--bui-space-9); - } - - .lg\:bui-mx-10 { - margin-left: var(--bui-space-10); - margin-right: var(--bui-space-10); - } - - .lg\:bui-mx-11 { - margin-left: var(--bui-space-11); - margin-right: var(--bui-space-11); - } - - .lg\:bui-mx-12 { - margin-left: var(--bui-space-12); - margin-right: var(--bui-space-12); - } - - .lg\:bui-mx-13 { - margin-left: var(--bui-space-13); - margin-right: var(--bui-space-13); - } - - .lg\:bui-mx-14 { - margin-left: var(--bui-space-14); - margin-right: var(--bui-space-14); - } -} - -@media (width >= 1536px) { - .xl\:bui-mx { - margin-left: var(--px-xl); - margin-right: var(--px-xl); - } - - .xl\:bui-mx-0\.5 { - margin-left: var(--bui-space-0_5); - margin-right: var(--bui-space-0_5); - } - - .xl\:bui-mx-1 { - margin-left: var(--bui-space-1); - margin-right: var(--bui-space-1); - } - - .xl\:bui-mx-1\.5 { - margin-left: var(--bui-space-1_5); - margin-right: var(--bui-space-1_5); - } - - .xl\:bui-mx-2 { - margin-left: var(--bui-space-2); - margin-right: var(--bui-space-2); - } - - .xl\:bui-mx-3 { - margin-left: var(--bui-space-3); - margin-right: var(--bui-space-3); - } - - .xl\:bui-mx-4 { - margin-left: var(--bui-space-4); - margin-right: var(--bui-space-4); - } - - .xl\:bui-mx-5 { - margin-left: var(--bui-space-5); - margin-right: var(--bui-space-5); - } - - .xl\:bui-mx-6 { - margin-left: var(--bui-space-6); - margin-right: var(--bui-space-6); - } - - .xl\:bui-mx-7 { - margin-left: var(--bui-space-7); - margin-right: var(--bui-space-7); - } - - .xl\:bui-mx-8 { - margin-left: var(--bui-space-8); - margin-right: var(--bui-space-8); - } - - .xl\:bui-mx-9 { - margin-left: var(--bui-space-9); - margin-right: var(--bui-space-9); - } - - .xl\:bui-mx-10 { - margin-left: var(--bui-space-10); - margin-right: var(--bui-space-10); - } - - .xl\:bui-mx-11 { - margin-left: var(--bui-space-11); - margin-right: var(--bui-space-11); - } - - .xl\:bui-mx-12 { - margin-left: var(--bui-space-12); - margin-right: var(--bui-space-12); - } - - .xl\:bui-mx-13 { - margin-left: var(--bui-space-13); - margin-right: var(--bui-space-13); - } - - .xl\:bui-mx-14 { - margin-left: var(--bui-space-14); - margin-right: var(--bui-space-14); - } -} - -.bui-display-none { - display: none; -} - -.bui-display-inline { - display: inline; -} - -.bui-display-inline-block { - display: inline-block; -} - -.bui-display-block { - display: block; -} - -@media (width >= 640px) { - .xs\:bui-display-none { - display: none; - } - - .xs\:bui-display-inline { - display: inline; - } - - .xs\:bui-display-inline-block { - display: inline-block; - } - - .xs\:bui-display-block { - display: block; - } -} - -@media (width >= 768px) { - .sm\:bui-display-none { - display: none; - } - - .sm\:bui-display-inline { - display: inline; - } - - .sm\:bui-display-inline-block { - display: inline-block; - } - - .sm\:bui-display-block { - display: block; - } -} - -@media (width >= 1024px) { - .md\:bui-display-none { - display: none; - } - - .md\:bui-display-inline { - display: inline; - } - - .md\:bui-display-inline-block { - display: inline-block; - } - - .md\:bui-display-block { - display: block; - } -} - -@media (width >= 1280px) { - .lg\:bui-display-none { - display: none; - } - - .lg\:bui-display-inline { - display: inline; - } - - .lg\:bui-display-inline-block { - display: inline-block; - } - - .lg\:bui-display-block { - display: block; - } -} - -@media (width >= 1536px) { - .xl\:bui-display-none { - display: none; - } - - .xl\:bui-display-inline { - display: inline; - } - - .xl\:bui-display-inline-block { - display: inline-block; - } - - .xl\:bui-display-block { - display: block; - } -} - -.bui-w { - width: var(--width); -} - -.bui-min-w { - min-width: var(--min-width); -} - -.bui-max-w { - max-width: var(--max-width); -} - -@media (width >= 640px) { - .xs\:bui-w { - width: var(--width); - } - - .xs\:bui-min-w { - min-width: var(--min-width); - } - - .xs\:bui-max-w { - max-width: var(--max-width); - } -} - -@media (width >= 768px) { - .sm\:bui-w { - width: var(--width); - } - - .sm\:bui-min-w { - min-width: var(--min-width); - } - - .sm\:bui-max-w { - max-width: var(--max-width); - } -} - -@media (width >= 1024px) { - .md\:bui-w { - width: var(--width); - } - - .md\:bui-min-w { - min-width: var(--min-width); - } - - .md\:bui-max-w { - max-width: var(--max-width); - } -} - -@media (width >= 1280px) { - .lg\:bui-w { - width: var(--width); - } - - .lg\:bui-min-w { - min-width: var(--min-width); - } - - .lg\:bui-max-w { - max-width: var(--max-width); - } -} - -@media (width >= 1536px) { - .xl\:bui-w { - width: var(--width); - } - - .xl\:bui-min-w { - min-width: var(--min-width); - } - - .xl\:bui-max-w { - max-width: var(--max-width); - } -} - -.bui-h { - height: var(--height); -} - -.bui-min-h { - min-height: var(--min-height); -} - -.bui-max-h { - max-height: var(--max-height); -} - -@media (width >= 640px) { - .xs\:bui-h { - height: var(--height); - } - - .xs\:bui-min-h { - min-height: var(--min-height); - } - - .xs\:bui-max-h { - max-height: var(--max-height); - } -} - -@media (width >= 768px) { - .sm\:bui-h { - height: var(--height); - } - - .sm\:bui-min-h { - min-height: var(--min-height); - } - - .sm\:bui-max-h { - max-height: var(--max-height); - } -} - -@media (width >= 1024px) { - .md\:bui-h { - height: var(--height); - } - - .md\:bui-min-h { - min-height: var(--min-height); - } - - .md\:bui-max-h { - max-height: var(--max-height); - } -} - -@media (width >= 1280px) { - .lg\:bui-h { - height: var(--height); - } - - .lg\:bui-min-h { - min-height: var(--min-height); - } - - .lg\:bui-max-h { - max-height: var(--max-height); - } -} - -@media (width >= 1536px) { - .xl\:bui-h { - height: var(--height); - } - - .xl\:bui-min-h { - min-height: var(--min-height); - } - - .xl\:bui-max-h { - max-height: var(--max-height); - } -} - -.bui-position-absolute { - position: absolute; -} - -.bui-position-fixed { - position: fixed; -} - -.bui-position-sticky { - position: sticky; -} - -.bui-position-relative { - position: relative; -} - -.bui-position-static { - position: static; -} - -@media (width >= 640px) { - .xs\:bui-position-absolute { - position: absolute; - } - - .xs\:bui-position-fixed { - position: fixed; - } - - .xs\:bui-position-sticky { - position: sticky; - } - - .xs\:bui-position-relative { - position: relative; - } - - .xs\:bui-position-static { - position: static; - } -} - -@media (width >= 768px) { - .sm\:bui-position-absolute { - position: absolute; - } - - .sm\:bui-position-fixed { - position: fixed; - } - - .sm\:bui-position-sticky { - position: sticky; - } - - .sm\:bui-position-relative { - position: relative; - } - - .sm\:bui-position-static { - position: static; - } -} - -@media (width >= 1024px) { - .md\:bui-position-absolute { - position: absolute; - } - - .md\:bui-position-fixed { - position: fixed; - } - - .md\:bui-position-sticky { - position: sticky; - } - - .md\:bui-position-relative { - position: relative; - } - - .md\:bui-position-static { - position: static; - } -} - -@media (width >= 1280px) { - .lg\:bui-position-absolute { - position: absolute; - } - - .lg\:bui-position-fixed { - position: fixed; - } - - .lg\:bui-position-sticky { - position: sticky; - } - - .lg\:bui-position-relative { - position: relative; - } - - .lg\:bui-position-static { - position: static; - } -} - -@media (width >= 1536px) { - .xl\:bui-position-absolute { - position: absolute; - } - - .xl\:bui-position-fixed { - position: fixed; - } - - .xl\:bui-position-sticky { - position: sticky; - } - - .xl\:bui-position-relative { - position: relative; - } - - .xl\:bui-position-static { - position: static; - } -} - -.bui-columns-1 { - grid-template-columns: repeat(1, minmax(0, 1fr)); -} - -.bui-columns-2 { - grid-template-columns: repeat(2, minmax(0, 1fr)); -} - -.bui-columns-3 { - grid-template-columns: repeat(3, minmax(0, 1fr)); -} - -.bui-columns-4 { - grid-template-columns: repeat(4, minmax(0, 1fr)); -} - -.bui-columns-5 { - grid-template-columns: repeat(5, minmax(0, 1fr)); -} - -.bui-columns-6 { - grid-template-columns: repeat(6, minmax(0, 1fr)); -} - -.bui-columns-7 { - grid-template-columns: repeat(7, minmax(0, 1fr)); -} - -.bui-columns-8 { - grid-template-columns: repeat(8, minmax(0, 1fr)); -} - -.bui-columns-9 { - grid-template-columns: repeat(9, minmax(0, 1fr)); -} - -.bui-columns-10 { - grid-template-columns: repeat(10, minmax(0, 1fr)); -} - -.bui-columns-11 { - grid-template-columns: repeat(11, minmax(0, 1fr)); -} - -.bui-columns-12 { - grid-template-columns: repeat(12, minmax(0, 1fr)); -} - -.bui-columns-auto { - grid-template-columns: repeat(auto-fit, minmax(0, 1fr)); -} - -.bui-col-span-1 { - grid-column: span 1 / span 1; -} - -.bui-col-span-2 { - grid-column: span 2 / span 2; -} - -.bui-col-span-3 { - grid-column: span 3 / span 3; -} - -.bui-col-span-4 { - grid-column: span 4 / span 4; -} - -.bui-col-span-5 { - grid-column: span 5 / span 5; -} - -.bui-col-span-6 { - grid-column: span 6 / span 6; -} - -.bui-col-span-7 { - grid-column: span 7 / span 7; -} - -.bui-col-span-8 { - grid-column: span 8 / span 8; -} - -.bui-col-span-9 { - grid-column: span 9 / span 9; -} - -.bui-col-span-10 { - grid-column: span 10 / span 10; -} - -.bui-col-span-11 { - grid-column: span 11 / span 11; -} - -.bui-col-span-12 { - grid-column: span 12 / span 12; -} - -.bui-col-span-auto { - grid-column: span auto / span auto; -} - -.bui-col-start-1 { - grid-column-start: 1; -} - -.bui-col-start-2 { - grid-column-start: 2; -} - -.bui-col-start-3 { - grid-column-start: 3; -} - -.bui-col-start-4 { - grid-column-start: 4; -} - -.bui-col-start-5 { - grid-column-start: 5; -} - -.bui-col-start-6 { - grid-column-start: 6; -} - -.bui-col-start-7 { - grid-column-start: 7; -} - -.bui-col-start-8 { - grid-column-start: 8; -} - -.bui-col-start-9 { - grid-column-start: 9; -} - -.bui-col-start-10 { - grid-column-start: 10; -} - -.bui-col-start-11 { - grid-column-start: 11; -} - -.bui-col-start-12 { - grid-column-start: 12; -} - -.bui-col-start-13 { - grid-column-start: 13; -} - -.bui-col-start-auto { - grid-column-start: auto; -} - -.bui-col-end-1 { - grid-column-end: 1; -} - -.bui-col-end-2 { - grid-column-end: 2; -} - -.bui-col-end-3 { - grid-column-end: 3; -} - -.bui-col-end-4 { - grid-column-end: 4; -} - -.bui-col-end-5 { - grid-column-end: 5; -} - -.bui-col-end-6 { - grid-column-end: 6; -} - -.bui-col-end-7 { - grid-column-end: 7; -} - -.bui-col-end-8 { - grid-column-end: 8; -} - -.bui-col-end-9 { - grid-column-end: 9; -} - -.bui-col-end-10 { - grid-column-end: 10; -} - -.bui-col-end-11 { - grid-column-end: 11; -} - -.bui-col-end-12 { - grid-column-end: 12; -} - -.bui-col-end-13 { - grid-column-end: 13; -} - -.bui-col-end-auto { - grid-column-end: auto; -} - -.bui-row-span-1 { - grid-row: span 1 / span 1; -} - -.bui-row-span-2 { - grid-row: span 2 / span 2; -} - -.bui-row-span-3 { - grid-row: span 3 / span 3; -} - -.bui-row-span-4 { - grid-row: span 4 / span 4; -} - -.bui-row-span-5 { - grid-row: span 5 / span 5; -} - -.bui-row-span-6 { - grid-row: span 6 / span 6; -} - -.bui-row-span-7 { - grid-row: span 7 / span 7; -} - -.bui-row-span-8 { - grid-row: span 8 / span 8; -} - -.bui-row-span-9 { - grid-row: span 9 / span 9; -} - -.bui-row-span-10 { - grid-row: span 10 / span 10; -} - -.bui-row-span-11 { - grid-row: span 11 / span 11; -} - -.bui-row-span-12 { - grid-row: span 12 / span 12; -} - -.bui-row-span-auto { - grid-row: span auto / span auto; -} - -@media (width >= 640px) { - .xs\:bui-columns-1 { - grid-template-columns: repeat(1, minmax(0, 1fr)); - } - - .xs\:bui-columns-2 { - grid-template-columns: repeat(2, minmax(0, 1fr)); - } - - .xs\:bui-columns-3 { - grid-template-columns: repeat(3, minmax(0, 1fr)); - } - - .xs\:bui-columns-4 { - grid-template-columns: repeat(4, minmax(0, 1fr)); - } - - .xs\:bui-columns-5 { - grid-template-columns: repeat(5, minmax(0, 1fr)); - } - - .xs\:bui-columns-6 { - grid-template-columns: repeat(6, minmax(0, 1fr)); - } - - .xs\:bui-columns-7 { - grid-template-columns: repeat(7, minmax(0, 1fr)); - } - - .xs\:bui-columns-8 { - grid-template-columns: repeat(8, minmax(0, 1fr)); - } - - .xs\:bui-columns-9 { - grid-template-columns: repeat(9, minmax(0, 1fr)); - } - - .xs\:bui-columns-10 { - grid-template-columns: repeat(10, minmax(0, 1fr)); - } - - .xs\:bui-columns-11 { - grid-template-columns: repeat(11, minmax(0, 1fr)); - } - - .xs\:bui-columns-12 { - grid-template-columns: repeat(12, minmax(0, 1fr)); - } - - .xs\:bui-columns-auto { - grid-template-columns: repeat(auto-fit, minmax(0, 1fr)); - } - - .xs\:bui-col-span-1 { - grid-column: span 1 / span 1; - } - - .xs\:bui-col-span-2 { - grid-column: span 2 / span 2; - } - - .xs\:bui-col-span-3 { - grid-column: span 3 / span 3; - } - - .xs\:bui-col-span-4 { - grid-column: span 4 / span 4; - } - - .xs\:bui-col-span-5 { - grid-column: span 5 / span 5; - } - - .xs\:bui-col-span-6 { - grid-column: span 6 / span 6; - } - - .xs\:bui-col-span-7 { - grid-column: span 7 / span 7; - } - - .xs\:bui-col-span-8 { - grid-column: span 8 / span 8; - } - - .xs\:bui-col-span-9 { - grid-column: span 9 / span 9; - } - - .xs\:bui-col-span-10 { - grid-column: span 10 / span 10; - } - - .xs\:bui-col-span-11 { - grid-column: span 11 / span 11; - } - - .xs\:bui-col-span-12 { - grid-column: span 12 / span 12; - } - - .xs\:bui-col-span-auto { - grid-column: span auto / span auto; - } - - .xs\:bui-col-start-1 { - grid-column-start: 1; - } - - .xs\:bui-col-start-2 { - grid-column-start: 2; - } - - .xs\:bui-col-start-3 { - grid-column-start: 3; - } - - .xs\:bui-col-start-4 { - grid-column-start: 4; - } - - .xs\:bui-col-start-5 { - grid-column-start: 5; - } - - .xs\:bui-col-start-6 { - grid-column-start: 6; - } - - .xs\:bui-col-start-7 { - grid-column-start: 7; - } - - .xs\:bui-col-start-8 { - grid-column-start: 8; - } - - .xs\:bui-col-start-9 { - grid-column-start: 9; - } - - .xs\:bui-col-start-10 { - grid-column-start: 10; - } - - .xs\:bui-col-start-11 { - grid-column-start: 11; - } - - .xs\:bui-col-start-12 { - grid-column-start: 12; - } - - .xs\:bui-col-start-13 { - grid-column-start: 13; - } - - .xs\:bui-col-start-auto { - grid-column-start: auto; - } - - .xs\:bui-col-end-1 { - grid-column-end: 1; - } - - .xs\:bui-col-end-2 { - grid-column-end: 2; - } - - .xs\:bui-col-end-3 { - grid-column-end: 3; - } - - .xs\:bui-col-end-4 { - grid-column-end: 4; - } - - .xs\:bui-col-end-5 { - grid-column-end: 5; - } - - .xs\:bui-col-end-6 { - grid-column-end: 6; - } - - .xs\:bui-col-end-7 { - grid-column-end: 7; - } - - .xs\:bui-col-end-8 { - grid-column-end: 8; - } - - .xs\:bui-col-end-9 { - grid-column-end: 9; - } - - .xs\:bui-col-end-10 { - grid-column-end: 10; - } - - .xs\:bui-col-end-11 { - grid-column-end: 11; - } - - .xs\:bui-col-end-12 { - grid-column-end: 12; - } - - .xs\:bui-col-end-13 { - grid-column-end: 13; - } - - .xs\:bui-col-end-auto { - grid-column-end: auto; - } - - .xs\:bui-row-span-1 { - grid-row: span 1 / span 1; - } - - .xs\:bui-row-span-2 { - grid-row: span 2 / span 2; - } - - .xs\:bui-row-span-3 { - grid-row: span 3 / span 3; - } - - .xs\:bui-row-span-4 { - grid-row: span 4 / span 4; - } - - .xs\:bui-row-span-5 { - grid-row: span 5 / span 5; - } - - .xs\:bui-row-span-6 { - grid-row: span 6 / span 6; - } - - .xs\:bui-row-span-7 { - grid-row: span 7 / span 7; - } - - .xs\:bui-row-span-8 { - grid-row: span 8 / span 8; - } - - .xs\:bui-row-span-9 { - grid-row: span 9 / span 9; - } - - .xs\:bui-row-span-10 { - grid-row: span 10 / span 10; - } - - .xs\:bui-row-span-11 { - grid-row: span 11 / span 11; - } - - .xs\:bui-row-span-12 { - grid-row: span 12 / span 12; - } - - .xs\:bui-row-span-auto { - grid-row: span auto / span auto; - } -} - -@media (width >= 768px) { - .sm\:bui-columns-1 { - grid-template-columns: repeat(1, minmax(0, 1fr)); - } - - .sm\:bui-columns-2 { - grid-template-columns: repeat(2, minmax(0, 1fr)); - } - - .sm\:bui-columns-3 { - grid-template-columns: repeat(3, minmax(0, 1fr)); - } - - .sm\:bui-columns-4 { - grid-template-columns: repeat(4, minmax(0, 1fr)); - } - - .sm\:bui-columns-5 { - grid-template-columns: repeat(5, minmax(0, 1fr)); - } - - .sm\:bui-columns-6 { - grid-template-columns: repeat(6, minmax(0, 1fr)); - } - - .sm\:bui-columns-7 { - grid-template-columns: repeat(7, minmax(0, 1fr)); - } - - .sm\:bui-columns-8 { - grid-template-columns: repeat(8, minmax(0, 1fr)); - } - - .sm\:bui-columns-9 { - grid-template-columns: repeat(9, minmax(0, 1fr)); - } - - .sm\:bui-columns-10 { - grid-template-columns: repeat(10, minmax(0, 1fr)); - } - - .sm\:bui-columns-11 { - grid-template-columns: repeat(11, minmax(0, 1fr)); - } - - .sm\:bui-columns-12 { - grid-template-columns: repeat(12, minmax(0, 1fr)); - } - - .sm\:bui-columns-auto { - grid-template-columns: repeat(auto-fit, minmax(0, 1fr)); - } - - .sm\:bui-col-span-1 { - grid-column: span 1 / span 1; - } - - .sm\:bui-col-span-2 { - grid-column: span 2 / span 2; - } - - .sm\:bui-col-span-3 { - grid-column: span 3 / span 3; - } - - .sm\:bui-col-span-4 { - grid-column: span 4 / span 4; - } - - .sm\:bui-col-span-5 { - grid-column: span 5 / span 5; - } - - .sm\:bui-col-span-6 { - grid-column: span 6 / span 6; - } - - .sm\:bui-col-span-7 { - grid-column: span 7 / span 7; - } - - .sm\:bui-col-span-8 { - grid-column: span 8 / span 8; - } - - .sm\:bui-col-span-9 { - grid-column: span 9 / span 9; - } - - .sm\:bui-col-span-10 { - grid-column: span 10 / span 10; - } - - .sm\:bui-col-span-11 { - grid-column: span 11 / span 11; - } - - .sm\:bui-col-span-12 { - grid-column: span 12 / span 12; - } - - .sm\:bui-col-span-auto { - grid-column: span auto / span auto; - } - - .sm\:bui-col-start-1 { - grid-column-start: 1; - } - - .sm\:bui-col-start-2 { - grid-column-start: 2; - } - - .sm\:bui-col-start-3 { - grid-column-start: 3; - } - - .sm\:bui-col-start-4 { - grid-column-start: 4; - } - - .sm\:bui-col-start-5 { - grid-column-start: 5; - } - - .sm\:bui-col-start-6 { - grid-column-start: 6; - } - - .sm\:bui-col-start-7 { - grid-column-start: 7; - } - - .sm\:bui-col-start-8 { - grid-column-start: 8; - } - - .sm\:bui-col-start-9 { - grid-column-start: 9; - } - - .sm\:bui-col-start-10 { - grid-column-start: 10; - } - - .sm\:bui-col-start-11 { - grid-column-start: 11; - } - - .sm\:bui-col-start-12 { - grid-column-start: 12; - } - - .sm\:bui-col-start-13 { - grid-column-start: 13; - } - - .sm\:bui-col-start-auto { - grid-column-start: auto; - } - - .sm\:bui-col-end-1 { - grid-column-end: 1; - } - - .sm\:bui-col-end-2 { - grid-column-end: 2; - } - - .sm\:bui-col-end-3 { - grid-column-end: 3; - } - - .sm\:bui-col-end-4 { - grid-column-end: 4; - } - - .sm\:bui-col-end-5 { - grid-column-end: 5; - } - - .sm\:bui-col-end-6 { - grid-column-end: 6; - } - - .sm\:bui-col-end-7 { - grid-column-end: 7; - } - - .sm\:bui-col-end-8 { - grid-column-end: 8; - } - - .sm\:bui-col-end-9 { - grid-column-end: 9; - } - - .sm\:bui-col-end-10 { - grid-column-end: 10; - } - - .sm\:bui-col-end-11 { - grid-column-end: 11; - } - - .sm\:bui-col-end-12 { - grid-column-end: 12; - } - - .sm\:bui-col-end-13 { - grid-column-end: 13; - } - - .sm\:bui-col-end-auto { - grid-column-end: auto; - } - - .sm\:bui-row-span-1 { - grid-row: span 1 / span 1; - } - - .sm\:bui-row-span-2 { - grid-row: span 2 / span 2; - } - - .sm\:bui-row-span-3 { - grid-row: span 3 / span 3; - } - - .sm\:bui-row-span-4 { - grid-row: span 4 / span 4; - } - - .sm\:bui-row-span-5 { - grid-row: span 5 / span 5; - } - - .sm\:bui-row-span-6 { - grid-row: span 6 / span 6; - } - - .sm\:bui-row-span-7 { - grid-row: span 7 / span 7; - } - - .sm\:bui-row-span-8 { - grid-row: span 8 / span 8; - } - - .sm\:bui-row-span-9 { - grid-row: span 9 / span 9; - } - - .sm\:bui-row-span-10 { - grid-row: span 10 / span 10; - } - - .sm\:bui-row-span-11 { - grid-row: span 11 / span 11; - } - - .sm\:bui-row-span-12 { - grid-row: span 12 / span 12; - } - - .sm\:bui-row-span-auto { - grid-row: span auto / span auto; - } -} - -@media (width >= 1024px) { - .md\:bui-columns-1 { - grid-template-columns: repeat(1, minmax(0, 1fr)); - } - - .md\:bui-columns-2 { - grid-template-columns: repeat(2, minmax(0, 1fr)); - } - - .md\:bui-columns-3 { - grid-template-columns: repeat(3, minmax(0, 1fr)); - } - - .md\:bui-columns-4 { - grid-template-columns: repeat(4, minmax(0, 1fr)); - } - - .md\:bui-columns-5 { - grid-template-columns: repeat(5, minmax(0, 1fr)); - } - - .md\:bui-columns-6 { - grid-template-columns: repeat(6, minmax(0, 1fr)); - } - - .md\:bui-columns-7 { - grid-template-columns: repeat(7, minmax(0, 1fr)); - } - - .md\:bui-columns-8 { - grid-template-columns: repeat(8, minmax(0, 1fr)); - } - - .md\:bui-columns-9 { - grid-template-columns: repeat(9, minmax(0, 1fr)); - } - - .md\:bui-columns-10 { - grid-template-columns: repeat(10, minmax(0, 1fr)); - } - - .md\:bui-columns-11 { - grid-template-columns: repeat(11, minmax(0, 1fr)); - } - - .md\:bui-columns-12 { - grid-template-columns: repeat(12, minmax(0, 1fr)); - } - - .md\:bui-columns-auto { - grid-template-columns: repeat(auto-fit, minmax(0, 1fr)); - } - - .md\:bui-col-span-1 { - grid-column: span 1 / span 1; - } - - .md\:bui-col-span-2 { - grid-column: span 2 / span 2; - } - - .md\:bui-col-span-3 { - grid-column: span 3 / span 3; - } - - .md\:bui-col-span-4 { - grid-column: span 4 / span 4; - } - - .md\:bui-col-span-5 { - grid-column: span 5 / span 5; - } - - .md\:bui-col-span-6 { - grid-column: span 6 / span 6; - } - - .md\:bui-col-span-7 { - grid-column: span 7 / span 7; - } - - .md\:bui-col-span-8 { - grid-column: span 8 / span 8; - } - - .md\:bui-col-span-9 { - grid-column: span 9 / span 9; - } - - .md\:bui-col-span-10 { - grid-column: span 10 / span 10; - } - - .md\:bui-col-span-11 { - grid-column: span 11 / span 11; - } - - .md\:bui-col-span-12 { - grid-column: span 12 / span 12; - } - - .md\:bui-col-span-auto { - grid-column: span auto / span auto; - } - - .md\:bui-col-start-1 { - grid-column-start: 1; - } - - .md\:bui-col-start-2 { - grid-column-start: 2; - } - - .md\:bui-col-start-3 { - grid-column-start: 3; - } - - .md\:bui-col-start-4 { - grid-column-start: 4; - } - - .md\:bui-col-start-5 { - grid-column-start: 5; - } - - .md\:bui-col-start-6 { - grid-column-start: 6; - } - - .md\:bui-col-start-7 { - grid-column-start: 7; - } - - .md\:bui-col-start-8 { - grid-column-start: 8; - } - - .md\:bui-col-start-9 { - grid-column-start: 9; - } - - .md\:bui-col-start-10 { - grid-column-start: 10; - } - - .md\:bui-col-start-11 { - grid-column-start: 11; - } - - .md\:bui-col-start-12 { - grid-column-start: 12; - } - - .md\:bui-col-start-13 { - grid-column-start: 13; - } - - .md\:bui-col-start-auto { - grid-column-start: auto; - } - - .md\:bui-col-end-1 { - grid-column-end: 1; - } - - .md\:bui-col-end-2 { - grid-column-end: 2; - } - - .md\:bui-col-end-3 { - grid-column-end: 3; - } - - .md\:bui-col-end-4 { - grid-column-end: 4; - } - - .md\:bui-col-end-5 { - grid-column-end: 5; - } - - .md\:bui-col-end-6 { - grid-column-end: 6; - } - - .md\:bui-col-end-7 { - grid-column-end: 7; - } - - .md\:bui-col-end-8 { - grid-column-end: 8; - } - - .md\:bui-col-end-9 { - grid-column-end: 9; - } - - .md\:bui-col-end-10 { - grid-column-end: 10; - } - - .md\:bui-col-end-11 { - grid-column-end: 11; - } - - .md\:bui-col-end-12 { - grid-column-end: 12; - } - - .md\:bui-col-end-13 { - grid-column-end: 13; - } - - .md\:bui-col-end-auto { - grid-column-end: auto; - } - - .md\:bui-row-span-1 { - grid-row: span 1 / span 1; - } - - .md\:bui-row-span-2 { - grid-row: span 2 / span 2; - } - - .md\:bui-row-span-3 { - grid-row: span 3 / span 3; - } - - .md\:bui-row-span-4 { - grid-row: span 4 / span 4; - } - - .md\:bui-row-span-5 { - grid-row: span 5 / span 5; - } - - .md\:bui-row-span-6 { - grid-row: span 6 / span 6; - } - - .md\:bui-row-span-7 { - grid-row: span 7 / span 7; - } - - .md\:bui-row-span-8 { - grid-row: span 8 / span 8; - } - - .md\:bui-row-span-9 { - grid-row: span 9 / span 9; - } - - .md\:bui-row-span-10 { - grid-row: span 10 / span 10; - } - - .md\:bui-row-span-11 { - grid-row: span 11 / span 11; - } - - .md\:bui-row-span-12 { - grid-row: span 12 / span 12; - } - - .md\:bui-row-span-auto { - grid-row: span auto / span auto; - } -} - -@media (width >= 1280px) { - .lg\:bui-columns-1 { - grid-template-columns: repeat(1, minmax(0, 1fr)); - } - - .lg\:bui-columns-2 { - grid-template-columns: repeat(2, minmax(0, 1fr)); - } - - .lg\:bui-columns-3 { - grid-template-columns: repeat(3, minmax(0, 1fr)); - } - - .lg\:bui-columns-4 { - grid-template-columns: repeat(4, minmax(0, 1fr)); - } - - .lg\:bui-columns-5 { - grid-template-columns: repeat(5, minmax(0, 1fr)); - } - - .lg\:bui-columns-6 { - grid-template-columns: repeat(6, minmax(0, 1fr)); - } - - .lg\:bui-columns-7 { - grid-template-columns: repeat(7, minmax(0, 1fr)); - } - - .lg\:bui-columns-8 { - grid-template-columns: repeat(8, minmax(0, 1fr)); - } - - .lg\:bui-columns-9 { - grid-template-columns: repeat(9, minmax(0, 1fr)); - } - - .lg\:bui-columns-10 { - grid-template-columns: repeat(10, minmax(0, 1fr)); - } - - .lg\:bui-columns-11 { - grid-template-columns: repeat(11, minmax(0, 1fr)); - } - - .lg\:bui-columns-12 { - grid-template-columns: repeat(12, minmax(0, 1fr)); - } - - .lg\:bui-columns-auto { - grid-template-columns: repeat(auto-fit, minmax(0, 1fr)); - } - - .lg\:bui-col-span-1 { - grid-column: span 1 / span 1; - } - - .lg\:bui-col-span-2 { - grid-column: span 2 / span 2; - } - - .lg\:bui-col-span-3 { - grid-column: span 3 / span 3; - } - - .lg\:bui-col-span-4 { - grid-column: span 4 / span 4; - } - - .lg\:bui-col-span-5 { - grid-column: span 5 / span 5; - } - - .lg\:bui-col-span-6 { - grid-column: span 6 / span 6; - } - - .lg\:bui-col-span-7 { - grid-column: span 7 / span 7; - } - - .lg\:bui-col-span-8 { - grid-column: span 8 / span 8; - } - - .lg\:bui-col-span-9 { - grid-column: span 9 / span 9; - } - - .lg\:bui-col-span-10 { - grid-column: span 10 / span 10; - } - - .lg\:bui-col-span-11 { - grid-column: span 11 / span 11; - } - - .lg\:bui-col-span-12 { - grid-column: span 12 / span 12; - } - - .lg\:bui-col-span-auto { - grid-column: span auto / span auto; - } - - .lg\:bui-col-start-1 { - grid-column-start: 1; - } - - .lg\:bui-col-start-2 { - grid-column-start: 2; - } - - .lg\:bui-col-start-3 { - grid-column-start: 3; - } - - .lg\:bui-col-start-4 { - grid-column-start: 4; - } - - .lg\:bui-col-start-5 { - grid-column-start: 5; - } - - .lg\:bui-col-start-6 { - grid-column-start: 6; - } - - .lg\:bui-col-start-7 { - grid-column-start: 7; - } - - .lg\:bui-col-start-8 { - grid-column-start: 8; - } - - .lg\:bui-col-start-9 { - grid-column-start: 9; - } - - .lg\:bui-col-start-10 { - grid-column-start: 10; - } - - .lg\:bui-col-start-11 { - grid-column-start: 11; - } - - .lg\:bui-col-start-12 { - grid-column-start: 12; - } - - .lg\:bui-col-start-13 { - grid-column-start: 13; - } - - .lg\:bui-col-start-auto { - grid-column-start: auto; - } - - .lg\:bui-col-end-1 { - grid-column-end: 1; - } - - .lg\:bui-col-end-2 { - grid-column-end: 2; - } - - .lg\:bui-col-end-3 { - grid-column-end: 3; - } - - .lg\:bui-col-end-4 { - grid-column-end: 4; - } - - .lg\:bui-col-end-5 { - grid-column-end: 5; - } - - .lg\:bui-col-end-6 { - grid-column-end: 6; - } - - .lg\:bui-col-end-7 { - grid-column-end: 7; - } - - .lg\:bui-col-end-8 { - grid-column-end: 8; - } - - .lg\:bui-col-end-9 { - grid-column-end: 9; - } - - .lg\:bui-col-end-10 { - grid-column-end: 10; - } - - .lg\:bui-col-end-11 { - grid-column-end: 11; - } - - .lg\:bui-col-end-12 { - grid-column-end: 12; - } - - .lg\:bui-col-end-13 { - grid-column-end: 13; - } - - .lg\:bui-col-end-auto { - grid-column-end: auto; - } - - .lg\:bui-row-span-1 { - grid-row: span 1 / span 1; - } - - .lg\:bui-row-span-2 { - grid-row: span 2 / span 2; - } - - .lg\:bui-row-span-3 { - grid-row: span 3 / span 3; - } - - .lg\:bui-row-span-4 { - grid-row: span 4 / span 4; - } - - .lg\:bui-row-span-5 { - grid-row: span 5 / span 5; - } - - .lg\:bui-row-span-6 { - grid-row: span 6 / span 6; - } - - .lg\:bui-row-span-7 { - grid-row: span 7 / span 7; - } - - .lg\:bui-row-span-8 { - grid-row: span 8 / span 8; - } - - .lg\:bui-row-span-9 { - grid-row: span 9 / span 9; - } - - .lg\:bui-row-span-10 { - grid-row: span 10 / span 10; - } - - .lg\:bui-row-span-11 { - grid-row: span 11 / span 11; - } - - .lg\:bui-row-span-12 { - grid-row: span 12 / span 12; - } - - .lg\:bui-row-span-auto { - grid-row: span auto / span auto; - } -} - -@media (width >= 1536px) { - .xl\:bui-columns-1 { - grid-template-columns: repeat(1, minmax(0, 1fr)); - } - - .xl\:bui-columns-2 { - grid-template-columns: repeat(2, minmax(0, 1fr)); - } - - .xl\:bui-columns-3 { - grid-template-columns: repeat(3, minmax(0, 1fr)); - } - - .xl\:bui-columns-4 { - grid-template-columns: repeat(4, minmax(0, 1fr)); - } - - .xl\:bui-columns-5 { - grid-template-columns: repeat(5, minmax(0, 1fr)); - } - - .xl\:bui-columns-6 { - grid-template-columns: repeat(6, minmax(0, 1fr)); - } - - .xl\:bui-columns-7 { - grid-template-columns: repeat(7, minmax(0, 1fr)); - } - - .xl\:bui-columns-8 { - grid-template-columns: repeat(8, minmax(0, 1fr)); - } - - .xl\:bui-columns-9 { - grid-template-columns: repeat(9, minmax(0, 1fr)); - } - - .xl\:bui-columns-10 { - grid-template-columns: repeat(10, minmax(0, 1fr)); - } - - .xl\:bui-columns-11 { - grid-template-columns: repeat(11, minmax(0, 1fr)); - } - - .xl\:bui-columns-12 { - grid-template-columns: repeat(12, minmax(0, 1fr)); - } - - .xl\:bui-columns-auto { - grid-template-columns: repeat(auto-fit, minmax(0, 1fr)); - } - - .xl\:bui-col-span-1 { - grid-column: span 1 / span 1; - } - - .xl\:bui-col-span-2 { - grid-column: span 2 / span 2; - } - - .xl\:bui-col-span-3 { - grid-column: span 3 / span 3; - } - - .xl\:bui-col-span-4 { - grid-column: span 4 / span 4; - } - - .xl\:bui-col-span-5 { - grid-column: span 5 / span 5; - } - - .xl\:bui-col-span-6 { - grid-column: span 6 / span 6; - } - - .xl\:bui-col-span-7 { - grid-column: span 7 / span 7; - } - - .xl\:bui-col-span-8 { - grid-column: span 8 / span 8; - } - - .xl\:bui-col-span-9 { - grid-column: span 9 / span 9; - } - - .xl\:bui-col-span-10 { - grid-column: span 10 / span 10; - } - - .xl\:bui-col-span-11 { - grid-column: span 11 / span 11; - } - - .xl\:bui-col-span-12 { - grid-column: span 12 / span 12; - } - - .xl\:bui-col-span-auto { - grid-column: span auto / span auto; - } - - .xl\:bui-col-start-1 { - grid-column-start: 1; - } - - .xl\:bui-col-start-2 { - grid-column-start: 2; - } - - .xl\:bui-col-start-3 { - grid-column-start: 3; - } - - .xl\:bui-col-start-4 { - grid-column-start: 4; - } - - .xl\:bui-col-start-5 { - grid-column-start: 5; - } - - .xl\:bui-col-start-6 { - grid-column-start: 6; - } - - .xl\:bui-col-start-7 { - grid-column-start: 7; - } - - .xl\:bui-col-start-8 { - grid-column-start: 8; - } - - .xl\:bui-col-start-9 { - grid-column-start: 9; - } - - .xl\:bui-col-start-10 { - grid-column-start: 10; - } - - .xl\:bui-col-start-11 { - grid-column-start: 11; - } - - .xl\:bui-col-start-12 { - grid-column-start: 12; - } - - .xl\:bui-col-start-13 { - grid-column-start: 13; - } - - .xl\:bui-col-start-auto { - grid-column-start: auto; - } - - .xl\:bui-col-end-1 { - grid-column-end: 1; - } - - .xl\:bui-col-end-2 { - grid-column-end: 2; - } - - .xl\:bui-col-end-3 { - grid-column-end: 3; - } - - .xl\:bui-col-end-4 { - grid-column-end: 4; - } - - .xl\:bui-col-end-5 { - grid-column-end: 5; - } - - .xl\:bui-col-end-6 { - grid-column-end: 6; - } - - .xl\:bui-col-end-7 { - grid-column-end: 7; - } - - .xl\:bui-col-end-8 { - grid-column-end: 8; - } - - .xl\:bui-col-end-9 { - grid-column-end: 9; - } - - .xl\:bui-col-end-10 { - grid-column-end: 10; - } - - .xl\:bui-col-end-11 { - grid-column-end: 11; - } - - .xl\:bui-col-end-12 { - grid-column-end: 12; - } - - .xl\:bui-col-end-13 { - grid-column-end: 13; - } - - .xl\:bui-col-end-auto { - grid-column-end: auto; - } - - .xl\:bui-row-span-1 { - grid-row: span 1 / span 1; - } - - .xl\:bui-row-span-2 { - grid-row: span 2 / span 2; - } - - .xl\:bui-row-span-3 { - grid-row: span 3 / span 3; - } - - .xl\:bui-row-span-4 { - grid-row: span 4 / span 4; - } - - .xl\:bui-row-span-5 { - grid-row: span 5 / span 5; - } - - .xl\:bui-row-span-6 { - grid-row: span 6 / span 6; - } - - .xl\:bui-row-span-7 { - grid-row: span 7 / span 7; - } - - .xl\:bui-row-span-8 { - grid-row: span 8 / span 8; - } - - .xl\:bui-row-span-9 { - grid-row: span 9 / span 9; - } - - .xl\:bui-row-span-10 { - grid-row: span 10 / span 10; - } - - .xl\:bui-row-span-11 { - grid-row: span 11 / span 11; - } - - .xl\:bui-row-span-12 { - grid-row: span 12 / span 12; - } - - .xl\:bui-row-span-auto { - grid-row: span auto / span auto; - } -} - -.bui-gap { - gap: var(--gap); -} - -.bui-gap-0\.5 { - gap: var(--bui-space-0_5); -} - -.bui-gap-1 { - gap: var(--bui-space-1); -} - -.bui-gap-1\.5 { - gap: var(--bui-space-1_5); -} - -.bui-gap-2 { - gap: var(--bui-space-2); -} - -.bui-gap-3 { - gap: var(--bui-space-3); -} - -.bui-gap-4 { - gap: var(--bui-space-4); -} - -.bui-gap-5 { - gap: var(--bui-space-5); -} - -.bui-gap-6 { - gap: var(--bui-space-6); -} - -.bui-gap-7 { - gap: var(--bui-space-7); -} - -.bui-gap-8 { - gap: var(--bui-space-8); -} - -.bui-gap-9 { - gap: var(--bui-space-9); -} - -.bui-gap-10 { - gap: var(--bui-space-10); -} - -.bui-gap-11 { - gap: var(--bui-space-11); -} - -.bui-gap-12 { - gap: var(--bui-space-12); -} - -.bui-gap-13 { - gap: var(--bui-space-13); -} - -.bui-gap-14 { - gap: var(--bui-space-14); -} - -@media (width >= 640px) { - .xs\:bui-gap { - gap: var(--gap-xs); - } - - .xs\:bui-gap-0\.5 { - gap: var(--bui-space-0_5); - } - - .xs\:bui-gap-1 { - gap: var(--bui-space-1); - } - - .xs\:bui-gap-1\.5 { - gap: var(--bui-space-1_5); - } - - .xs\:bui-gap-2 { - gap: var(--bui-space-2); - } - - .xs\:bui-gap-3 { - gap: var(--bui-space-3); - } - - .xs\:bui-gap-4 { - gap: var(--bui-space-4); - } - - .xs\:bui-gap-5 { - gap: var(--bui-space-5); - } - - .xs\:bui-gap-6 { - gap: var(--bui-space-6); - } - - .xs\:bui-gap-7 { - gap: var(--bui-space-7); - } - - .xs\:bui-gap-8 { - gap: var(--bui-space-8); - } - - .xs\:bui-gap-9 { - gap: var(--bui-space-9); - } - - .xs\:bui-gap-10 { - gap: var(--bui-space-10); - } - - .xs\:bui-gap-11 { - gap: var(--bui-space-11); - } - - .xs\:bui-gap-12 { - gap: var(--bui-space-12); - } - - .xs\:bui-gap-13 { - gap: var(--bui-space-13); - } - - .xs\:bui-gap-14 { - gap: var(--bui-space-14); - } -} - -@media (width >= 768px) { - .sm\:bui-gap { - gap: var(--gap-sm); - } - - .sm\:bui-gap-0\.5 { - gap: var(--bui-space-0_5); - } - - .sm\:bui-gap-1 { - gap: var(--bui-space-1); - } - - .sm\:bui-gap-1\.5 { - gap: var(--bui-space-1_5); - } - - .sm\:bui-gap-2 { - gap: var(--bui-space-2); - } - - .sm\:bui-gap-3 { - gap: var(--bui-space-3); - } - - .sm\:bui-gap-4 { - gap: var(--bui-space-4); - } - - .sm\:bui-gap-5 { - gap: var(--bui-space-5); - } - - .sm\:bui-gap-6 { - gap: var(--bui-space-6); - } - - .sm\:bui-gap-7 { - gap: var(--bui-space-7); - } - - .sm\:bui-gap-8 { - gap: var(--bui-space-8); - } - - .sm\:bui-gap-9 { - gap: var(--bui-space-9); - } - - .sm\:bui-gap-10 { - gap: var(--bui-space-10); - } - - .sm\:bui-gap-11 { - gap: var(--bui-space-11); - } - - .sm\:bui-gap-12 { - gap: var(--bui-space-12); - } - - .sm\:bui-gap-13 { - gap: var(--bui-space-13); - } - - .sm\:bui-gap-14 { - gap: var(--bui-space-14); - } -} - -@media (width >= 1024px) { - .md\:bui-gap { - gap: var(--gap-md); - } - - .md\:bui-gap-0\.5 { - gap: var(--bui-space-0_5); - } - - .md\:bui-gap-1 { - gap: var(--bui-space-1); - } - - .md\:bui-gap-1\.5 { - gap: var(--bui-space-1_5); - } - - .md\:bui-gap-2 { - gap: var(--bui-space-2); - } - - .md\:bui-gap-3 { - gap: var(--bui-space-3); - } - - .md\:bui-gap-4 { - gap: var(--bui-space-4); - } - - .md\:bui-gap-5 { - gap: var(--bui-space-5); - } - - .md\:bui-gap-6 { - gap: var(--bui-space-6); - } - - .md\:bui-gap-7 { - gap: var(--bui-space-7); - } - - .md\:bui-gap-8 { - gap: var(--bui-space-8); - } - - .md\:bui-gap-9 { - gap: var(--bui-space-9); - } - - .md\:bui-gap-10 { - gap: var(--bui-space-10); - } - - .md\:bui-gap-11 { - gap: var(--bui-space-11); - } - - .md\:bui-gap-12 { - gap: var(--bui-space-12); - } - - .md\:bui-gap-13 { - gap: var(--bui-space-13); - } - - .md\:bui-gap-14 { - gap: var(--bui-space-14); - } -} - -@media (width >= 1280px) { - .lg\:bui-gap { - gap: var(--gap-lg); - } - - .lg\:bui-gap-0\.5 { - gap: var(--bui-space-0_5); - } - - .lg\:bui-gap-1 { - gap: var(--bui-space-1); - } - - .lg\:bui-gap-1\.5 { - gap: var(--bui-space-1_5); - } - - .lg\:bui-gap-2 { - gap: var(--bui-space-2); - } - - .lg\:bui-gap-3 { - gap: var(--bui-space-3); - } - - .lg\:bui-gap-4 { - gap: var(--bui-space-4); - } - - .lg\:bui-gap-5 { - gap: var(--bui-space-5); - } - - .lg\:bui-gap-6 { - gap: var(--bui-space-6); - } - - .lg\:bui-gap-7 { - gap: var(--bui-space-7); - } - - .lg\:bui-gap-8 { - gap: var(--bui-space-8); - } - - .lg\:bui-gap-9 { - gap: var(--bui-space-9); - } - - .lg\:bui-gap-10 { - gap: var(--bui-space-10); - } - - .lg\:bui-gap-11 { - gap: var(--bui-space-11); - } - - .lg\:bui-gap-12 { - gap: var(--bui-space-12); - } - - .lg\:bui-gap-13 { - gap: var(--bui-space-13); - } - - .lg\:bui-gap-14 { - gap: var(--bui-space-14); - } -} - -@media (width >= 1536px) { - .xl\:bui-gap { - gap: var(--gap-xl); - } - - .xl\:bui-gap-0\.5 { - gap: var(--bui-space-0_5); - } - - .xl\:bui-gap-1 { - gap: var(--bui-space-1); - } - - .xl\:bui-gap-1\.5 { - gap: var(--bui-space-1_5); - } - - .xl\:bui-gap-2 { - gap: var(--bui-space-2); - } - - .xl\:bui-gap-3 { - gap: var(--bui-space-3); - } - - .xl\:bui-gap-4 { - gap: var(--bui-space-4); - } - - .xl\:bui-gap-5 { - gap: var(--bui-space-5); - } - - .xl\:bui-gap-6 { - gap: var(--bui-space-6); - } - - .xl\:bui-gap-7 { - gap: var(--bui-space-7); - } - - .xl\:bui-gap-8 { - gap: var(--bui-space-8); - } - - .xl\:bui-gap-9 { - gap: var(--bui-space-9); - } - - .xl\:bui-gap-10 { - gap: var(--bui-space-10); - } - - .xl\:bui-gap-11 { - gap: var(--bui-space-11); - } - - .xl\:bui-gap-12 { - gap: var(--bui-space-12); - } - - .xl\:bui-gap-13 { - gap: var(--bui-space-13); - } - - .xl\:bui-gap-14 { - gap: var(--bui-space-14); - } -} - -.bui-align-start { - align-items: start; -} - -.bui-align-center { - align-items: center; -} - -.bui-align-end { - align-items: end; -} - -.bui-align-baseline { - align-items: baseline; -} - -.bui-align-stretch { - align-items: stretch; -} - -.bui-jc-start { - justify-content: start; -} - -.bui-jc-center { - justify-content: center; -} - -.bui-jc-end { - justify-content: end; -} - -.bui-jc-between { - justify-content: space-between; -} - -.bui-fd-row { - flex-direction: row; -} - -.bui-fd-column { - flex-direction: column; -} - -.bui-fd-row-reverse { - flex-direction: row-reverse; -} - -.bui-fd-column-reverse { - flex-direction: column-reverse; -} - -@media (width >= 640px) { - .xs\:bui-align-start { - align-items: start; - } - - .xs\:bui-align-center { - align-items: center; - } - - .xs\:bui-align-end { - align-items: end; - } - - .xs\:bui-align-stretch { - align-items: stretch; - } - - .xs\:bui-jc-start { - justify-content: start; - } - - .xs\:bui-jc-center { - justify-content: center; - } - - .xs\:bui-jc-end { - justify-content: end; - } - - .xs\:bui-jc-between { - justify-content: space-between; - } - - .xs\:bui-fd-row { - flex-direction: row; - } - - .xs\:bui-fd-column { - flex-direction: column; - } - - .xs\:bui-fd-row-reverse { - flex-direction: row-reverse; - } - - .xs\:bui-fd-column-reverse { - flex-direction: column-reverse; - } -} - -@media (width >= 768px) { - .sm\:bui-align-start { - align-items: start; - } - - .sm\:bui-align-center { - align-items: center; - } - - .sm\:bui-align-end { - align-items: end; - } - - .sm\:bui-align-stretch { - align-items: stretch; - } - - .sm\:bui-jc-start { - justify-content: start; - } - - .sm\:bui-jc-center { - justify-content: center; - } - - .sm\:bui-jc-end { - justify-content: end; - } - - .sm\:bui-jc-between { - justify-content: space-between; - } - - .sm\:bui-fd-row { - flex-direction: row; - } - - .sm\:bui-fd-column { - flex-direction: column; - } - - .sm\:bui-fd-row-reverse { - flex-direction: row-reverse; - } - - .sm\:bui-fd-column-reverse { - flex-direction: column-reverse; + :where(a) { + color: inherit; + text-decoration: none; } -} - -@media (width >= 1024px) { - .md\:bui-align-start { - align-items: start; - } - - .md\:bui-align-center { - align-items: center; - } - - .md\:bui-align-end { - align-items: end; - } - - .md\:bui-align-stretch { - align-items: stretch; - } - - .md\:bui-jc-start { - justify-content: start; - } - - .md\:bui-jc-center { - justify-content: center; - } - - .md\:bui-jc-end { - justify-content: end; - } - - .md\:bui-jc-between { - justify-content: space-between; - } - - .md\:bui-fd-row { - flex-direction: row; - } - - .md\:bui-fd-column { - flex-direction: column; - } - - .md\:bui-fd-row-reverse { - flex-direction: row-reverse; - } - - .md\:bui-fd-column-reverse { - flex-direction: column-reverse; - } -} - -@media (width >= 1280px) { - .lg\:bui-align-start { - align-items: start; - } - - .lg\:bui-align-center { - align-items: center; - } - - .lg\:bui-align-end { - align-items: end; - } - - .lg\:bui-align-stretch { - align-items: stretch; - } - - .lg\:bui-jc-start { - justify-content: start; - } - - .lg\:bui-jc-center { - justify-content: center; - } - - .lg\:bui-jc-end { - justify-content: end; - } - - .lg\:bui-jc-between { - justify-content: space-between; - } - - .lg\:bui-fd-row { - flex-direction: row; - } - - .lg\:bui-fd-column { - flex-direction: column; - } - - .lg\:bui-fd-row-reverse { - flex-direction: row-reverse; - } - - .lg\:bui-fd-column-reverse { - flex-direction: column-reverse; - } -} - -@media (width >= 1536px) { - .xl\:bui-align-start { - align-items: start; - } - - .xl\:bui-align-center { - align-items: center; - } - - .xl\:bui-align-end { - align-items: end; - } - - .xl\:bui-align-stretch { - align-items: stretch; - } - - .xl\:bui-jc-start { - justify-content: start; - } - - .xl\:bui-jc-center { - justify-content: center; - } - - .xl\:bui-jc-end { - justify-content: end; - } - - .xl\:bui-jc-between { - justify-content: space-between; - } - - .xl\:bui-fd-row { - flex-direction: row; - } - - .xl\:bui-fd-column { - flex-direction: column; - } - - .xl\:bui-fd-row-reverse { - flex-direction: row-reverse; - } - - .xl\:bui-fd-column-reverse { - flex-direction: column-reverse; - } -} - -:where(a) { - color: inherit; - text-decoration: none; -} - -@keyframes pulse { - 50% { - opacity: .5; - } -} - -:root { - --bui-font-regular: system-ui; - --bui-font-monospace: ui-monospace, "Menlo", "Monaco", "Consolas", "Liberation Mono", "Courier New", monospace; - --bui-font-weight-regular: 400; - --bui-font-weight-bold: 600; - --bui-font-size-1: .625rem; - --bui-font-size-2: .75rem; - --bui-font-size-3: .875rem; - --bui-font-size-4: 1rem; - --bui-font-size-5: 1.25rem; - --bui-font-size-6: 1.5rem; - --bui-font-size-7: 2rem; - --bui-font-size-8: 3rem; - --bui-font-size-9: 4rem; - --bui-font-size-10: 5.75rem; - --bui-space: .25rem; - --bui-space-0_5: calc(var(--bui-space) * .5); - --bui-space-1: var(--bui-space); - --bui-space-1_5: calc(var(--bui-space) * 1.5); - --bui-space-2: calc(var(--bui-space) * 2); - --bui-space-3: calc(var(--bui-space) * 3); - --bui-space-4: calc(var(--bui-space) * 4); - --bui-space-5: calc(var(--bui-space) * 5); - --bui-space-6: calc(var(--bui-space) * 6); - --bui-space-7: calc(var(--bui-space) * 7); - --bui-space-8: calc(var(--bui-space) * 8); - --bui-space-9: calc(var(--bui-space) * 9); - --bui-space-10: calc(var(--bui-space) * 10); - --bui-space-11: calc(var(--bui-space) * 11); - --bui-space-12: calc(var(--bui-space) * 12); - --bui-space-13: calc(var(--bui-space) * 13); - --bui-space-14: calc(var(--bui-space) * 14); - --bui-radius-1: calc(.125rem); - --bui-radius-2: calc(.25rem); - --bui-radius-3: calc(.5rem); - --bui-radius-4: calc(.75rem); - --bui-radius-5: calc(1rem); - --bui-radius-6: calc(1.25rem); - --bui-radius-full: 9999px; - --bui-black: #000; - --bui-white: #fff; - --bui-gray-1: #f8f8f8; - --bui-gray-2: #ececec; - --bui-gray-3: #d9d9d9; - --bui-gray-4: #c1c1c1; - --bui-gray-5: #9e9e9e; - --bui-gray-6: #8c8c8c; - --bui-gray-7: #757575; - --bui-gray-8: #595959; - --bui-bg: var(--bui-gray-1); - --bui-bg-surface-1: var(--bui-white); - --bui-bg-surface-2: var(--bui-gray-2); - --bui-bg-solid: #1f5493; - --bui-bg-solid-hover: #163a66; - --bui-bg-solid-pressed: #0f2b4e; - --bui-bg-solid-disabled: #ebebeb; - --bui-bg-tint: transparent; - --bui-bg-tint-hover: #1f549366; - --bui-bg-tint-pressed: #1f549399; - --bui-bg-tint-disabled: #ebebeb; - --bui-bg-danger: #feebe7; - --bui-bg-warning: #fff2b2; - --bui-bg-success: #e6f6eb; - --bui-fg-primary: var(--bui-black); - --bui-fg-secondary: var(--bui-gray-7); - --bui-fg-link: #1f5493; - --bui-fg-link-hover: #1f2d5c; - --bui-fg-disabled: #9e9e9e; - --bui-fg-solid: var(--bui-white); - --bui-fg-solid-disabled: #9c9c9c; - --bui-fg-tint: #1f5493; - --bui-fg-tint-disabled: var(--bui-gray-5); - --bui-fg-danger: #e22b2b; - --bui-fg-warning: #e36d05; - --bui-fg-success: #1db954; - --bui-border: #0000001a; - --bui-border-hover: #0003; - --bui-border-pressed: #0006; - --bui-border-disabled: #0000001a; - --bui-border-danger: #f87a7a; - --bui-border-warning: #e36d05; - --bui-border-success: #53db83; - --bui-ring: #1f5493; - --bui-scrollbar: #a0a0a03b; - --bui-scrollbar-thumb: #a0a0a0; - --bui-animate-pulse: pulse 2s cubic-bezier(.4, 0, .6, 1) infinite; -} - -[data-theme-mode="dark"] { - --bui-gray-1: #191919; - --bui-gray-2: #242424; - --bui-gray-3: #373737; - --bui-gray-4: #464646; - --bui-gray-5: #575757; - --bui-gray-6: #7b7b7b; - --bui-gray-7: #9e9e9e; - --bui-gray-8: #b4b4b4; - --bui-bg: #333; - --bui-bg-surface-1: #424242; - --bui-bg-surface-2: var(--bui-gray-2); - --bui-bg-solid: #9cc9ff; - --bui-bg-solid-hover: #83b9fd; - --bui-bg-solid-pressed: #83b9fd; - --bui-bg-solid-disabled: #222; - --bui-bg-tint: transparent; - --bui-bg-tint-hover: #9cc9ff1f; - --bui-bg-tint-pressed: #9cc9ff29; - --bui-bg-tint-disabled: transparent; - --bui-bg-danger: #3b1219; - --bui-bg-warning: #302008; - --bui-bg-success: #132d21; - --bui-fg-primary: var(--bui-white); - --bui-fg-secondary: var(--bui-gray-7); - --bui-fg-link: #9cc9ff; - --bui-fg-link-hover: #7eb5f7; - --bui-fg-disabled: var(--bui-gray-7); - --bui-fg-solid: #101821; - --bui-fg-solid-disabled: var(--bui-gray-5); - --bui-fg-tint: #9cc9ff; - --bui-fg-tint-disabled: var(--bui-gray-5); - --bui-fg-danger: #e22b2b; - --bui-fg-warning: #e36d05; - --bui-fg-success: #1db954; - --bui-border: #ffffff1f; - --bui-border-hover: #fff6; - --bui-border-pressed: #ffffff80; - --bui-border-disabled: #fff3; - --bui-border-danger: #f87a7a; - --bui-border-warning: #e36d05; - --bui-border-success: #53db83; - --bui-ring: #1f5493; - --bui-scrollbar: #3636363a; - --bui-scrollbar-thumb: #575757; -} - -.bui-AvatarRoot { - vertical-align: middle; - user-select: none; - color: var(--bui-fg-primary); - background-color: var(--bui-bg-surface-2); - border-radius: 100%; - justify-content: center; - align-items: center; - width: 2rem; - height: 2rem; - font-size: 1rem; - font-weight: 500; - line-height: 1; - display: inline-flex; - overflow: hidden; -} - -.bui-AvatarRoot[data-size="small"] { - width: 1.5rem; - height: 1.5rem; -} - -.bui-AvatarRoot[data-size="medium"] { - width: 2rem; - height: 2rem; -} - -.bui-AvatarRoot[data-size="large"] { - width: 3rem; - height: 3rem; -} - -.bui-AvatarImage { - object-fit: cover; - width: 100%; - height: 100%; -} - -.bui-AvatarFallback { - width: 100%; - height: 100%; - font-size: var(--bui-font-size-3); - font-weight: var(--bui-font-weight-regular); - box-shadow: inset 0 0 0 1px var(--bui-border); - border-radius: var(--bui-radius-full); - justify-content: center; - align-items: center; - display: flex; -} - -.bui-Box { - font-family: var(--bui-font-regular); - font-weight: var(--bui-font-weight-regular); - color: var(--bui-fg-primary); -} - -.bui-Button { - user-select: none; - font-family: var(--bui-font-regular); - font-weight: var(--bui-font-weight-bold); - cursor: pointer; - border-radius: var(--bui-radius-2); - justify-content: center; - align-items: center; - gap: var(--bui-space-1_5); - border: none; - flex-shrink: 0; - padding: 0; - display: inline-flex; - - &[data-disabled="true"] { - cursor: not-allowed; - } -} - -.bui-Button[data-variant="primary"] { - background-color: var(--bui-bg-solid); - color: var(--bui-fg-solid); - - &:hover { - background-color: var(--bui-bg-solid-hover); - transition: background-color .15s; - } - - &:active { - background-color: var(--bui-bg-solid-pressed); - } - - &:focus-visible { - outline: 2px solid var(--bui-ring); - outline-offset: 2px; - } - - &[data-disabled="true"] { - background-color: var(--bui-bg-solid-disabled); - color: var(--bui-fg-solid-disabled); - } -} - -.bui-Button[data-variant="secondary"] { - background-color: var(--bui-bg-surface-1); - box-shadow: inset 0 0 0 1px var(--bui-border); - color: var(--bui-fg-primary); - - &:hover { - box-shadow: inset 0 0 0 1px var(--bui-border-hover); - transition: box-shadow .15s; - } - - &:active { - box-shadow: inset 0 0 0 1px var(--bui-border-pressed); - } - - &:focus-visible { - box-shadow: inset 0 0 0 2px var(--bui-ring); - outline: none; - transition: none; - } - - &[data-disabled="true"] { - box-shadow: inset 0 0 0 1px var(--bui-border-disabled); - color: var(--bui-fg-disabled); - } -} - -.bui-Button[data-variant="tertiary"] { - color: var(--bui-fg-primary); - background-color: #0000; - - &:hover { - background-color: var(--bui-bg-surface-1); - transition: background-color .2s; - } - - &:active { - background-color: var(--bui-bg-surface-2); - } - - &:focus-visible { - box-shadow: inset 0 0 0 2px var(--bui-ring); - outline: none; - transition: none; - } - - &[data-disabled="true"] { - color: var(--bui-fg-disabled); - background-color: #0000; - } -} - -.bui-Button[data-size="medium"] { - font-size: var(--bui-font-size-4); - padding: 0 var(--bui-space-3); - height: 2.5rem; -} - -.bui-Button[data-size="small"] { - font-size: var(--bui-font-size-3); - padding: 0 var(--bui-space-2); - height: 2rem; -} - -.bui-Button[data-size="small"] svg { - width: 1rem; - height: 1rem; -} - -.bui-Button[data-size="medium"] svg { - width: 1.25rem; - height: 1.25rem; -} - -.bui-ButtonIcon { - justify-content: center; - align-items: center; -} - -.bui-ButtonIcon[data-size="small"] { - width: 2rem; - padding: 0; -} - -.bui-ButtonIcon[data-size="medium"] { - width: 2.5rem; - padding: 0; -} - -.bui-Card { - gap: var(--bui-space-3); - background-color: var(--bui-bg-surface-1); - border-radius: var(--bui-radius-3); - padding-block: var(--bui-space-3); - color: var(--bui-fg-primary); - border: 1px solid var(--bui-border); - flex-direction: column; - width: 100%; - min-height: 0; - display: flex; - overflow: hidden; -} - -.bui-CardBody { - flex: 1; - min-height: 0; - overflow: auto; -} - -.bui-CardHeader, .bui-CardFooter { - padding-inline: var(--bui-space-3); -} - -.bui-CheckboxRoot { - width: 1rem; - height: 1rem; - box-shadow: inset 0 0 0 1px var(--bui-border); - cursor: pointer; - background-color: var(--bui-bg-surface-1); - border: none; - border-radius: 2px; - flex-shrink: 0; - justify-content: center; - align-items: center; - padding: 0; - transition: background-color .2s ease-in-out; - display: flex; -} - -.bui-CheckboxRoot:focus-visible { - outline: 2px solid var(--bui-ring); - outline-offset: 2px; - transition: none; -} - -.bui-CheckboxRoot[data-checked] { - background-color: var(--bui-bg-solid); - box-shadow: none; - color: var(--bui-fg-solid); -} - -.bui-CheckboxLabel { - align-items: center; - gap: var(--bui-space-2); - font-size: var(--bui-font-size-3); - font-family: var(--bui-font-regular); - font-weight: var(--bui-font-weight-regular); - color: var(--bui-fg-primary); - user-select: none; - flex-direction: row; - display: flex; - &:hover { - & .bui-CheckboxRoot:not([data-checked]) { - box-shadow: inset 0 0 0 1px var(--bui-border-hover); + @keyframes pulse { + 50% { + opacity: .5; } } -} -.bui-CheckboxIndicator { - color: var(--bui-fg-solid); - justify-content: center; - align-items: center; - display: flex; -} - -.bui-CollapsiblePanel { - height: var(--collapsible-panel-height); - transition: all .15s ease-out; - display: flex; - overflow: hidden; - - &[data-starting-style], &[data-ending-style] { - height: 0; - } -} - -.bui-Container { - max-width: 120rem; - padding-inline: var(--bui-space-4); - margin-inline: auto; - transition: padding .2s ease-in-out; -} - -@media (width >= 640px) { - .bui-Container { - padding-inline: var(--bui-space-5); - } -} - -.bui-FieldError { - color: var(--bui-fg-danger); - font-size: var(--bui-font-size-2); - font-weight: var(--bui-font-weight-regular); - margin-top: var(--bui-space-2); - display: inline-block; -} - -.bui-FieldLabelWrapper { - margin-bottom: var(--bui-space-3); - gap: var(--bui-space-1); - flex-direction: column; - display: flex; -} - -.bui-FieldLabel { - color: var(--bui-fg-primary); - cursor: pointer; - font-weight: var(--bui-font-weight-regular); - font-size: var(--bui-font-size-2); - margin-right: auto; -} - -.bui-FieldSecondaryLabel { - color: var(--bui-fg-secondary); - font-weight: var(--bui-font-weight-regular); - margin-left: var(--bui-space-1); -} - -.bui-FieldDescription { - font-weight: var(--bui-font-weight-regular); - font-size: var(--bui-font-size-2); - color: var(--bui-fg-secondary); - margin: 0; -} - -.bui-Flex { - min-width: 0; - display: flex; -} - -.bui-Grid { - display: grid; -} - -.bui-HeaderToolbar { - margin-bottom: var(--bui-space-6); - - &:before { - content: ""; + body { background-color: var(--bui-bg); - z-index: 0; - height: 16px; - position: absolute; - top: 0; - left: 0; - right: 0; - } - - &[data-has-tabs="true"] { - margin-bottom: 0; - } -} - -.bui-HeaderToolbarWrapper { - z-index: 1; - background-color: var(--bui-bg-surface-1); - padding-inline: var(--bui-space-5); - border-bottom: 1px solid var(--bui-border); - color: var(--bui-fg-primary); - flex-direction: row; - justify-content: space-between; - align-items: center; - height: 52px; - display: flex; - position: relative; -} - -.bui-HeaderToolbarContent { - align-items: center; - gap: var(--bui-space-2); - flex-direction: row; - display: flex; -} - -.bui-HeaderToolbarName { - align-items: center; - gap: var(--bui-space-2); - font-size: var(--bui-font-size-3); - font-weight: var(--bui-font-weight-regular); - flex-direction: row; - flex-shrink: 0; - display: flex; -} - -.bui-HeaderToolbarIcon { - width: 16px; - height: 16px; - color: var(--bui-fg-primary); - - & svg { - width: 100%; - height: 100%; - } -} - -.bui-HeaderToolbarControls { - right: var(--bui-space-5); - align-items: center; - gap: var(--bui-space-2); - flex-direction: row; - display: flex; - position: absolute; - top: 50%; - transform: translateY(-50%); -} - -.bui-HeaderTabsWrapper { - margin-bottom: var(--bui-space-4); - padding-inline: var(--bui-space-3); - border-bottom: 1px solid var(--bui-border); - background-color: var(--bui-bg-surface-1); -} - -.bui-HeaderPage { - gap: var(--bui-space-1); - margin-top: var(--bui-space-6); - margin-bottom: var(--bui-space-6); - flex-direction: column; - display: flex; -} - -.bui-HeaderPageContent { - flex-direction: row; - justify-content: space-between; - display: flex; -} - -.bui-HeaderPageTabsWrapper { - margin-left: -8px; -} - -.bui-HeaderPageControls, .bui-HeaderPageBreadcrumbs { - align-items: center; - gap: var(--bui-space-2); - flex-direction: row; - display: flex; -} - -.bui-Icon { - width: 1rem; - height: 1rem; -} - -.bui-Link { - font-family: var(--bui-font-regular); - cursor: pointer; - margin: 0; - padding: 0; - text-decoration-line: none; - display: inline-block; - - &:hover { - text-underline-offset: calc(.025em + 2px); - text-decoration-line: underline; - text-decoration-style: solid; - text-decoration-thickness: min(2px, max(1px, .05em)); - text-decoration-color: color-mix(in srgb, currentColor 30%, transparent); - } -} - -.bui-MenuPopover { - border: 1px solid var(--bui-border); - border-radius: var(--bui-radius-2); - background: var(--bui-bg-surface-1); - color: var(--bui-fg-primary); - outline: none; - flex-direction: column; - min-height: 0; - transition: transform .2s, opacity .2s; - display: flex; - overflow: hidden; - - &[data-entering], &[data-exiting] { - transform: var(--origin); - opacity: 0; - } - - &[data-placement="top"] { - --origin: translateY(8px); - } - - &[data-placement="bottom"] { - --origin: translateY(-8px); - } - - &[data-placement="right"] { - --origin: translateX(-8px); - } - - &[data-placement="left"] { - --origin: translateX(8px); - } -} - -.bui-MenuContent { - max-height: inherit; - box-sizing: border-box; - padding: var(--bui-space-1); - outline: none; - min-width: 150px; - overflow: auto; -} - -.bui-MenuPopover .bui-ScrollAreaRoot { - flex-direction: column; - flex: 1; - height: 100%; - min-height: 0; - display: flex; -} - -.bui-MenuPopover .bui-ScrollAreaScrollbar { - margin-inline: var(--bui-space-1_5); -} - -.bui-MenuItem { - height: 2rem; - padding-inline: var(--bui-space-2); - border-radius: var(--bui-radius-2); - cursor: default; - color: var(--bui-fg-primary); - font-size: var(--bui-font-size-3); - justify-content: space-between; - align-items: center; - gap: var(--bui-space-6); - outline: none; - display: flex; - - &[data-focused], &[data-open] { - background: var(--bui-bg-surface-2); color: var(--bui-fg-primary); + font-family: var(--bui-font-regular); + font-weight: var(--bui-font-weight-regular); + font-size: var(--bui-font-size-3); + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; } - &[data-color="danger"] { - color: var(--bui-fg-danger); + [data-theme-mode="dark"] { + color-scheme: dark; } - &[data-color="danger"][data-focused] { - background: var(--bui-bg-danger); - color: var(--bui-fg-danger); + [data-theme-mode="light"] { + color-scheme: light; + } +} + +@layer components; + +@layer utilities { + .bui-p { + padding: var(--p); } - &[data-has-submenu] { - & > .bui-MenuItemArrow { + .bui-p-0\.5 { + padding: var(--bui-space-0_5); + } + + .bui-p-1 { + padding: var(--bui-space-1); + } + + .bui-p-1\.5 { + padding: var(--bui-space-1_5); + } + + .bui-p-2 { + padding: var(--bui-space-2); + } + + .bui-p-3 { + padding: var(--bui-space-3); + } + + .bui-p-4 { + padding: var(--bui-space-4); + } + + .bui-p-5 { + padding: var(--bui-space-5); + } + + .bui-p-6 { + padding: var(--bui-space-6); + } + + .bui-p-7 { + padding: var(--bui-space-7); + } + + .bui-p-8 { + padding: var(--bui-space-8); + } + + .bui-p-9 { + padding: var(--bui-space-9); + } + + .bui-p-10 { + padding: var(--bui-space-10); + } + + .bui-p-11 { + padding: var(--bui-space-11); + } + + .bui-p-12 { + padding: var(--bui-space-12); + } + + .bui-p-13 { + padding: var(--bui-space-13); + } + + .bui-p-14 { + padding: var(--bui-space-14); + } + + @media (width >= 640px) { + .xs\:bui-p { + padding: var(--p-xs); + } + + .xs\:bui-p-0\.5 { + padding: var(--bui-space-0_5); + } + + .xs\:bui-p-1 { + padding: var(--bui-space-1); + } + + .xs\:bui-p-1\.5 { + padding: var(--bui-space-1_5); + } + + .xs\:bui-p-2 { + padding: var(--bui-space-2); + } + + .xs\:bui-p-3 { + padding: var(--bui-space-3); + } + + .xs\:bui-p-4 { + padding: var(--bui-space-4); + } + + .xs\:bui-p-5 { + padding: var(--bui-space-5); + } + + .xs\:bui-p-6 { + padding: var(--bui-space-6); + } + + .xs\:bui-p-7 { + padding: var(--bui-space-7); + } + + .xs\:bui-p-8 { + padding: var(--bui-space-8); + } + + .xs\:bui-p-9 { + padding: var(--bui-space-9); + } + + .xs\:bui-p-10 { + padding: var(--bui-space-10); + } + + .xs\:bui-p-11 { + padding: var(--bui-space-11); + } + + .xs\:bui-p-12 { + padding: var(--bui-space-12); + } + + .xs\:bui-p-13 { + padding: var(--bui-space-13); + } + + .xs\:bui-p-14 { + padding: var(--bui-space-14); + } + } + + @media (width >= 768px) { + .sm\:bui-p { + padding: var(--p-sm); + } + + .sm\:bui-p-0\.5 { + padding: var(--bui-space-0_5); + } + + .sm\:bui-p-1 { + padding: var(--bui-space-1); + } + + .sm\:bui-p-1\.5 { + padding: var(--bui-space-1_5); + } + + .sm\:bui-p-2 { + padding: var(--bui-space-2); + } + + .sm\:bui-p-3 { + padding: var(--bui-space-3); + } + + .sm\:bui-p-4 { + padding: var(--bui-space-4); + } + + .sm\:bui-p-5 { + padding: var(--bui-space-5); + } + + .sm\:bui-p-6 { + padding: var(--bui-space-6); + } + + .sm\:bui-p-7 { + padding: var(--bui-space-7); + } + + .sm\:bui-p-8 { + padding: var(--bui-space-8); + } + + .sm\:bui-p-9 { + padding: var(--bui-space-9); + } + + .sm\:bui-p-10 { + padding: var(--bui-space-10); + } + + .sm\:bui-p-11 { + padding: var(--bui-space-11); + } + + .sm\:bui-p-12 { + padding: var(--bui-space-12); + } + + .sm\:bui-p-13 { + padding: var(--bui-space-13); + } + + .sm\:bui-p-14 { + padding: var(--bui-space-14); + } + } + + @media (width >= 1024px) { + .md\:bui-p { + padding: var(--p-md); + } + + .md\:bui-p-0\.5 { + padding: var(--bui-space-0_5); + } + + .md\:bui-p-1 { + padding: var(--bui-space-1); + } + + .md\:bui-p-1\.5 { + padding: var(--bui-space-1_5); + } + + .md\:bui-p-2 { + padding: var(--bui-space-2); + } + + .md\:bui-p-3 { + padding: var(--bui-space-3); + } + + .md\:bui-p-4 { + padding: var(--bui-space-4); + } + + .md\:bui-p-5 { + padding: var(--bui-space-5); + } + + .md\:bui-p-6 { + padding: var(--bui-space-6); + } + + .md\:bui-p-7 { + padding: var(--bui-space-7); + } + + .md\:bui-p-8 { + padding: var(--bui-space-8); + } + + .md\:bui-p-9 { + padding: var(--bui-space-9); + } + + .md\:bui-p-10 { + padding: var(--bui-space-10); + } + + .md\:bui-p-11 { + padding: var(--bui-space-11); + } + + .md\:bui-p-12 { + padding: var(--bui-space-12); + } + + .md\:bui-p-13 { + padding: var(--bui-space-13); + } + + .md\:bui-p-14 { + padding: var(--bui-space-14); + } + } + + @media (width >= 1280px) { + .lg\:bui-p { + padding: var(--p-lg); + } + + .lg\:bui-p-0\.5 { + padding: var(--bui-space-0_5); + } + + .lg\:bui-p-1 { + padding: var(--bui-space-1); + } + + .lg\:bui-p-1\.5 { + padding: var(--bui-space-1_5); + } + + .lg\:bui-p-2 { + padding: var(--bui-space-2); + } + + .lg\:bui-p-3 { + padding: var(--bui-space-3); + } + + .lg\:bui-p-4 { + padding: var(--bui-space-4); + } + + .lg\:bui-p-5 { + padding: var(--bui-space-5); + } + + .lg\:bui-p-6 { + padding: var(--bui-space-6); + } + + .lg\:bui-p-7 { + padding: var(--bui-space-7); + } + + .lg\:bui-p-8 { + padding: var(--bui-space-8); + } + + .lg\:bui-p-9 { + padding: var(--bui-space-9); + } + + .lg\:bui-p-10 { + padding: var(--bui-space-10); + } + + .lg\:bui-p-11 { + padding: var(--bui-space-11); + } + + .lg\:bui-p-12 { + padding: var(--bui-space-12); + } + + .lg\:bui-p-13 { + padding: var(--bui-space-13); + } + + .lg\:bui-p-14 { + padding: var(--bui-space-14); + } + } + + @media (width >= 1536px) { + .xl\:bui-p { + padding: var(--p-xl); + } + + .xl\:bui-p-0\.5 { + padding: var(--bui-space-0_5); + } + + .xl\:bui-p-1 { + padding: var(--bui-space-1); + } + + .xl\:bui-p-1\.5 { + padding: var(--bui-space-1_5); + } + + .xl\:bui-p-2 { + padding: var(--bui-space-2); + } + + .xl\:bui-p-3 { + padding: var(--bui-space-3); + } + + .xl\:bui-p-4 { + padding: var(--bui-space-4); + } + + .xl\:bui-p-5 { + padding: var(--bui-space-5); + } + + .xl\:bui-p-6 { + padding: var(--bui-space-6); + } + + .xl\:bui-p-7 { + padding: var(--bui-space-7); + } + + .xl\:bui-p-8 { + padding: var(--bui-space-8); + } + + .xl\:bui-p-9 { + padding: var(--bui-space-9); + } + + .xl\:bui-p-10 { + padding: var(--bui-space-10); + } + + .xl\:bui-p-11 { + padding: var(--bui-space-11); + } + + .xl\:bui-p-12 { + padding: var(--bui-space-12); + } + + .xl\:bui-p-13 { + padding: var(--bui-space-13); + } + + .xl\:bui-p-14 { + padding: var(--bui-space-14); + } + } + + .bui-pl { + padding-left: var(--pl); + } + + .bui-pl-0\.5 { + padding-left: var(--bui-space-0_5); + } + + .bui-pl-1 { + padding-left: var(--bui-space-1); + } + + .bui-pl-1\.5 { + padding-left: var(--bui-space-1_5); + } + + .bui-pl-2 { + padding-left: var(--bui-space-2); + } + + .bui-pl-3 { + padding-left: var(--bui-space-3); + } + + .bui-pl-4 { + padding-left: var(--bui-space-4); + } + + .bui-pl-5 { + padding-left: var(--bui-space-5); + } + + .bui-pl-6 { + padding-left: var(--bui-space-6); + } + + .bui-pl-7 { + padding-left: var(--bui-space-7); + } + + .bui-pl-8 { + padding-left: var(--bui-space-8); + } + + .bui-pl-9 { + padding-left: var(--bui-space-9); + } + + .bui-pl-10 { + padding-left: var(--bui-space-10); + } + + .bui-pl-11 { + padding-left: var(--bui-space-11); + } + + .bui-pl-12 { + padding-left: var(--bui-space-12); + } + + .bui-pl-13 { + padding-left: var(--bui-space-13); + } + + .bui-pl-14 { + padding-left: var(--bui-space-14); + } + + @media (width >= 640px) { + .xs\:bui-pl { + padding-left: var(--pl-xs); + } + + .xs\:bui-pl-0\.5 { + padding-left: var(--bui-space-0_5); + } + + .xs\:bui-pl-1 { + padding-left: var(--bui-space-1); + } + + .xs\:bui-pl-1\.5 { + padding-left: var(--bui-space-1_5); + } + + .xs\:bui-pl-2 { + padding-left: var(--bui-space-2); + } + + .xs\:bui-pl-3 { + padding-left: var(--bui-space-3); + } + + .xs\:bui-pl-4 { + padding-left: var(--bui-space-4); + } + + .xs\:bui-pl-5 { + padding-left: var(--bui-space-5); + } + + .xs\:bui-pl-6 { + padding-left: var(--bui-space-6); + } + + .xs\:bui-pl-7 { + padding-left: var(--bui-space-7); + } + + .xs\:bui-pl-8 { + padding-left: var(--bui-space-8); + } + + .xs\:bui-pl-9 { + padding-left: var(--bui-space-9); + } + + .xs\:bui-pl-10 { + padding-left: var(--bui-space-10); + } + + .xs\:bui-pl-11 { + padding-left: var(--bui-space-11); + } + + .xs\:bui-pl-12 { + padding-left: var(--bui-space-12); + } + + .xs\:bui-pl-13 { + padding-left: var(--bui-space-13); + } + + .xs\:bui-pl-14 { + padding-left: var(--bui-space-14); + } + } + + @media (width >= 768px) { + .sm\:bui-pl { + padding-left: var(--pl-sm); + } + + .sm\:bui-pl-0\.5 { + padding-left: var(--bui-space-0_5); + } + + .sm\:bui-pl-1 { + padding-left: var(--bui-space-1); + } + + .sm\:bui-pl-1\.5 { + padding-left: var(--bui-space-1_5); + } + + .sm\:bui-pl-2 { + padding-left: var(--bui-space-2); + } + + .sm\:bui-pl-3 { + padding-left: var(--bui-space-3); + } + + .sm\:bui-pl-4 { + padding-left: var(--bui-space-4); + } + + .sm\:bui-pl-5 { + padding-left: var(--bui-space-5); + } + + .sm\:bui-pl-6 { + padding-left: var(--bui-space-6); + } + + .sm\:bui-pl-7 { + padding-left: var(--bui-space-7); + } + + .sm\:bui-pl-8 { + padding-left: var(--bui-space-8); + } + + .sm\:bui-pl-9 { + padding-left: var(--bui-space-9); + } + + .sm\:bui-pl-10 { + padding-left: var(--bui-space-10); + } + + .sm\:bui-pl-11 { + padding-left: var(--bui-space-11); + } + + .sm\:bui-pl-12 { + padding-left: var(--bui-space-12); + } + + .sm\:bui-pl-13 { + padding-left: var(--bui-space-13); + } + + .sm\:bui-pl-14 { + padding-left: var(--bui-space-14); + } + } + + @media (width >= 1024px) { + .md\:bui-pl { + padding-left: var(--pl-md); + } + + .md\:bui-pl-0\.5 { + padding-left: var(--bui-space-0_5); + } + + .md\:bui-pl-1 { + padding-left: var(--bui-space-1); + } + + .md\:bui-pl-1\.5 { + padding-left: var(--bui-space-1_5); + } + + .md\:bui-pl-2 { + padding-left: var(--bui-space-2); + } + + .md\:bui-pl-3 { + padding-left: var(--bui-space-3); + } + + .md\:bui-pl-4 { + padding-left: var(--bui-space-4); + } + + .md\:bui-pl-5 { + padding-left: var(--bui-space-5); + } + + .md\:bui-pl-6 { + padding-left: var(--bui-space-6); + } + + .md\:bui-pl-7 { + padding-left: var(--bui-space-7); + } + + .md\:bui-pl-8 { + padding-left: var(--bui-space-8); + } + + .md\:bui-pl-9 { + padding-left: var(--bui-space-9); + } + + .md\:bui-pl-10 { + padding-left: var(--bui-space-10); + } + + .md\:bui-pl-11 { + padding-left: var(--bui-space-11); + } + + .md\:bui-pl-12 { + padding-left: var(--bui-space-12); + } + + .md\:bui-pl-13 { + padding-left: var(--bui-space-13); + } + + .md\:bui-pl-14 { + padding-left: var(--bui-space-14); + } + } + + @media (width >= 1280px) { + .lg\:bui-pl { + padding-left: var(--pl-lg); + } + + .lg\:bui-pl-0\.5 { + padding-left: var(--bui-space-0_5); + } + + .lg\:bui-pl-1 { + padding-left: var(--bui-space-1); + } + + .lg\:bui-pl-1\.5 { + padding-left: var(--bui-space-1_5); + } + + .lg\:bui-pl-2 { + padding-left: var(--bui-space-2); + } + + .lg\:bui-pl-3 { + padding-left: var(--bui-space-3); + } + + .lg\:bui-pl-4 { + padding-left: var(--bui-space-4); + } + + .lg\:bui-pl-5 { + padding-left: var(--bui-space-5); + } + + .lg\:bui-pl-6 { + padding-left: var(--bui-space-6); + } + + .lg\:bui-pl-7 { + padding-left: var(--bui-space-7); + } + + .lg\:bui-pl-8 { + padding-left: var(--bui-space-8); + } + + .lg\:bui-pl-9 { + padding-left: var(--bui-space-9); + } + + .lg\:bui-pl-10 { + padding-left: var(--bui-space-10); + } + + .lg\:bui-pl-11 { + padding-left: var(--bui-space-11); + } + + .lg\:bui-pl-12 { + padding-left: var(--bui-space-12); + } + + .lg\:bui-pl-13 { + padding-left: var(--bui-space-13); + } + + .lg\:bui-pl-14 { + padding-left: var(--bui-space-14); + } + } + + @media (width >= 1536px) { + .xl\:bui-pl { + padding-left: var(--pl-xl); + } + + .xl\:bui-pl-0\.5 { + padding-left: var(--bui-space-0_5); + } + + .xl\:bui-pl-1 { + padding-left: var(--bui-space-1); + } + + .xl\:bui-pl-1\.5 { + padding-left: var(--bui-space-1_5); + } + + .xl\:bui-pl-2 { + padding-left: var(--bui-space-2); + } + + .xl\:bui-pl-3 { + padding-left: var(--bui-space-3); + } + + .xl\:bui-pl-4 { + padding-left: var(--bui-space-4); + } + + .xl\:bui-pl-5 { + padding-left: var(--bui-space-5); + } + + .xl\:bui-pl-6 { + padding-left: var(--bui-space-6); + } + + .xl\:bui-pl-7 { + padding-left: var(--bui-space-7); + } + + .xl\:bui-pl-8 { + padding-left: var(--bui-space-8); + } + + .xl\:bui-pl-9 { + padding-left: var(--bui-space-9); + } + + .xl\:bui-pl-10 { + padding-left: var(--bui-space-10); + } + + .xl\:bui-pl-11 { + padding-left: var(--bui-space-11); + } + + .xl\:bui-pl-12 { + padding-left: var(--bui-space-12); + } + + .xl\:bui-pl-13 { + padding-left: var(--bui-space-13); + } + + .xl\:bui-pl-14 { + padding-left: var(--bui-space-14); + } + } + + .bui-pr { + padding-right: var(--pr); + } + + .bui-pr-0\.5 { + padding-right: var(--bui-space-0_5); + } + + .bui-pr-1 { + padding-right: var(--bui-space-1); + } + + .bui-pr-1\.5 { + padding-right: var(--bui-space-1_5); + } + + .bui-pr-2 { + padding-right: var(--bui-space-2); + } + + .bui-pr-3 { + padding-right: var(--bui-space-3); + } + + .bui-pr-4 { + padding-right: var(--bui-space-4); + } + + .bui-pr-5 { + padding-right: var(--bui-space-5); + } + + .bui-pr-6 { + padding-right: var(--bui-space-6); + } + + .bui-pr-7 { + padding-right: var(--bui-space-7); + } + + .bui-pr-8 { + padding-right: var(--bui-space-8); + } + + .bui-pr-9 { + padding-right: var(--bui-space-9); + } + + .bui-pr-10 { + padding-right: var(--bui-space-10); + } + + .bui-pr-11 { + padding-right: var(--bui-space-11); + } + + .bui-pr-12 { + padding-right: var(--bui-space-12); + } + + .bui-pr-13 { + padding-right: var(--bui-space-13); + } + + .bui-pr-14 { + padding-right: var(--bui-space-14); + } + + @media (width >= 640px) { + .xs\:bui-pr { + padding-right: var(--pr-xs); + } + + .xs\:bui-pr-0\.5 { + padding-right: var(--bui-space-0_5); + } + + .xs\:bui-pr-1 { + padding-right: var(--bui-space-1); + } + + .xs\:bui-pr-1\.5 { + padding-right: var(--bui-space-1_5); + } + + .xs\:bui-pr-2 { + padding-right: var(--bui-space-2); + } + + .xs\:bui-pr-3 { + padding-right: var(--bui-space-3); + } + + .xs\:bui-pr-4 { + padding-right: var(--bui-space-4); + } + + .xs\:bui-pr-5 { + padding-right: var(--bui-space-5); + } + + .xs\:bui-pr-6 { + padding-right: var(--bui-space-6); + } + + .xs\:bui-pr-7 { + padding-right: var(--bui-space-7); + } + + .xs\:bui-pr-8 { + padding-right: var(--bui-space-8); + } + + .xs\:bui-pr-9 { + padding-right: var(--bui-space-9); + } + + .xs\:bui-pr-10 { + padding-right: var(--bui-space-10); + } + + .xs\:bui-pr-11 { + padding-right: var(--bui-space-11); + } + + .xs\:bui-pr-12 { + padding-right: var(--bui-space-12); + } + + .xs\:bui-pr-13 { + padding-right: var(--bui-space-13); + } + + .xs\:bui-pr-14 { + padding-right: var(--bui-space-14); + } + } + + @media (width >= 768px) { + .sm\:bui-pr { + padding-right: var(--pr-sm); + } + + .sm\:bui-pr-0\.5 { + padding-right: var(--bui-space-0_5); + } + + .sm\:bui-pr-1 { + padding-right: var(--bui-space-1); + } + + .sm\:bui-pr-1\.5 { + padding-right: var(--bui-space-1_5); + } + + .sm\:bui-pr-2 { + padding-right: var(--bui-space-2); + } + + .sm\:bui-pr-3 { + padding-right: var(--bui-space-3); + } + + .sm\:bui-pr-4 { + padding-right: var(--bui-space-4); + } + + .sm\:bui-pr-5 { + padding-right: var(--bui-space-5); + } + + .sm\:bui-pr-6 { + padding-right: var(--bui-space-6); + } + + .sm\:bui-pr-7 { + padding-right: var(--bui-space-7); + } + + .sm\:bui-pr-8 { + padding-right: var(--bui-space-8); + } + + .sm\:bui-pr-9 { + padding-right: var(--bui-space-9); + } + + .sm\:bui-pr-10 { + padding-right: var(--bui-space-10); + } + + .sm\:bui-pr-11 { + padding-right: var(--bui-space-11); + } + + .sm\:bui-pr-12 { + padding-right: var(--bui-space-12); + } + + .sm\:bui-pr-13 { + padding-right: var(--bui-space-13); + } + + .sm\:bui-pr-14 { + padding-right: var(--bui-space-14); + } + } + + @media (width >= 1024px) { + .md\:bui-pr { + padding-right: var(--pr-md); + } + + .md\:bui-pr-0\.5 { + padding-right: var(--bui-space-0_5); + } + + .md\:bui-pr-1 { + padding-right: var(--bui-space-1); + } + + .md\:bui-pr-1\.5 { + padding-right: var(--bui-space-1_5); + } + + .md\:bui-pr-2 { + padding-right: var(--bui-space-2); + } + + .md\:bui-pr-3 { + padding-right: var(--bui-space-3); + } + + .md\:bui-pr-4 { + padding-right: var(--bui-space-4); + } + + .md\:bui-pr-5 { + padding-right: var(--bui-space-5); + } + + .md\:bui-pr-6 { + padding-right: var(--bui-space-6); + } + + .md\:bui-pr-7 { + padding-right: var(--bui-space-7); + } + + .md\:bui-pr-8 { + padding-right: var(--bui-space-8); + } + + .md\:bui-pr-9 { + padding-right: var(--bui-space-9); + } + + .md\:bui-pr-10 { + padding-right: var(--bui-space-10); + } + + .md\:bui-pr-11 { + padding-right: var(--bui-space-11); + } + + .md\:bui-pr-12 { + padding-right: var(--bui-space-12); + } + + .md\:bui-pr-13 { + padding-right: var(--bui-space-13); + } + + .md\:bui-pr-14 { + padding-right: var(--bui-space-14); + } + } + + @media (width >= 1280px) { + .lg\:bui-pr { + padding-right: var(--pr-lg); + } + + .lg\:bui-pr-0\.5 { + padding-right: var(--bui-space-0_5); + } + + .lg\:bui-pr-1 { + padding-right: var(--bui-space-1); + } + + .lg\:bui-pr-1\.5 { + padding-right: var(--bui-space-1_5); + } + + .lg\:bui-pr-2 { + padding-right: var(--bui-space-2); + } + + .lg\:bui-pr-3 { + padding-right: var(--bui-space-3); + } + + .lg\:bui-pr-4 { + padding-right: var(--bui-space-4); + } + + .lg\:bui-pr-5 { + padding-right: var(--bui-space-5); + } + + .lg\:bui-pr-6 { + padding-right: var(--bui-space-6); + } + + .lg\:bui-pr-7 { + padding-right: var(--bui-space-7); + } + + .lg\:bui-pr-8 { + padding-right: var(--bui-space-8); + } + + .lg\:bui-pr-9 { + padding-right: var(--bui-space-9); + } + + .lg\:bui-pr-10 { + padding-right: var(--bui-space-10); + } + + .lg\:bui-pr-11 { + padding-right: var(--bui-space-11); + } + + .lg\:bui-pr-12 { + padding-right: var(--bui-space-12); + } + + .lg\:bui-pr-13 { + padding-right: var(--bui-space-13); + } + + .lg\:bui-pr-14 { + padding-right: var(--bui-space-14); + } + } + + @media (width >= 1536px) { + .xl\:bui-pr { + padding-right: var(--pr-xl); + } + + .xl\:bui-pr-0\.5 { + padding-right: var(--bui-space-0_5); + } + + .xl\:bui-pr-1 { + padding-right: var(--bui-space-1); + } + + .xl\:bui-pr-1\.5 { + padding-right: var(--bui-space-1_5); + } + + .xl\:bui-pr-2 { + padding-right: var(--bui-space-2); + } + + .xl\:bui-pr-3 { + padding-right: var(--bui-space-3); + } + + .xl\:bui-pr-4 { + padding-right: var(--bui-space-4); + } + + .xl\:bui-pr-5 { + padding-right: var(--bui-space-5); + } + + .xl\:bui-pr-6 { + padding-right: var(--bui-space-6); + } + + .xl\:bui-pr-7 { + padding-right: var(--bui-space-7); + } + + .xl\:bui-pr-8 { + padding-right: var(--bui-space-8); + } + + .xl\:bui-pr-9 { + padding-right: var(--bui-space-9); + } + + .xl\:bui-pr-10 { + padding-right: var(--bui-space-10); + } + + .xl\:bui-pr-11 { + padding-right: var(--bui-space-11); + } + + .xl\:bui-pr-12 { + padding-right: var(--bui-space-12); + } + + .xl\:bui-pr-13 { + padding-right: var(--bui-space-13); + } + + .xl\:bui-pr-14 { + padding-right: var(--bui-space-14); + } + } + + .bui-pt { + padding-top: var(--pt); + } + + .bui-pt-0\.5 { + padding-top: var(--bui-space-0_5); + } + + .bui-pt-1 { + padding-top: var(--bui-space-1); + } + + .bui-pt-1\.5 { + padding-top: var(--bui-space-1_5); + } + + .bui-pt-2 { + padding-top: var(--bui-space-2); + } + + .bui-pt-3 { + padding-top: var(--bui-space-3); + } + + .bui-pt-4 { + padding-top: var(--bui-space-4); + } + + .bui-pt-5 { + padding-top: var(--bui-space-5); + } + + .bui-pt-6 { + padding-top: var(--bui-space-6); + } + + .bui-pt-7 { + padding-top: var(--bui-space-7); + } + + .bui-pt-8 { + padding-top: var(--bui-space-8); + } + + .bui-pt-9 { + padding-top: var(--bui-space-9); + } + + .bui-pt-10 { + padding-top: var(--bui-space-10); + } + + .bui-pt-11 { + padding-top: var(--bui-space-11); + } + + .bui-pt-12 { + padding-top: var(--bui-space-12); + } + + .bui-pt-13 { + padding-top: var(--bui-space-13); + } + + .bui-pt-14 { + padding-top: var(--bui-space-14); + } + + @media (width >= 640px) { + .xs\:bui-pt { + padding-top: var(--pt-xs); + } + + .xs\:bui-pt-0\.5 { + padding-top: var(--bui-space-0_5); + } + + .xs\:bui-pt-1 { + padding-top: var(--bui-space-1); + } + + .xs\:bui-pt-1\.5 { + padding-top: var(--bui-space-1_5); + } + + .xs\:bui-pt-2 { + padding-top: var(--bui-space-2); + } + + .xs\:bui-pt-3 { + padding-top: var(--bui-space-3); + } + + .xs\:bui-pt-4 { + padding-top: var(--bui-space-4); + } + + .xs\:bui-pt-5 { + padding-top: var(--bui-space-5); + } + + .xs\:bui-pt-6 { + padding-top: var(--bui-space-6); + } + + .xs\:bui-pt-7 { + padding-top: var(--bui-space-7); + } + + .xs\:bui-pt-8 { + padding-top: var(--bui-space-8); + } + + .xs\:bui-pt-9 { + padding-top: var(--bui-space-9); + } + + .xs\:bui-pt-10 { + padding-top: var(--bui-space-10); + } + + .xs\:bui-pt-11 { + padding-top: var(--bui-space-11); + } + + .xs\:bui-pt-12 { + padding-top: var(--bui-space-12); + } + + .xs\:bui-pt-13 { + padding-top: var(--bui-space-13); + } + + .xs\:bui-pt-14 { + padding-top: var(--bui-space-14); + } + } + + @media (width >= 768px) { + .sm\:bui-pt { + padding-top: var(--pt-sm); + } + + .sm\:bui-pt-0\.5 { + padding-top: var(--bui-space-0_5); + } + + .sm\:bui-pt-1 { + padding-top: var(--bui-space-1); + } + + .sm\:bui-pt-1\.5 { + padding-top: var(--bui-space-1_5); + } + + .sm\:bui-pt-2 { + padding-top: var(--bui-space-2); + } + + .sm\:bui-pt-3 { + padding-top: var(--bui-space-3); + } + + .sm\:bui-pt-4 { + padding-top: var(--bui-space-4); + } + + .sm\:bui-pt-5 { + padding-top: var(--bui-space-5); + } + + .sm\:bui-pt-6 { + padding-top: var(--bui-space-6); + } + + .sm\:bui-pt-7 { + padding-top: var(--bui-space-7); + } + + .sm\:bui-pt-8 { + padding-top: var(--bui-space-8); + } + + .sm\:bui-pt-9 { + padding-top: var(--bui-space-9); + } + + .sm\:bui-pt-10 { + padding-top: var(--bui-space-10); + } + + .sm\:bui-pt-11 { + padding-top: var(--bui-space-11); + } + + .sm\:bui-pt-12 { + padding-top: var(--bui-space-12); + } + + .sm\:bui-pt-13 { + padding-top: var(--bui-space-13); + } + + .sm\:bui-pt-14 { + padding-top: var(--bui-space-14); + } + } + + @media (width >= 1024px) { + .md\:bui-pt { + padding-top: var(--pt-md); + } + + .md\:bui-pt-0\.5 { + padding-top: var(--bui-space-0_5); + } + + .md\:bui-pt-1 { + padding-top: var(--bui-space-1); + } + + .md\:bui-pt-1\.5 { + padding-top: var(--bui-space-1_5); + } + + .md\:bui-pt-2 { + padding-top: var(--bui-space-2); + } + + .md\:bui-pt-3 { + padding-top: var(--bui-space-3); + } + + .md\:bui-pt-4 { + padding-top: var(--bui-space-4); + } + + .md\:bui-pt-5 { + padding-top: var(--bui-space-5); + } + + .md\:bui-pt-6 { + padding-top: var(--bui-space-6); + } + + .md\:bui-pt-7 { + padding-top: var(--bui-space-7); + } + + .md\:bui-pt-8 { + padding-top: var(--bui-space-8); + } + + .md\:bui-pt-9 { + padding-top: var(--bui-space-9); + } + + .md\:bui-pt-10 { + padding-top: var(--bui-space-10); + } + + .md\:bui-pt-11 { + padding-top: var(--bui-space-11); + } + + .md\:bui-pt-12 { + padding-top: var(--bui-space-12); + } + + .md\:bui-pt-13 { + padding-top: var(--bui-space-13); + } + + .md\:bui-pt-14 { + padding-top: var(--bui-space-14); + } + } + + @media (width >= 1280px) { + .lg\:bui-pt { + padding-top: var(--pt-lg); + } + + .lg\:bui-pt-0\.5 { + padding-top: var(--bui-space-0_5); + } + + .lg\:bui-pt-1 { + padding-top: var(--bui-space-1); + } + + .lg\:bui-pt-1\.5 { + padding-top: var(--bui-space-1_5); + } + + .lg\:bui-pt-2 { + padding-top: var(--bui-space-2); + } + + .lg\:bui-pt-3 { + padding-top: var(--bui-space-3); + } + + .lg\:bui-pt-4 { + padding-top: var(--bui-space-4); + } + + .lg\:bui-pt-5 { + padding-top: var(--bui-space-5); + } + + .lg\:bui-pt-6 { + padding-top: var(--bui-space-6); + } + + .lg\:bui-pt-7 { + padding-top: var(--bui-space-7); + } + + .lg\:bui-pt-8 { + padding-top: var(--bui-space-8); + } + + .lg\:bui-pt-9 { + padding-top: var(--bui-space-9); + } + + .lg\:bui-pt-10 { + padding-top: var(--bui-space-10); + } + + .lg\:bui-pt-11 { + padding-top: var(--bui-space-11); + } + + .lg\:bui-pt-12 { + padding-top: var(--bui-space-12); + } + + .lg\:bui-pt-13 { + padding-top: var(--bui-space-13); + } + + .lg\:bui-pt-14 { + padding-top: var(--bui-space-14); + } + } + + @media (width >= 1536px) { + .xl\:bui-pt { + padding-top: var(--pt-xl); + } + + .xl\:bui-pt-0\.5 { + padding-top: var(--bui-space-0_5); + } + + .xl\:bui-pt-1 { + padding-top: var(--bui-space-1); + } + + .xl\:bui-pt-1\.5 { + padding-top: var(--bui-space-1_5); + } + + .xl\:bui-pt-2 { + padding-top: var(--bui-space-2); + } + + .xl\:bui-pt-3 { + padding-top: var(--bui-space-3); + } + + .xl\:bui-pt-4 { + padding-top: var(--bui-space-4); + } + + .xl\:bui-pt-5 { + padding-top: var(--bui-space-5); + } + + .xl\:bui-pt-6 { + padding-top: var(--bui-space-6); + } + + .xl\:bui-pt-7 { + padding-top: var(--bui-space-7); + } + + .xl\:bui-pt-8 { + padding-top: var(--bui-space-8); + } + + .xl\:bui-pt-9 { + padding-top: var(--bui-space-9); + } + + .xl\:bui-pt-10 { + padding-top: var(--bui-space-10); + } + + .xl\:bui-pt-11 { + padding-top: var(--bui-space-11); + } + + .xl\:bui-pt-12 { + padding-top: var(--bui-space-12); + } + + .xl\:bui-pt-13 { + padding-top: var(--bui-space-13); + } + + .xl\:bui-pt-14 { + padding-top: var(--bui-space-14); + } + } + + .bui-pb { + padding-bottom: var(--pb); + } + + .bui-pb-0\.5 { + padding-bottom: var(--bui-space-0_5); + } + + .bui-pb-1 { + padding-bottom: var(--bui-space-1); + } + + .bui-pb-1\.5 { + padding-bottom: var(--bui-space-1_5); + } + + .bui-pb-2 { + padding-bottom: var(--bui-space-2); + } + + .bui-pb-3 { + padding-bottom: var(--bui-space-3); + } + + .bui-pb-4 { + padding-bottom: var(--bui-space-4); + } + + .bui-pb-5 { + padding-bottom: var(--bui-space-5); + } + + .bui-pb-6 { + padding-bottom: var(--bui-space-6); + } + + .bui-pb-7 { + padding-bottom: var(--bui-space-7); + } + + .bui-pb-8 { + padding-bottom: var(--bui-space-8); + } + + .bui-pb-9 { + padding-bottom: var(--bui-space-9); + } + + .bui-pb-10 { + padding-bottom: var(--bui-space-10); + } + + .bui-pb-11 { + padding-bottom: var(--bui-space-11); + } + + .bui-pb-12 { + padding-bottom: var(--bui-space-12); + } + + .bui-pb-13 { + padding-bottom: var(--bui-space-13); + } + + .bui-pb-14 { + padding-bottom: var(--bui-space-14); + } + + @media (width >= 640px) { + .xs\:bui-pb { + padding-bottom: var(--pb-xs); + } + + .xs\:bui-pb-0\.5 { + padding-bottom: var(--bui-space-0_5); + } + + .xs\:bui-pb-1 { + padding-bottom: var(--bui-space-1); + } + + .xs\:bui-pb-1\.5 { + padding-bottom: var(--bui-space-1_5); + } + + .xs\:bui-pb-2 { + padding-bottom: var(--bui-space-2); + } + + .xs\:bui-pb-3 { + padding-bottom: var(--bui-space-3); + } + + .xs\:bui-pb-4 { + padding-bottom: var(--bui-space-4); + } + + .xs\:bui-pb-5 { + padding-bottom: var(--bui-space-5); + } + + .xs\:bui-pb-6 { + padding-bottom: var(--bui-space-6); + } + + .xs\:bui-pb-7 { + padding-bottom: var(--bui-space-7); + } + + .xs\:bui-pb-8 { + padding-bottom: var(--bui-space-8); + } + + .xs\:bui-pb-9 { + padding-bottom: var(--bui-space-9); + } + + .xs\:bui-pb-10 { + padding-bottom: var(--bui-space-10); + } + + .xs\:bui-pb-11 { + padding-bottom: var(--bui-space-11); + } + + .xs\:bui-pb-12 { + padding-bottom: var(--bui-space-12); + } + + .xs\:bui-pb-13 { + padding-bottom: var(--bui-space-13); + } + + .xs\:bui-pb-14 { + padding-bottom: var(--bui-space-14); + } + } + + @media (width >= 768px) { + .sm\:bui-pb { + padding-bottom: var(--pb-sm); + } + + .sm\:bui-pb-0\.5 { + padding-bottom: var(--bui-space-0_5); + } + + .sm\:bui-pb-1 { + padding-bottom: var(--bui-space-1); + } + + .sm\:bui-pb-1\.5 { + padding-bottom: var(--bui-space-1_5); + } + + .sm\:bui-pb-2 { + padding-bottom: var(--bui-space-2); + } + + .sm\:bui-pb-3 { + padding-bottom: var(--bui-space-3); + } + + .sm\:bui-pb-4 { + padding-bottom: var(--bui-space-4); + } + + .sm\:bui-pb-5 { + padding-bottom: var(--bui-space-5); + } + + .sm\:bui-pb-6 { + padding-bottom: var(--bui-space-6); + } + + .sm\:bui-pb-7 { + padding-bottom: var(--bui-space-7); + } + + .sm\:bui-pb-8 { + padding-bottom: var(--bui-space-8); + } + + .sm\:bui-pb-9 { + padding-bottom: var(--bui-space-9); + } + + .sm\:bui-pb-10 { + padding-bottom: var(--bui-space-10); + } + + .sm\:bui-pb-11 { + padding-bottom: var(--bui-space-11); + } + + .sm\:bui-pb-12 { + padding-bottom: var(--bui-space-12); + } + + .sm\:bui-pb-13 { + padding-bottom: var(--bui-space-13); + } + + .sm\:bui-pb-14 { + padding-bottom: var(--bui-space-14); + } + } + + @media (width >= 1024px) { + .md\:bui-pb { + padding-bottom: var(--pb-md); + } + + .md\:bui-pb-0\.5 { + padding-bottom: var(--bui-space-0_5); + } + + .md\:bui-pb-1 { + padding-bottom: var(--bui-space-1); + } + + .md\:bui-pb-1\.5 { + padding-bottom: var(--bui-space-1_5); + } + + .md\:bui-pb-2 { + padding-bottom: var(--bui-space-2); + } + + .md\:bui-pb-3 { + padding-bottom: var(--bui-space-3); + } + + .md\:bui-pb-4 { + padding-bottom: var(--bui-space-4); + } + + .md\:bui-pb-5 { + padding-bottom: var(--bui-space-5); + } + + .md\:bui-pb-6 { + padding-bottom: var(--bui-space-6); + } + + .md\:bui-pb-7 { + padding-bottom: var(--bui-space-7); + } + + .md\:bui-pb-8 { + padding-bottom: var(--bui-space-8); + } + + .md\:bui-pb-9 { + padding-bottom: var(--bui-space-9); + } + + .md\:bui-pb-10 { + padding-bottom: var(--bui-space-10); + } + + .md\:bui-pb-11 { + padding-bottom: var(--bui-space-11); + } + + .md\:bui-pb-12 { + padding-bottom: var(--bui-space-12); + } + + .md\:bui-pb-13 { + padding-bottom: var(--bui-space-13); + } + + .md\:bui-pb-14 { + padding-bottom: var(--bui-space-14); + } + } + + @media (width >= 1280px) { + .lg\:bui-pb { + padding-bottom: var(--pb-lg); + } + + .lg\:bui-pb-0\.5 { + padding-bottom: var(--bui-space-0_5); + } + + .lg\:bui-pb-1 { + padding-bottom: var(--bui-space-1); + } + + .lg\:bui-pb-1\.5 { + padding-bottom: var(--bui-space-1_5); + } + + .lg\:bui-pb-2 { + padding-bottom: var(--bui-space-2); + } + + .lg\:bui-pb-3 { + padding-bottom: var(--bui-space-3); + } + + .lg\:bui-pb-4 { + padding-bottom: var(--bui-space-4); + } + + .lg\:bui-pb-5 { + padding-bottom: var(--bui-space-5); + } + + .lg\:bui-pb-6 { + padding-bottom: var(--bui-space-6); + } + + .lg\:bui-pb-7 { + padding-bottom: var(--bui-space-7); + } + + .lg\:bui-pb-8 { + padding-bottom: var(--bui-space-8); + } + + .lg\:bui-pb-9 { + padding-bottom: var(--bui-space-9); + } + + .lg\:bui-pb-10 { + padding-bottom: var(--bui-space-10); + } + + .lg\:bui-pb-11 { + padding-bottom: var(--bui-space-11); + } + + .lg\:bui-pb-12 { + padding-bottom: var(--bui-space-12); + } + + .lg\:bui-pb-13 { + padding-bottom: var(--bui-space-13); + } + + .lg\:bui-pb-14 { + padding-bottom: var(--bui-space-14); + } + } + + @media (width >= 1536px) { + .xl\:bui-pb { + padding-bottom: var(--pb-xl); + } + + .xl\:bui-pb-0\.5 { + padding-bottom: var(--bui-space-0_5); + } + + .xl\:bui-pb-1 { + padding-bottom: var(--bui-space-1); + } + + .xl\:bui-pb-1\.5 { + padding-bottom: var(--bui-space-1_5); + } + + .xl\:bui-pb-2 { + padding-bottom: var(--bui-space-2); + } + + .xl\:bui-pb-3 { + padding-bottom: var(--bui-space-3); + } + + .xl\:bui-pb-4 { + padding-bottom: var(--bui-space-4); + } + + .xl\:bui-pb-5 { + padding-bottom: var(--bui-space-5); + } + + .xl\:bui-pb-6 { + padding-bottom: var(--bui-space-6); + } + + .xl\:bui-pb-7 { + padding-bottom: var(--bui-space-7); + } + + .xl\:bui-pb-8 { + padding-bottom: var(--bui-space-8); + } + + .xl\:bui-pb-9 { + padding-bottom: var(--bui-space-9); + } + + .xl\:bui-pb-10 { + padding-bottom: var(--bui-space-10); + } + + .xl\:bui-pb-11 { + padding-bottom: var(--bui-space-11); + } + + .xl\:bui-pb-12 { + padding-bottom: var(--bui-space-12); + } + + .xl\:bui-pb-13 { + padding-bottom: var(--bui-space-13); + } + + .xl\:bui-pb-14 { + padding-bottom: var(--bui-space-14); + } + } + + .bui-py { + padding-top: var(--py); + padding-bottom: var(--py); + } + + .bui-py-0\.5 { + padding-top: var(--bui-space-0_5); + padding-bottom: var(--bui-space-0_5); + } + + .bui-py-1 { + padding-top: var(--bui-space-1); + padding-bottom: var(--bui-space-1); + } + + .bui-py-1\.5 { + padding-top: var(--bui-space-1_5); + padding-bottom: var(--bui-space-1_5); + } + + .bui-py-2 { + padding-top: var(--bui-space-2); + padding-bottom: var(--bui-space-2); + } + + .bui-py-3 { + padding-top: var(--bui-space-3); + padding-bottom: var(--bui-space-3); + } + + .bui-py-4 { + padding-top: var(--bui-space-4); + padding-bottom: var(--bui-space-4); + } + + .bui-py-5 { + padding-top: var(--bui-space-5); + padding-bottom: var(--bui-space-5); + } + + .bui-py-6 { + padding-top: var(--bui-space-6); + padding-bottom: var(--bui-space-6); + } + + .bui-py-7 { + padding-top: var(--bui-space-7); + padding-bottom: var(--bui-space-7); + } + + .bui-py-8 { + padding-top: var(--bui-space-8); + padding-bottom: var(--bui-space-8); + } + + .bui-py-9 { + padding-top: var(--bui-space-9); + padding-bottom: var(--bui-space-9); + } + + .bui-py-10 { + padding-top: var(--bui-space-10); + padding-bottom: var(--bui-space-10); + } + + .bui-py-11 { + padding-top: var(--bui-space-11); + padding-bottom: var(--bui-space-11); + } + + .bui-py-12 { + padding-top: var(--bui-space-12); + padding-bottom: var(--bui-space-12); + } + + .bui-py-13 { + padding-top: var(--bui-space-13); + padding-bottom: var(--bui-space-13); + } + + .bui-py-14 { + padding-top: var(--bui-space-14); + padding-bottom: var(--bui-space-14); + } + + @media (width >= 640px) { + .xs\:bui-py { + padding-top: var(--py-xs); + padding-bottom: var(--py-xs); + } + + .xs\:bui-py-0\.5 { + padding-top: var(--bui-space-0_5); + padding-bottom: var(--bui-space-0_5); + } + + .xs\:bui-py-1 { + padding-top: var(--bui-space-1); + padding-bottom: var(--bui-space-1); + } + + .xs\:bui-py-1\.5 { + padding-top: var(--bui-space-1_5); + padding-bottom: var(--bui-space-1_5); + } + + .xs\:bui-py-2 { + padding-top: var(--bui-space-2); + padding-bottom: var(--bui-space-2); + } + + .xs\:bui-py-3 { + padding-top: var(--bui-space-3); + padding-bottom: var(--bui-space-3); + } + + .xs\:bui-py-4 { + padding-top: var(--bui-space-4); + padding-bottom: var(--bui-space-4); + } + + .xs\:bui-py-5 { + padding-top: var(--bui-space-5); + padding-bottom: var(--bui-space-5); + } + + .xs\:bui-py-6 { + padding-top: var(--bui-space-6); + padding-bottom: var(--bui-space-6); + } + + .xs\:bui-py-7 { + padding-top: var(--bui-space-7); + padding-bottom: var(--bui-space-7); + } + + .xs\:bui-py-8 { + padding-top: var(--bui-space-8); + padding-bottom: var(--bui-space-8); + } + + .xs\:bui-py-9 { + padding-top: var(--bui-space-9); + padding-bottom: var(--bui-space-9); + } + + .xs\:bui-py-10 { + padding-top: var(--bui-space-10); + padding-bottom: var(--bui-space-10); + } + + .xs\:bui-py-11 { + padding-top: var(--bui-space-11); + padding-bottom: var(--bui-space-11); + } + + .xs\:bui-py-12 { + padding-top: var(--bui-space-12); + padding-bottom: var(--bui-space-12); + } + + .xs\:bui-py-13 { + padding-top: var(--bui-space-13); + padding-bottom: var(--bui-space-13); + } + + .xs\:bui-py-14 { + padding-top: var(--bui-space-14); + padding-bottom: var(--bui-space-14); + } + } + + @media (width >= 768px) { + .sm\:bui-py { + padding-top: var(--py-sm); + padding-bottom: var(--py-sm); + } + + .sm\:bui-py-0\.5 { + padding-top: var(--bui-space-0_5); + padding-bottom: var(--bui-space-0_5); + } + + .sm\:bui-py-1 { + padding-top: var(--bui-space-1); + padding-bottom: var(--bui-space-1); + } + + .sm\:bui-py-1\.5 { + padding-top: var(--bui-space-1_5); + padding-bottom: var(--bui-space-1_5); + } + + .sm\:bui-py-2 { + padding-top: var(--bui-space-2); + padding-bottom: var(--bui-space-2); + } + + .sm\:bui-py-3 { + padding-top: var(--bui-space-3); + padding-bottom: var(--bui-space-3); + } + + .sm\:bui-py-4 { + padding-top: var(--bui-space-4); + padding-bottom: var(--bui-space-4); + } + + .sm\:bui-py-5 { + padding-top: var(--bui-space-5); + padding-bottom: var(--bui-space-5); + } + + .sm\:bui-py-6 { + padding-top: var(--bui-space-6); + padding-bottom: var(--bui-space-6); + } + + .sm\:bui-py-7 { + padding-top: var(--bui-space-7); + padding-bottom: var(--bui-space-7); + } + + .sm\:bui-py-8 { + padding-top: var(--bui-space-8); + padding-bottom: var(--bui-space-8); + } + + .sm\:bui-py-9 { + padding-top: var(--bui-space-9); + padding-bottom: var(--bui-space-9); + } + + .sm\:bui-py-10 { + padding-top: var(--bui-space-10); + padding-bottom: var(--bui-space-10); + } + + .sm\:bui-py-11 { + padding-top: var(--bui-space-11); + padding-bottom: var(--bui-space-11); + } + + .sm\:bui-py-12 { + padding-top: var(--bui-space-12); + padding-bottom: var(--bui-space-12); + } + + .sm\:bui-py-13 { + padding-top: var(--bui-space-13); + padding-bottom: var(--bui-space-13); + } + + .sm\:bui-py-14 { + padding-top: var(--bui-space-14); + padding-bottom: var(--bui-space-14); + } + } + + @media (width >= 1024px) { + .md\:bui-py { + padding-top: var(--py-md); + padding-bottom: var(--py-md); + } + + .md\:bui-py-0\.5 { + padding-top: var(--bui-space-0_5); + padding-bottom: var(--bui-space-0_5); + } + + .md\:bui-py-1 { + padding-top: var(--bui-space-1); + padding-bottom: var(--bui-space-1); + } + + .md\:bui-py-1\.5 { + padding-top: var(--bui-space-1_5); + padding-bottom: var(--bui-space-1_5); + } + + .md\:bui-py-2 { + padding-top: var(--bui-space-2); + padding-bottom: var(--bui-space-2); + } + + .md\:bui-py-3 { + padding-top: var(--bui-space-3); + padding-bottom: var(--bui-space-3); + } + + .md\:bui-py-4 { + padding-top: var(--bui-space-4); + padding-bottom: var(--bui-space-4); + } + + .md\:bui-py-5 { + padding-top: var(--bui-space-5); + padding-bottom: var(--bui-space-5); + } + + .md\:bui-py-6 { + padding-top: var(--bui-space-6); + padding-bottom: var(--bui-space-6); + } + + .md\:bui-py-7 { + padding-top: var(--bui-space-7); + padding-bottom: var(--bui-space-7); + } + + .md\:bui-py-8 { + padding-top: var(--bui-space-8); + padding-bottom: var(--bui-space-8); + } + + .md\:bui-py-9 { + padding-top: var(--bui-space-9); + padding-bottom: var(--bui-space-9); + } + + .md\:bui-py-10 { + padding-top: var(--bui-space-10); + padding-bottom: var(--bui-space-10); + } + + .md\:bui-py-11 { + padding-top: var(--bui-space-11); + padding-bottom: var(--bui-space-11); + } + + .md\:bui-py-12 { + padding-top: var(--bui-space-12); + padding-bottom: var(--bui-space-12); + } + + .md\:bui-py-13 { + padding-top: var(--bui-space-13); + padding-bottom: var(--bui-space-13); + } + + .md\:bui-py-14 { + padding-top: var(--bui-space-14); + padding-bottom: var(--bui-space-14); + } + } + + @media (width >= 1280px) { + .lg\:bui-py { + padding-top: var(--py-lg); + padding-bottom: var(--py-lg); + } + + .lg\:bui-py-0\.5 { + padding-top: var(--bui-space-0_5); + padding-bottom: var(--bui-space-0_5); + } + + .lg\:bui-py-1 { + padding-top: var(--bui-space-1); + padding-bottom: var(--bui-space-1); + } + + .lg\:bui-py-1\.5 { + padding-top: var(--bui-space-1_5); + padding-bottom: var(--bui-space-1_5); + } + + .lg\:bui-py-2 { + padding-top: var(--bui-space-2); + padding-bottom: var(--bui-space-2); + } + + .lg\:bui-py-3 { + padding-top: var(--bui-space-3); + padding-bottom: var(--bui-space-3); + } + + .lg\:bui-py-4 { + padding-top: var(--bui-space-4); + padding-bottom: var(--bui-space-4); + } + + .lg\:bui-py-5 { + padding-top: var(--bui-space-5); + padding-bottom: var(--bui-space-5); + } + + .lg\:bui-py-6 { + padding-top: var(--bui-space-6); + padding-bottom: var(--bui-space-6); + } + + .lg\:bui-py-7 { + padding-top: var(--bui-space-7); + padding-bottom: var(--bui-space-7); + } + + .lg\:bui-py-8 { + padding-top: var(--bui-space-8); + padding-bottom: var(--bui-space-8); + } + + .lg\:bui-py-9 { + padding-top: var(--bui-space-9); + padding-bottom: var(--bui-space-9); + } + + .lg\:bui-py-10 { + padding-top: var(--bui-space-10); + padding-bottom: var(--bui-space-10); + } + + .lg\:bui-py-11 { + padding-top: var(--bui-space-11); + padding-bottom: var(--bui-space-11); + } + + .lg\:bui-py-12 { + padding-top: var(--bui-space-12); + padding-bottom: var(--bui-space-12); + } + + .lg\:bui-py-13 { + padding-top: var(--bui-space-13); + padding-bottom: var(--bui-space-13); + } + + .lg\:bui-py-14 { + padding-top: var(--bui-space-14); + padding-bottom: var(--bui-space-14); + } + } + + @media (width >= 1536px) { + .xl\:bui-py { + padding-top: var(--py-xl); + padding-bottom: var(--py-xl); + } + + .xl\:bui-py-0\.5 { + padding-top: var(--bui-space-0_5); + padding-bottom: var(--bui-space-0_5); + } + + .xl\:bui-py-1 { + padding-top: var(--bui-space-1); + padding-bottom: var(--bui-space-1); + } + + .xl\:bui-py-1\.5 { + padding-top: var(--bui-space-1_5); + padding-bottom: var(--bui-space-1_5); + } + + .xl\:bui-py-2 { + padding-top: var(--bui-space-2); + padding-bottom: var(--bui-space-2); + } + + .xl\:bui-py-3 { + padding-top: var(--bui-space-3); + padding-bottom: var(--bui-space-3); + } + + .xl\:bui-py-4 { + padding-top: var(--bui-space-4); + padding-bottom: var(--bui-space-4); + } + + .xl\:bui-py-5 { + padding-top: var(--bui-space-5); + padding-bottom: var(--bui-space-5); + } + + .xl\:bui-py-6 { + padding-top: var(--bui-space-6); + padding-bottom: var(--bui-space-6); + } + + .xl\:bui-py-7 { + padding-top: var(--bui-space-7); + padding-bottom: var(--bui-space-7); + } + + .xl\:bui-py-8 { + padding-top: var(--bui-space-8); + padding-bottom: var(--bui-space-8); + } + + .xl\:bui-py-9 { + padding-top: var(--bui-space-9); + padding-bottom: var(--bui-space-9); + } + + .xl\:bui-py-10 { + padding-top: var(--bui-space-10); + padding-bottom: var(--bui-space-10); + } + + .xl\:bui-py-11 { + padding-top: var(--bui-space-11); + padding-bottom: var(--bui-space-11); + } + + .xl\:bui-py-12 { + padding-top: var(--bui-space-12); + padding-bottom: var(--bui-space-12); + } + + .xl\:bui-py-13 { + padding-top: var(--bui-space-13); + padding-bottom: var(--bui-space-13); + } + + .xl\:bui-py-14 { + padding-top: var(--bui-space-14); + padding-bottom: var(--bui-space-14); + } + } + + .bui-px { + padding-left: var(--px); + padding-right: var(--px); + } + + .bui-px-0\.5 { + padding-left: var(--bui-space-0_5); + padding-right: var(--bui-space-0_5); + } + + .bui-px-1 { + padding-left: var(--bui-space-1); + padding-right: var(--bui-space-1); + } + + .bui-px-1\.5 { + padding-left: var(--bui-space-1_5); + padding-right: var(--bui-space-1_5); + } + + .bui-px-2 { + padding-left: var(--bui-space-2); + padding-right: var(--bui-space-2); + } + + .bui-px-3 { + padding-left: var(--bui-space-3); + padding-right: var(--bui-space-3); + } + + .bui-px-4 { + padding-left: var(--bui-space-4); + padding-right: var(--bui-space-4); + } + + .bui-px-5 { + padding-left: var(--bui-space-5); + padding-right: var(--bui-space-5); + } + + .bui-px-6 { + padding-left: var(--bui-space-6); + padding-right: var(--bui-space-6); + } + + .bui-px-7 { + padding-left: var(--bui-space-7); + padding-right: var(--bui-space-7); + } + + .bui-px-8 { + padding-left: var(--bui-space-8); + padding-right: var(--bui-space-8); + } + + .bui-px-9 { + padding-left: var(--bui-space-9); + padding-right: var(--bui-space-9); + } + + .bui-px-10 { + padding-left: var(--bui-space-10); + padding-right: var(--bui-space-10); + } + + .bui-px-11 { + padding-left: var(--bui-space-11); + padding-right: var(--bui-space-11); + } + + .bui-px-12 { + padding-left: var(--bui-space-12); + padding-right: var(--bui-space-12); + } + + .bui-px-13 { + padding-left: var(--bui-space-13); + padding-right: var(--bui-space-13); + } + + .bui-px-14 { + padding-left: var(--bui-space-14); + padding-right: var(--bui-space-14); + } + + @media (width >= 640px) { + .xs\:bui-px { + padding-left: var(--px-xs); + padding-right: var(--px-xs); + } + + .xs\:bui-px-0\.5 { + padding-left: var(--bui-space-0_5); + padding-right: var(--bui-space-0_5); + } + + .xs\:bui-px-1 { + padding-left: var(--bui-space-1); + padding-right: var(--bui-space-1); + } + + .xs\:bui-px-1\.5 { + padding-left: var(--bui-space-1_5); + padding-right: var(--bui-space-1_5); + } + + .xs\:bui-px-2 { + padding-left: var(--bui-space-2); + padding-right: var(--bui-space-2); + } + + .xs\:bui-px-3 { + padding-left: var(--bui-space-3); + padding-right: var(--bui-space-3); + } + + .xs\:bui-px-4 { + padding-left: var(--bui-space-4); + padding-right: var(--bui-space-4); + } + + .xs\:bui-px-5 { + padding-left: var(--bui-space-5); + padding-right: var(--bui-space-5); + } + + .xs\:bui-px-6 { + padding-left: var(--bui-space-6); + padding-right: var(--bui-space-6); + } + + .xs\:bui-px-7 { + padding-left: var(--bui-space-7); + padding-right: var(--bui-space-7); + } + + .xs\:bui-px-8 { + padding-left: var(--bui-space-8); + padding-right: var(--bui-space-8); + } + + .xs\:bui-px-9 { + padding-left: var(--bui-space-9); + padding-right: var(--bui-space-9); + } + + .xs\:bui-px-10 { + padding-left: var(--bui-space-10); + padding-right: var(--bui-space-10); + } + + .xs\:bui-px-11 { + padding-left: var(--bui-space-11); + padding-right: var(--bui-space-11); + } + + .xs\:bui-px-12 { + padding-left: var(--bui-space-12); + padding-right: var(--bui-space-12); + } + + .xs\:bui-px-13 { + padding-left: var(--bui-space-13); + padding-right: var(--bui-space-13); + } + + .xs\:bui-px-14 { + padding-left: var(--bui-space-14); + padding-right: var(--bui-space-14); + } + } + + @media (width >= 768px) { + .sm\:bui-px { + padding-left: var(--px-sm); + padding-right: var(--px-sm); + } + + .sm\:bui-px-0\.5 { + padding-left: var(--bui-space-0_5); + padding-right: var(--bui-space-0_5); + } + + .sm\:bui-px-1 { + padding-left: var(--bui-space-1); + padding-right: var(--bui-space-1); + } + + .sm\:bui-px-1\.5 { + padding-left: var(--bui-space-1_5); + padding-right: var(--bui-space-1_5); + } + + .sm\:bui-px-2 { + padding-left: var(--bui-space-2); + padding-right: var(--bui-space-2); + } + + .sm\:bui-px-3 { + padding-left: var(--bui-space-3); + padding-right: var(--bui-space-3); + } + + .sm\:bui-px-4 { + padding-left: var(--bui-space-4); + padding-right: var(--bui-space-4); + } + + .sm\:bui-px-5 { + padding-left: var(--bui-space-5); + padding-right: var(--bui-space-5); + } + + .sm\:bui-px-6 { + padding-left: var(--bui-space-6); + padding-right: var(--bui-space-6); + } + + .sm\:bui-px-7 { + padding-left: var(--bui-space-7); + padding-right: var(--bui-space-7); + } + + .sm\:bui-px-8 { + padding-left: var(--bui-space-8); + padding-right: var(--bui-space-8); + } + + .sm\:bui-px-9 { + padding-left: var(--bui-space-9); + padding-right: var(--bui-space-9); + } + + .sm\:bui-px-10 { + padding-left: var(--bui-space-10); + padding-right: var(--bui-space-10); + } + + .sm\:bui-px-11 { + padding-left: var(--bui-space-11); + padding-right: var(--bui-space-11); + } + + .sm\:bui-px-12 { + padding-left: var(--bui-space-12); + padding-right: var(--bui-space-12); + } + + .sm\:bui-px-13 { + padding-left: var(--bui-space-13); + padding-right: var(--bui-space-13); + } + + .sm\:bui-px-14 { + padding-left: var(--bui-space-14); + padding-right: var(--bui-space-14); + } + } + + @media (width >= 1024px) { + .md\:bui-px { + padding-left: var(--px-md); + padding-right: var(--px-md); + } + + .md\:bui-px-0\.5 { + padding-left: var(--bui-space-0_5); + padding-right: var(--bui-space-0_5); + } + + .md\:bui-px-1 { + padding-left: var(--bui-space-1); + padding-right: var(--bui-space-1); + } + + .md\:bui-px-1\.5 { + padding-left: var(--bui-space-1_5); + padding-right: var(--bui-space-1_5); + } + + .md\:bui-px-2 { + padding-left: var(--bui-space-2); + padding-right: var(--bui-space-2); + } + + .md\:bui-px-3 { + padding-left: var(--bui-space-3); + padding-right: var(--bui-space-3); + } + + .md\:bui-px-4 { + padding-left: var(--bui-space-4); + padding-right: var(--bui-space-4); + } + + .md\:bui-px-5 { + padding-left: var(--bui-space-5); + padding-right: var(--bui-space-5); + } + + .md\:bui-px-6 { + padding-left: var(--bui-space-6); + padding-right: var(--bui-space-6); + } + + .md\:bui-px-7 { + padding-left: var(--bui-space-7); + padding-right: var(--bui-space-7); + } + + .md\:bui-px-8 { + padding-left: var(--bui-space-8); + padding-right: var(--bui-space-8); + } + + .md\:bui-px-9 { + padding-left: var(--bui-space-9); + padding-right: var(--bui-space-9); + } + + .md\:bui-px-10 { + padding-left: var(--bui-space-10); + padding-right: var(--bui-space-10); + } + + .md\:bui-px-11 { + padding-left: var(--bui-space-11); + padding-right: var(--bui-space-11); + } + + .md\:bui-px-12 { + padding-left: var(--bui-space-12); + padding-right: var(--bui-space-12); + } + + .md\:bui-px-13 { + padding-left: var(--bui-space-13); + padding-right: var(--bui-space-13); + } + + .md\:bui-px-14 { + padding-left: var(--bui-space-14); + padding-right: var(--bui-space-14); + } + } + + @media (width >= 1280px) { + .lg\:bui-px { + padding-left: var(--px-lg); + padding-right: var(--px-lg); + } + + .lg\:bui-px-0\.5 { + padding-left: var(--bui-space-0_5); + padding-right: var(--bui-space-0_5); + } + + .lg\:bui-px-1 { + padding-left: var(--bui-space-1); + padding-right: var(--bui-space-1); + } + + .lg\:bui-px-1\.5 { + padding-left: var(--bui-space-1_5); + padding-right: var(--bui-space-1_5); + } + + .lg\:bui-px-2 { + padding-left: var(--bui-space-2); + padding-right: var(--bui-space-2); + } + + .lg\:bui-px-3 { + padding-left: var(--bui-space-3); + padding-right: var(--bui-space-3); + } + + .lg\:bui-px-4 { + padding-left: var(--bui-space-4); + padding-right: var(--bui-space-4); + } + + .lg\:bui-px-5 { + padding-left: var(--bui-space-5); + padding-right: var(--bui-space-5); + } + + .lg\:bui-px-6 { + padding-left: var(--bui-space-6); + padding-right: var(--bui-space-6); + } + + .lg\:bui-px-7 { + padding-left: var(--bui-space-7); + padding-right: var(--bui-space-7); + } + + .lg\:bui-px-8 { + padding-left: var(--bui-space-8); + padding-right: var(--bui-space-8); + } + + .lg\:bui-px-9 { + padding-left: var(--bui-space-9); + padding-right: var(--bui-space-9); + } + + .lg\:bui-px-10 { + padding-left: var(--bui-space-10); + padding-right: var(--bui-space-10); + } + + .lg\:bui-px-11 { + padding-left: var(--bui-space-11); + padding-right: var(--bui-space-11); + } + + .lg\:bui-px-12 { + padding-left: var(--bui-space-12); + padding-right: var(--bui-space-12); + } + + .lg\:bui-px-13 { + padding-left: var(--bui-space-13); + padding-right: var(--bui-space-13); + } + + .lg\:bui-px-14 { + padding-left: var(--bui-space-14); + padding-right: var(--bui-space-14); + } + } + + @media (width >= 1536px) { + .xl\:bui-px { + padding-left: var(--px-xl); + padding-right: var(--px-xl); + } + + .xl\:bui-px-0\.5 { + padding-left: var(--bui-space-0_5); + padding-right: var(--bui-space-0_5); + } + + .xl\:bui-px-1 { + padding-left: var(--bui-space-1); + padding-right: var(--bui-space-1); + } + + .xl\:bui-px-1\.5 { + padding-left: var(--bui-space-1_5); + padding-right: var(--bui-space-1_5); + } + + .xl\:bui-px-2 { + padding-left: var(--bui-space-2); + padding-right: var(--bui-space-2); + } + + .xl\:bui-px-3 { + padding-left: var(--bui-space-3); + padding-right: var(--bui-space-3); + } + + .xl\:bui-px-4 { + padding-left: var(--bui-space-4); + padding-right: var(--bui-space-4); + } + + .xl\:bui-px-5 { + padding-left: var(--bui-space-5); + padding-right: var(--bui-space-5); + } + + .xl\:bui-px-6 { + padding-left: var(--bui-space-6); + padding-right: var(--bui-space-6); + } + + .xl\:bui-px-7 { + padding-left: var(--bui-space-7); + padding-right: var(--bui-space-7); + } + + .xl\:bui-px-8 { + padding-left: var(--bui-space-8); + padding-right: var(--bui-space-8); + } + + .xl\:bui-px-9 { + padding-left: var(--bui-space-9); + padding-right: var(--bui-space-9); + } + + .xl\:bui-px-10 { + padding-left: var(--bui-space-10); + padding-right: var(--bui-space-10); + } + + .xl\:bui-px-11 { + padding-left: var(--bui-space-11); + padding-right: var(--bui-space-11); + } + + .xl\:bui-px-12 { + padding-left: var(--bui-space-12); + padding-right: var(--bui-space-12); + } + + .xl\:bui-px-13 { + padding-left: var(--bui-space-13); + padding-right: var(--bui-space-13); + } + + .xl\:bui-px-14 { + padding-left: var(--bui-space-14); + padding-right: var(--bui-space-14); + } + } + + .bui-m { + margin: var(--m); + } + + .bui-m-0\.5 { + margin: var(--bui-space-0_5); + } + + .bui-m-1 { + margin: var(--bui-space-1); + } + + .bui-m-1\.5 { + margin: var(--bui-space-1_5); + } + + .bui-m-2 { + margin: var(--bui-space-2); + } + + .bui-m-3 { + margin: var(--bui-space-3); + } + + .bui-m-4 { + margin: var(--bui-space-4); + } + + .bui-m-5 { + margin: var(--bui-space-5); + } + + .bui-m-6 { + margin: var(--bui-space-6); + } + + .bui-m-7 { + margin: var(--bui-space-7); + } + + .bui-m-8 { + margin: var(--bui-space-8); + } + + .bui-m-9 { + margin: var(--bui-space-9); + } + + .bui-m-10 { + margin: var(--bui-space-10); + } + + .bui-m-11 { + margin: var(--bui-space-11); + } + + .bui-m-12 { + margin: var(--bui-space-12); + } + + .bui-m-13 { + margin: var(--bui-space-13); + } + + .bui-m-14 { + margin: var(--bui-space-14); + } + + @media (width >= 640px) { + .xs\:bui-m { + margin: var(--p-xs); + } + + .xs\:bui-m-0\.5 { + margin: var(--bui-space-0_5); + } + + .xs\:bui-m-1 { + margin: var(--bui-space-1); + } + + .xs\:bui-m-1\.5 { + margin: var(--bui-space-1_5); + } + + .xs\:bui-m-2 { + margin: var(--bui-space-2); + } + + .xs\:bui-m-3 { + margin: var(--bui-space-3); + } + + .xs\:bui-m-4 { + margin: var(--bui-space-4); + } + + .xs\:bui-m-5 { + margin: var(--bui-space-5); + } + + .xs\:bui-m-6 { + margin: var(--bui-space-6); + } + + .xs\:bui-m-7 { + margin: var(--bui-space-7); + } + + .xs\:bui-m-8 { + margin: var(--bui-space-8); + } + + .xs\:bui-m-9 { + margin: var(--bui-space-9); + } + + .xs\:bui-m-10 { + margin: var(--bui-space-10); + } + + .xs\:bui-m-11 { + margin: var(--bui-space-11); + } + + .xs\:bui-m-12 { + margin: var(--bui-space-12); + } + + .xs\:bui-m-13 { + margin: var(--bui-space-13); + } + + .xs\:bui-m-14 { + margin: var(--bui-space-14); + } + } + + @media (width >= 768px) { + .sm\:bui-m { + margin: var(--p-sm); + } + + .sm\:bui-m-0\.5 { + margin: var(--bui-space-0_5); + } + + .sm\:bui-m-1 { + margin: var(--bui-space-1); + } + + .sm\:bui-m-1\.5 { + margin: var(--bui-space-1_5); + } + + .sm\:bui-m-2 { + margin: var(--bui-space-2); + } + + .sm\:bui-m-3 { + margin: var(--bui-space-3); + } + + .sm\:bui-m-4 { + margin: var(--bui-space-4); + } + + .sm\:bui-m-5 { + margin: var(--bui-space-5); + } + + .sm\:bui-m-6 { + margin: var(--bui-space-6); + } + + .sm\:bui-m-7 { + margin: var(--bui-space-7); + } + + .sm\:bui-m-8 { + margin: var(--bui-space-8); + } + + .sm\:bui-m-9 { + margin: var(--bui-space-9); + } + + .sm\:bui-m-10 { + margin: var(--bui-space-10); + } + + .sm\:bui-m-11 { + margin: var(--bui-space-11); + } + + .sm\:bui-m-12 { + margin: var(--bui-space-12); + } + + .sm\:bui-m-13 { + margin: var(--bui-space-13); + } + + .sm\:bui-m-14 { + margin: var(--bui-space-14); + } + } + + @media (width >= 1024px) { + .md\:bui-m { + margin: var(--p-md); + } + + .md\:bui-m-0\.5 { + margin: var(--bui-space-0_5); + } + + .md\:bui-m-1 { + margin: var(--bui-space-1); + } + + .md\:bui-m-1\.5 { + margin: var(--bui-space-1_5); + } + + .md\:bui-m-2 { + margin: var(--bui-space-2); + } + + .md\:bui-m-3 { + margin: var(--bui-space-3); + } + + .md\:bui-m-4 { + margin: var(--bui-space-4); + } + + .md\:bui-m-5 { + margin: var(--bui-space-5); + } + + .md\:bui-m-6 { + margin: var(--bui-space-6); + } + + .md\:bui-m-7 { + margin: var(--bui-space-7); + } + + .md\:bui-m-8 { + margin: var(--bui-space-8); + } + + .md\:bui-m-9 { + margin: var(--bui-space-9); + } + + .md\:bui-m-10 { + margin: var(--bui-space-10); + } + + .md\:bui-m-11 { + margin: var(--bui-space-11); + } + + .md\:bui-m-12 { + margin: var(--bui-space-12); + } + + .md\:bui-m-13 { + margin: var(--bui-space-13); + } + + .md\:bui-m-14 { + margin: var(--bui-space-14); + } + } + + @media (width >= 1280px) { + .lg\:bui-m { + margin: var(--p-lg); + } + + .lg\:bui-m-0\.5 { + margin: var(--bui-space-0_5); + } + + .lg\:bui-m-1 { + margin: var(--bui-space-1); + } + + .lg\:bui-m-1\.5 { + margin: var(--bui-space-1_5); + } + + .lg\:bui-m-2 { + margin: var(--bui-space-2); + } + + .lg\:bui-m-3 { + margin: var(--bui-space-3); + } + + .lg\:bui-m-4 { + margin: var(--bui-space-4); + } + + .lg\:bui-m-5 { + margin: var(--bui-space-5); + } + + .lg\:bui-m-6 { + margin: var(--bui-space-6); + } + + .lg\:bui-m-7 { + margin: var(--bui-space-7); + } + + .lg\:bui-m-8 { + margin: var(--bui-space-8); + } + + .lg\:bui-m-9 { + margin: var(--bui-space-9); + } + + .lg\:bui-m-10 { + margin: var(--bui-space-10); + } + + .lg\:bui-m-11 { + margin: var(--bui-space-11); + } + + .lg\:bui-m-12 { + margin: var(--bui-space-12); + } + + .lg\:bui-m-13 { + margin: var(--bui-space-13); + } + + .lg\:bui-m-14 { + margin: var(--bui-space-14); + } + } + + @media (width >= 1536px) { + .xl\:bui-m { + margin: var(--p-xl); + } + + .xl\:bui-m-0\.5 { + margin: var(--bui-space-0_5); + } + + .xl\:bui-m-1 { + margin: var(--bui-space-1); + } + + .xl\:bui-m-1\.5 { + margin: var(--bui-space-1_5); + } + + .xl\:bui-m-2 { + margin: var(--bui-space-2); + } + + .xl\:bui-m-3 { + margin: var(--bui-space-3); + } + + .xl\:bui-m-4 { + margin: var(--bui-space-4); + } + + .xl\:bui-m-5 { + margin: var(--bui-space-5); + } + + .xl\:bui-m-6 { + margin: var(--bui-space-6); + } + + .xl\:bui-m-7 { + margin: var(--bui-space-7); + } + + .xl\:bui-m-8 { + margin: var(--bui-space-8); + } + + .xl\:bui-m-9 { + margin: var(--bui-space-9); + } + + .xl\:bui-m-10 { + margin: var(--bui-space-10); + } + + .xl\:bui-m-11 { + margin: var(--bui-space-11); + } + + .xl\:bui-m-12 { + margin: var(--bui-space-12); + } + + .xl\:bui-m-13 { + margin: var(--bui-space-13); + } + + .xl\:bui-m-14 { + margin: var(--bui-space-14); + } + } + + .bui-ml { + margin-left: var(--ml); + } + + .bui-ml-0\.5 { + margin-left: var(--bui-space-0_5); + } + + .bui-ml-1 { + margin-left: var(--bui-space-1); + } + + .bui-ml-1\.5 { + margin-left: var(--bui-space-1_5); + } + + .bui-ml-2 { + margin-left: var(--bui-space-2); + } + + .bui-ml-3 { + margin-left: var(--bui-space-3); + } + + .bui-ml-4 { + margin-left: var(--bui-space-4); + } + + .bui-ml-5 { + margin-left: var(--bui-space-5); + } + + .bui-ml-6 { + margin-left: var(--bui-space-6); + } + + .bui-ml-7 { + margin-left: var(--bui-space-7); + } + + .bui-ml-8 { + margin-left: var(--bui-space-8); + } + + .bui-ml-9 { + margin-left: var(--bui-space-9); + } + + .bui-ml-10 { + margin-left: var(--bui-space-10); + } + + .bui-ml-11 { + margin-left: var(--bui-space-11); + } + + .bui-ml-12 { + margin-left: var(--bui-space-12); + } + + .bui-ml-13 { + margin-left: var(--bui-space-13); + } + + .bui-ml-14 { + margin-left: var(--bui-space-14); + } + + @media (width >= 640px) { + .xs\:bui-ml { + margin-left: var(--ml-xs); + } + + .xs\:bui-ml-0\.5 { + margin-left: var(--bui-space-0_5); + } + + .xs\:bui-ml-1 { + margin-left: var(--bui-space-1); + } + + .xs\:bui-ml-1\.5 { + margin-left: var(--bui-space-1_5); + } + + .xs\:bui-ml-2 { + margin-left: var(--bui-space-2); + } + + .xs\:bui-ml-3 { + margin-left: var(--bui-space-3); + } + + .xs\:bui-ml-4 { + margin-left: var(--bui-space-4); + } + + .xs\:bui-ml-5 { + margin-left: var(--bui-space-5); + } + + .xs\:bui-ml-6 { + margin-left: var(--bui-space-6); + } + + .xs\:bui-ml-7 { + margin-left: var(--bui-space-7); + } + + .xs\:bui-ml-8 { + margin-left: var(--bui-space-8); + } + + .xs\:bui-ml-9 { + margin-left: var(--bui-space-9); + } + + .xs\:bui-ml-10 { + margin-left: var(--bui-space-10); + } + + .xs\:bui-ml-11 { + margin-left: var(--bui-space-11); + } + + .xs\:bui-ml-12 { + margin-left: var(--bui-space-12); + } + + .xs\:bui-ml-13 { + margin-left: var(--bui-space-13); + } + + .xs\:bui-ml-14 { + margin-left: var(--bui-space-14); + } + } + + @media (width >= 768px) { + .sm\:bui-ml { + margin-left: var(--ml-sm); + } + + .sm\:bui-ml-0\.5 { + margin-left: var(--bui-space-0_5); + } + + .sm\:bui-ml-1 { + margin-left: var(--bui-space-1); + } + + .sm\:bui-ml-1\.5 { + margin-left: var(--bui-space-1_5); + } + + .sm\:bui-ml-2 { + margin-left: var(--bui-space-2); + } + + .sm\:bui-ml-3 { + margin-left: var(--bui-space-3); + } + + .sm\:bui-ml-4 { + margin-left: var(--bui-space-4); + } + + .sm\:bui-ml-5 { + margin-left: var(--bui-space-5); + } + + .sm\:bui-ml-6 { + margin-left: var(--bui-space-6); + } + + .sm\:bui-ml-7 { + margin-left: var(--bui-space-7); + } + + .sm\:bui-ml-8 { + margin-left: var(--bui-space-8); + } + + .sm\:bui-ml-9 { + margin-left: var(--bui-space-9); + } + + .sm\:bui-ml-10 { + margin-left: var(--bui-space-10); + } + + .sm\:bui-ml-11 { + margin-left: var(--bui-space-11); + } + + .sm\:bui-ml-12 { + margin-left: var(--bui-space-12); + } + + .sm\:bui-ml-13 { + margin-left: var(--bui-space-13); + } + + .sm\:bui-ml-14 { + margin-left: var(--bui-space-14); + } + } + + @media (width >= 1024px) { + .md\:bui-ml { + margin-left: var(--ml-md); + } + + .md\:bui-ml-0\.5 { + margin-left: var(--bui-space-0_5); + } + + .md\:bui-ml-1 { + margin-left: var(--bui-space-1); + } + + .md\:bui-ml-1\.5 { + margin-left: var(--bui-space-1_5); + } + + .md\:bui-ml-2 { + margin-left: var(--bui-space-2); + } + + .md\:bui-ml-3 { + margin-left: var(--bui-space-3); + } + + .md\:bui-ml-4 { + margin-left: var(--bui-space-4); + } + + .md\:bui-ml-5 { + margin-left: var(--bui-space-5); + } + + .md\:bui-ml-6 { + margin-left: var(--bui-space-6); + } + + .md\:bui-ml-7 { + margin-left: var(--bui-space-7); + } + + .md\:bui-ml-8 { + margin-left: var(--bui-space-8); + } + + .md\:bui-ml-9 { + margin-left: var(--bui-space-9); + } + + .md\:bui-ml-10 { + margin-left: var(--bui-space-10); + } + + .md\:bui-ml-11 { + margin-left: var(--bui-space-11); + } + + .md\:bui-ml-12 { + margin-left: var(--bui-space-12); + } + + .md\:bui-ml-13 { + margin-left: var(--bui-space-13); + } + + .md\:bui-ml-14 { + margin-left: var(--bui-space-14); + } + } + + @media (width >= 1280px) { + .lg\:bui-ml { + margin-left: var(--ml-lg); + } + + .lg\:bui-ml-0\.5 { + margin-left: var(--bui-space-0_5); + } + + .lg\:bui-ml-1 { + margin-left: var(--bui-space-1); + } + + .lg\:bui-ml-1\.5 { + margin-left: var(--bui-space-1_5); + } + + .lg\:bui-ml-2 { + margin-left: var(--bui-space-2); + } + + .lg\:bui-ml-3 { + margin-left: var(--bui-space-3); + } + + .lg\:bui-ml-4 { + margin-left: var(--bui-space-4); + } + + .lg\:bui-ml-5 { + margin-left: var(--bui-space-5); + } + + .lg\:bui-ml-6 { + margin-left: var(--bui-space-6); + } + + .lg\:bui-ml-7 { + margin-left: var(--bui-space-7); + } + + .lg\:bui-ml-8 { + margin-left: var(--bui-space-8); + } + + .lg\:bui-ml-9 { + margin-left: var(--bui-space-9); + } + + .lg\:bui-ml-10 { + margin-left: var(--bui-space-10); + } + + .lg\:bui-ml-11 { + margin-left: var(--bui-space-11); + } + + .lg\:bui-ml-12 { + margin-left: var(--bui-space-12); + } + + .lg\:bui-ml-13 { + margin-left: var(--bui-space-13); + } + + .lg\:bui-ml-14 { + margin-left: var(--bui-space-14); + } + } + + @media (width >= 1536px) { + .xl\:bui-ml { + margin-left: var(--ml-xl); + } + + .xl\:bui-ml-0\.5 { + margin-left: var(--bui-space-0_5); + } + + .xl\:bui-ml-1 { + margin-left: var(--bui-space-1); + } + + .xl\:bui-ml-1\.5 { + margin-left: var(--bui-space-1_5); + } + + .xl\:bui-ml-2 { + margin-left: var(--bui-space-2); + } + + .xl\:bui-ml-3 { + margin-left: var(--bui-space-3); + } + + .xl\:bui-ml-4 { + margin-left: var(--bui-space-4); + } + + .xl\:bui-ml-5 { + margin-left: var(--bui-space-5); + } + + .xl\:bui-ml-6 { + margin-left: var(--bui-space-6); + } + + .xl\:bui-ml-7 { + margin-left: var(--bui-space-7); + } + + .xl\:bui-ml-8 { + margin-left: var(--bui-space-8); + } + + .xl\:bui-ml-9 { + margin-left: var(--bui-space-9); + } + + .xl\:bui-ml-10 { + margin-left: var(--bui-space-10); + } + + .xl\:bui-ml-11 { + margin-left: var(--bui-space-11); + } + + .xl\:bui-ml-12 { + margin-left: var(--bui-space-12); + } + + .xl\:bui-ml-13 { + margin-left: var(--bui-space-13); + } + + .xl\:bui-ml-14 { + margin-left: var(--bui-space-14); + } + } + + .bui-mr { + margin-right: var(--mr); + } + + .bui-mr-0\.5 { + margin-right: var(--bui-space-0_5); + } + + .bui-mr-1 { + margin-right: var(--bui-space-1); + } + + .bui-mr-1\.5 { + margin-right: var(--bui-space-1_5); + } + + .bui-mr-2 { + margin-right: var(--bui-space-2); + } + + .bui-mr-3 { + margin-right: var(--bui-space-3); + } + + .bui-mr-4 { + margin-right: var(--bui-space-4); + } + + .bui-mr-5 { + margin-right: var(--bui-space-5); + } + + .bui-mr-6 { + margin-right: var(--bui-space-6); + } + + .bui-mr-7 { + margin-right: var(--bui-space-7); + } + + .bui-mr-8 { + margin-right: var(--bui-space-8); + } + + .bui-mr-9 { + margin-right: var(--bui-space-9); + } + + .bui-mr-10 { + margin-right: var(--bui-space-10); + } + + .bui-mr-11 { + margin-right: var(--bui-space-11); + } + + .bui-mr-12 { + margin-right: var(--bui-space-12); + } + + .bui-mr-13 { + margin-right: var(--bui-space-13); + } + + .bui-mr-14 { + margin-right: var(--bui-space-14); + } + + @media (width >= 640px) { + .xs\:bui-mr { + margin-right: var(--mr-xs); + } + + .xs\:bui-mr-0\.5 { + margin-right: var(--bui-space-0_5); + } + + .xs\:bui-mr-1 { + margin-right: var(--bui-space-1); + } + + .xs\:bui-mr-1\.5 { + margin-right: var(--bui-space-1_5); + } + + .xs\:bui-mr-2 { + margin-right: var(--bui-space-2); + } + + .xs\:bui-mr-3 { + margin-right: var(--bui-space-3); + } + + .xs\:bui-mr-4 { + margin-right: var(--bui-space-4); + } + + .xs\:bui-mr-5 { + margin-right: var(--bui-space-5); + } + + .xs\:bui-mr-6 { + margin-right: var(--bui-space-6); + } + + .xs\:bui-mr-7 { + margin-right: var(--bui-space-7); + } + + .xs\:bui-mr-8 { + margin-right: var(--bui-space-8); + } + + .xs\:bui-mr-9 { + margin-right: var(--bui-space-9); + } + + .xs\:bui-mr-10 { + margin-right: var(--bui-space-10); + } + + .xs\:bui-mr-11 { + margin-right: var(--bui-space-11); + } + + .xs\:bui-mr-12 { + margin-right: var(--bui-space-12); + } + + .xs\:bui-mr-13 { + margin-right: var(--bui-space-13); + } + + .xs\:bui-mr-14 { + margin-right: var(--bui-space-14); + } + } + + @media (width >= 768px) { + .sm\:bui-mr { + margin-right: var(--mr-sm); + } + + .sm\:bui-mr-0\.5 { + margin-right: var(--bui-space-0_5); + } + + .sm\:bui-mr-1 { + margin-right: var(--bui-space-1); + } + + .sm\:bui-mr-1\.5 { + margin-right: var(--bui-space-1_5); + } + + .sm\:bui-mr-2 { + margin-right: var(--bui-space-2); + } + + .sm\:bui-mr-3 { + margin-right: var(--bui-space-3); + } + + .sm\:bui-mr-4 { + margin-right: var(--bui-space-4); + } + + .sm\:bui-mr-5 { + margin-right: var(--bui-space-5); + } + + .sm\:bui-mr-6 { + margin-right: var(--bui-space-6); + } + + .sm\:bui-mr-7 { + margin-right: var(--bui-space-7); + } + + .sm\:bui-mr-8 { + margin-right: var(--bui-space-8); + } + + .sm\:bui-mr-9 { + margin-right: var(--bui-space-9); + } + + .sm\:bui-mr-10 { + margin-right: var(--bui-space-10); + } + + .sm\:bui-mr-11 { + margin-right: var(--bui-space-11); + } + + .sm\:bui-mr-12 { + margin-right: var(--bui-space-12); + } + + .sm\:bui-mr-13 { + margin-right: var(--bui-space-13); + } + + .sm\:bui-mr-14 { + margin-right: var(--bui-space-14); + } + } + + @media (width >= 1024px) { + .md\:bui-mr { + margin-right: var(--mr-md); + } + + .md\:bui-mr-0\.5 { + margin-right: var(--bui-space-0_5); + } + + .md\:bui-mr-1 { + margin-right: var(--bui-space-1); + } + + .md\:bui-mr-1\.5 { + margin-right: var(--bui-space-1_5); + } + + .md\:bui-mr-2 { + margin-right: var(--bui-space-2); + } + + .md\:bui-mr-3 { + margin-right: var(--bui-space-3); + } + + .md\:bui-mr-4 { + margin-right: var(--bui-space-4); + } + + .md\:bui-mr-5 { + margin-right: var(--bui-space-5); + } + + .md\:bui-mr-6 { + margin-right: var(--bui-space-6); + } + + .md\:bui-mr-7 { + margin-right: var(--bui-space-7); + } + + .md\:bui-mr-8 { + margin-right: var(--bui-space-8); + } + + .md\:bui-mr-9 { + margin-right: var(--bui-space-9); + } + + .md\:bui-mr-10 { + margin-right: var(--bui-space-10); + } + + .md\:bui-mr-11 { + margin-right: var(--bui-space-11); + } + + .md\:bui-mr-12 { + margin-right: var(--bui-space-12); + } + + .md\:bui-mr-13 { + margin-right: var(--bui-space-13); + } + + .md\:bui-mr-14 { + margin-right: var(--bui-space-14); + } + } + + @media (width >= 1280px) { + .lg\:bui-mr { + margin-right: var(--mr-lg); + } + + .lg\:bui-mr-0\.5 { + margin-right: var(--bui-space-0_5); + } + + .lg\:bui-mr-1 { + margin-right: var(--bui-space-1); + } + + .lg\:bui-mr-1\.5 { + margin-right: var(--bui-space-1_5); + } + + .lg\:bui-mr-2 { + margin-right: var(--bui-space-2); + } + + .lg\:bui-mr-3 { + margin-right: var(--bui-space-3); + } + + .lg\:bui-mr-4 { + margin-right: var(--bui-space-4); + } + + .lg\:bui-mr-5 { + margin-right: var(--bui-space-5); + } + + .lg\:bui-mr-6 { + margin-right: var(--bui-space-6); + } + + .lg\:bui-mr-7 { + margin-right: var(--bui-space-7); + } + + .lg\:bui-mr-8 { + margin-right: var(--bui-space-8); + } + + .lg\:bui-mr-9 { + margin-right: var(--bui-space-9); + } + + .lg\:bui-mr-10 { + margin-right: var(--bui-space-10); + } + + .lg\:bui-mr-11 { + margin-right: var(--bui-space-11); + } + + .lg\:bui-mr-12 { + margin-right: var(--bui-space-12); + } + + .lg\:bui-mr-13 { + margin-right: var(--bui-space-13); + } + + .lg\:bui-mr-14 { + margin-right: var(--bui-space-14); + } + } + + @media (width >= 1536px) { + .xl\:bui-mr { + margin-right: var(--mr-xl); + } + + .xl\:bui-mr-0\.5 { + margin-right: var(--bui-space-0_5); + } + + .xl\:bui-mr-1 { + margin-right: var(--bui-space-1); + } + + .xl\:bui-mr-1\.5 { + margin-right: var(--bui-space-1_5); + } + + .xl\:bui-mr-2 { + margin-right: var(--bui-space-2); + } + + .xl\:bui-mr-3 { + margin-right: var(--bui-space-3); + } + + .xl\:bui-mr-4 { + margin-right: var(--bui-space-4); + } + + .xl\:bui-mr-5 { + margin-right: var(--bui-space-5); + } + + .xl\:bui-mr-6 { + margin-right: var(--bui-space-6); + } + + .xl\:bui-mr-7 { + margin-right: var(--bui-space-7); + } + + .xl\:bui-mr-8 { + margin-right: var(--bui-space-8); + } + + .xl\:bui-mr-9 { + margin-right: var(--bui-space-9); + } + + .xl\:bui-mr-10 { + margin-right: var(--bui-space-10); + } + + .xl\:bui-mr-11 { + margin-right: var(--bui-space-11); + } + + .xl\:bui-mr-12 { + margin-right: var(--bui-space-12); + } + + .xl\:bui-mr-13 { + margin-right: var(--bui-space-13); + } + + .xl\:bui-mr-14 { + margin-right: var(--bui-space-14); + } + } + + .bui-mt { + margin-top: var(--mt); + } + + .bui-mt-0\.5 { + margin-top: var(--bui-space-0_5); + } + + .bui-mt-1 { + margin-top: var(--bui-space-1); + } + + .bui-mt-1\.5 { + margin-top: var(--bui-space-1_5); + } + + .bui-mt-2 { + margin-top: var(--bui-space-2); + } + + .bui-mt-3 { + margin-top: var(--bui-space-3); + } + + .bui-mt-4 { + margin-top: var(--bui-space-4); + } + + .bui-mt-5 { + margin-top: var(--bui-space-5); + } + + .bui-mt-6 { + margin-top: var(--bui-space-6); + } + + .bui-mt-7 { + margin-top: var(--bui-space-7); + } + + .bui-mt-8 { + margin-top: var(--bui-space-8); + } + + .bui-mt-9 { + margin-top: var(--bui-space-9); + } + + .bui-mt-10 { + margin-top: var(--bui-space-10); + } + + .bui-mt-11 { + margin-top: var(--bui-space-11); + } + + .bui-mt-12 { + margin-top: var(--bui-space-12); + } + + .bui-mt-13 { + margin-top: var(--bui-space-13); + } + + .bui-mt-14 { + margin-top: var(--bui-space-14); + } + + @media (width >= 640px) { + .xs\:bui-mt { + margin-top: var(--mt-xs); + } + + .xs\:bui-mt-0\.5 { + margin-top: var(--bui-space-0_5); + } + + .xs\:bui-mt-1 { + margin-top: var(--bui-space-1); + } + + .xs\:bui-mt-1\.5 { + margin-top: var(--bui-space-1_5); + } + + .xs\:bui-mt-2 { + margin-top: var(--bui-space-2); + } + + .xs\:bui-mt-3 { + margin-top: var(--bui-space-3); + } + + .xs\:bui-mt-4 { + margin-top: var(--bui-space-4); + } + + .xs\:bui-mt-5 { + margin-top: var(--bui-space-5); + } + + .xs\:bui-mt-6 { + margin-top: var(--bui-space-6); + } + + .xs\:bui-mt-7 { + margin-top: var(--bui-space-7); + } + + .xs\:bui-mt-8 { + margin-top: var(--bui-space-8); + } + + .xs\:bui-mt-9 { + margin-top: var(--bui-space-9); + } + + .xs\:bui-mt-10 { + margin-top: var(--bui-space-10); + } + + .xs\:bui-mt-11 { + margin-top: var(--bui-space-11); + } + + .xs\:bui-mt-12 { + margin-top: var(--bui-space-12); + } + + .xs\:bui-mt-13 { + margin-top: var(--bui-space-13); + } + + .xs\:bui-mt-14 { + margin-top: var(--bui-space-14); + } + } + + @media (width >= 768px) { + .sm\:bui-mt { + margin-top: var(--mt-sm); + } + + .sm\:bui-mt-0\.5 { + margin-top: var(--bui-space-0_5); + } + + .sm\:bui-mt-1 { + margin-top: var(--bui-space-1); + } + + .sm\:bui-mt-1\.5 { + margin-top: var(--bui-space-1_5); + } + + .sm\:bui-mt-2 { + margin-top: var(--bui-space-2); + } + + .sm\:bui-mt-3 { + margin-top: var(--bui-space-3); + } + + .sm\:bui-mt-4 { + margin-top: var(--bui-space-4); + } + + .sm\:bui-mt-5 { + margin-top: var(--bui-space-5); + } + + .sm\:bui-mt-6 { + margin-top: var(--bui-space-6); + } + + .sm\:bui-mt-7 { + margin-top: var(--bui-space-7); + } + + .sm\:bui-mt-8 { + margin-top: var(--bui-space-8); + } + + .sm\:bui-mt-9 { + margin-top: var(--bui-space-9); + } + + .sm\:bui-mt-10 { + margin-top: var(--bui-space-10); + } + + .sm\:bui-mt-11 { + margin-top: var(--bui-space-11); + } + + .sm\:bui-mt-12 { + margin-top: var(--bui-space-12); + } + + .sm\:bui-mt-13 { + margin-top: var(--bui-space-13); + } + + .sm\:bui-mt-14 { + margin-top: var(--bui-space-14); + } + } + + @media (width >= 1024px) { + .md\:bui-mt { + margin-top: var(--mt-md); + } + + .md\:bui-mt-0\.5 { + margin-top: var(--bui-space-0_5); + } + + .md\:bui-mt-1 { + margin-top: var(--bui-space-1); + } + + .md\:bui-mt-1\.5 { + margin-top: var(--bui-space-1_5); + } + + .md\:bui-mt-2 { + margin-top: var(--bui-space-2); + } + + .md\:bui-mt-3 { + margin-top: var(--bui-space-3); + } + + .md\:bui-mt-4 { + margin-top: var(--bui-space-4); + } + + .md\:bui-mt-5 { + margin-top: var(--bui-space-5); + } + + .md\:bui-mt-6 { + margin-top: var(--bui-space-6); + } + + .md\:bui-mt-7 { + margin-top: var(--bui-space-7); + } + + .md\:bui-mt-8 { + margin-top: var(--bui-space-8); + } + + .md\:bui-mt-9 { + margin-top: var(--bui-space-9); + } + + .md\:bui-mt-10 { + margin-top: var(--bui-space-10); + } + + .md\:bui-mt-11 { + margin-top: var(--bui-space-11); + } + + .md\:bui-mt-12 { + margin-top: var(--bui-space-12); + } + + .md\:bui-mt-13 { + margin-top: var(--bui-space-13); + } + + .md\:bui-mt-14 { + margin-top: var(--bui-space-14); + } + } + + @media (width >= 1280px) { + .lg\:bui-mt { + margin-top: var(--mt-lg); + } + + .lg\:bui-mt-0\.5 { + margin-top: var(--bui-space-0_5); + } + + .lg\:bui-mt-1 { + margin-top: var(--bui-space-1); + } + + .lg\:bui-mt-1\.5 { + margin-top: var(--bui-space-1_5); + } + + .lg\:bui-mt-2 { + margin-top: var(--bui-space-2); + } + + .lg\:bui-mt-3 { + margin-top: var(--bui-space-3); + } + + .lg\:bui-mt-4 { + margin-top: var(--bui-space-4); + } + + .lg\:bui-mt-5 { + margin-top: var(--bui-space-5); + } + + .lg\:bui-mt-6 { + margin-top: var(--bui-space-6); + } + + .lg\:bui-mt-7 { + margin-top: var(--bui-space-7); + } + + .lg\:bui-mt-8 { + margin-top: var(--bui-space-8); + } + + .lg\:bui-mt-9 { + margin-top: var(--bui-space-9); + } + + .lg\:bui-mt-10 { + margin-top: var(--bui-space-10); + } + + .lg\:bui-mt-11 { + margin-top: var(--bui-space-11); + } + + .lg\:bui-mt-12 { + margin-top: var(--bui-space-12); + } + + .lg\:bui-mt-13 { + margin-top: var(--bui-space-13); + } + + .lg\:bui-mt-14 { + margin-top: var(--bui-space-14); + } + } + + @media (width >= 1536px) { + .xl\:bui-mt { + margin-top: var(--mt-xl); + } + + .xl\:bui-mt-0\.5 { + margin-top: var(--bui-space-0_5); + } + + .xl\:bui-mt-1 { + margin-top: var(--bui-space-1); + } + + .xl\:bui-mt-1\.5 { + margin-top: var(--bui-space-1_5); + } + + .xl\:bui-mt-2 { + margin-top: var(--bui-space-2); + } + + .xl\:bui-mt-3 { + margin-top: var(--bui-space-3); + } + + .xl\:bui-mt-4 { + margin-top: var(--bui-space-4); + } + + .xl\:bui-mt-5 { + margin-top: var(--bui-space-5); + } + + .xl\:bui-mt-6 { + margin-top: var(--bui-space-6); + } + + .xl\:bui-mt-7 { + margin-top: var(--bui-space-7); + } + + .xl\:bui-mt-8 { + margin-top: var(--bui-space-8); + } + + .xl\:bui-mt-9 { + margin-top: var(--bui-space-9); + } + + .xl\:bui-mt-10 { + margin-top: var(--bui-space-10); + } + + .xl\:bui-mt-11 { + margin-top: var(--bui-space-11); + } + + .xl\:bui-mt-12 { + margin-top: var(--bui-space-12); + } + + .xl\:bui-mt-13 { + margin-top: var(--bui-space-13); + } + + .xl\:bui-mt-14 { + margin-top: var(--bui-space-14); + } + } + + .bui-mb { + margin-bottom: var(--mb); + } + + .bui-mb-0\.5 { + margin-bottom: var(--bui-space-0_5); + } + + .bui-mb-1 { + margin-bottom: var(--bui-space-1); + } + + .bui-mb-1\.5 { + margin-bottom: var(--bui-space-1_5); + } + + .bui-mb-2 { + margin-bottom: var(--bui-space-2); + } + + .bui-mb-3 { + margin-bottom: var(--bui-space-3); + } + + .bui-mb-4 { + margin-bottom: var(--bui-space-4); + } + + .bui-mb-5 { + margin-bottom: var(--bui-space-5); + } + + .bui-mb-6 { + margin-bottom: var(--bui-space-6); + } + + .bui-mb-7 { + margin-bottom: var(--bui-space-7); + } + + .bui-mb-8 { + margin-bottom: var(--bui-space-8); + } + + .bui-mb-9 { + margin-bottom: var(--bui-space-9); + } + + .bui-mb-10 { + margin-bottom: var(--bui-space-10); + } + + .bui-mb-11 { + margin-bottom: var(--bui-space-11); + } + + .bui-mb-12 { + margin-bottom: var(--bui-space-12); + } + + .bui-mb-13 { + margin-bottom: var(--bui-space-13); + } + + .bui-mb-14 { + margin-bottom: var(--bui-space-14); + } + + @media (width >= 640px) { + .xs\:bui-mb { + margin-bottom: var(--mb-xs); + } + + .xs\:bui-mb-0\.5 { + margin-bottom: var(--bui-space-0_5); + } + + .xs\:bui-mb-1 { + margin-bottom: var(--bui-space-1); + } + + .xs\:bui-mb-1\.5 { + margin-bottom: var(--bui-space-1_5); + } + + .xs\:bui-mb-2 { + margin-bottom: var(--bui-space-2); + } + + .xs\:bui-mb-3 { + margin-bottom: var(--bui-space-3); + } + + .xs\:bui-mb-4 { + margin-bottom: var(--bui-space-4); + } + + .xs\:bui-mb-5 { + margin-bottom: var(--bui-space-5); + } + + .xs\:bui-mb-6 { + margin-bottom: var(--bui-space-6); + } + + .xs\:bui-mb-7 { + margin-bottom: var(--bui-space-7); + } + + .xs\:bui-mb-8 { + margin-bottom: var(--bui-space-8); + } + + .xs\:bui-mb-9 { + margin-bottom: var(--bui-space-9); + } + + .xs\:bui-mb-10 { + margin-bottom: var(--bui-space-10); + } + + .xs\:bui-mb-11 { + margin-bottom: var(--bui-space-11); + } + + .xs\:bui-mb-12 { + margin-bottom: var(--bui-space-12); + } + + .xs\:bui-mb-13 { + margin-bottom: var(--bui-space-13); + } + + .xs\:bui-mb-14 { + margin-bottom: var(--bui-space-14); + } + } + + @media (width >= 768px) { + .sm\:bui-mb { + margin-bottom: var(--mb-sm); + } + + .sm\:bui-mb-0\.5 { + margin-bottom: var(--bui-space-0_5); + } + + .sm\:bui-mb-1 { + margin-bottom: var(--bui-space-1); + } + + .sm\:bui-mb-1\.5 { + margin-bottom: var(--bui-space-1_5); + } + + .sm\:bui-mb-2 { + margin-bottom: var(--bui-space-2); + } + + .sm\:bui-mb-3 { + margin-bottom: var(--bui-space-3); + } + + .sm\:bui-mb-4 { + margin-bottom: var(--bui-space-4); + } + + .sm\:bui-mb-5 { + margin-bottom: var(--bui-space-5); + } + + .sm\:bui-mb-6 { + margin-bottom: var(--bui-space-6); + } + + .sm\:bui-mb-7 { + margin-bottom: var(--bui-space-7); + } + + .sm\:bui-mb-8 { + margin-bottom: var(--bui-space-8); + } + + .sm\:bui-mb-9 { + margin-bottom: var(--bui-space-9); + } + + .sm\:bui-mb-10 { + margin-bottom: var(--bui-space-10); + } + + .sm\:bui-mb-11 { + margin-bottom: var(--bui-space-11); + } + + .sm\:bui-mb-12 { + margin-bottom: var(--bui-space-12); + } + + .sm\:bui-mb-13 { + margin-bottom: var(--bui-space-13); + } + + .sm\:bui-mb-14 { + margin-bottom: var(--bui-space-14); + } + } + + @media (width >= 1024px) { + .md\:bui-mb { + margin-bottom: var(--mb-md); + } + + .md\:bui-mb-0\.5 { + margin-bottom: var(--bui-space-0_5); + } + + .md\:bui-mb-1 { + margin-bottom: var(--bui-space-1); + } + + .md\:bui-mb-1\.5 { + margin-bottom: var(--bui-space-1_5); + } + + .md\:bui-mb-2 { + margin-bottom: var(--bui-space-2); + } + + .md\:bui-mb-3 { + margin-bottom: var(--bui-space-3); + } + + .md\:bui-mb-4 { + margin-bottom: var(--bui-space-4); + } + + .md\:bui-mb-5 { + margin-bottom: var(--bui-space-5); + } + + .md\:bui-mb-6 { + margin-bottom: var(--bui-space-6); + } + + .md\:bui-mb-7 { + margin-bottom: var(--bui-space-7); + } + + .md\:bui-mb-8 { + margin-bottom: var(--bui-space-8); + } + + .md\:bui-mb-9 { + margin-bottom: var(--bui-space-9); + } + + .md\:bui-mb-10 { + margin-bottom: var(--bui-space-10); + } + + .md\:bui-mb-11 { + margin-bottom: var(--bui-space-11); + } + + .md\:bui-mb-12 { + margin-bottom: var(--bui-space-12); + } + + .md\:bui-mb-13 { + margin-bottom: var(--bui-space-13); + } + + .md\:bui-mb-14 { + margin-bottom: var(--bui-space-14); + } + } + + @media (width >= 1280px) { + .lg\:bui-mb { + margin-bottom: var(--mb-lg); + } + + .lg\:bui-mb-0\.5 { + margin-bottom: var(--bui-space-0_5); + } + + .lg\:bui-mb-1 { + margin-bottom: var(--bui-space-1); + } + + .lg\:bui-mb-1\.5 { + margin-bottom: var(--bui-space-1_5); + } + + .lg\:bui-mb-2 { + margin-bottom: var(--bui-space-2); + } + + .lg\:bui-mb-3 { + margin-bottom: var(--bui-space-3); + } + + .lg\:bui-mb-4 { + margin-bottom: var(--bui-space-4); + } + + .lg\:bui-mb-5 { + margin-bottom: var(--bui-space-5); + } + + .lg\:bui-mb-6 { + margin-bottom: var(--bui-space-6); + } + + .lg\:bui-mb-7 { + margin-bottom: var(--bui-space-7); + } + + .lg\:bui-mb-8 { + margin-bottom: var(--bui-space-8); + } + + .lg\:bui-mb-9 { + margin-bottom: var(--bui-space-9); + } + + .lg\:bui-mb-10 { + margin-bottom: var(--bui-space-10); + } + + .lg\:bui-mb-11 { + margin-bottom: var(--bui-space-11); + } + + .lg\:bui-mb-12 { + margin-bottom: var(--bui-space-12); + } + + .lg\:bui-mb-13 { + margin-bottom: var(--bui-space-13); + } + + .lg\:bui-mb-14 { + margin-bottom: var(--bui-space-14); + } + } + + @media (width >= 1536px) { + .xl\:bui-mb { + margin-bottom: var(--mb-xl); + } + + .xl\:bui-mb-0\.5 { + margin-bottom: var(--bui-space-0_5); + } + + .xl\:bui-mb-1 { + margin-bottom: var(--bui-space-1); + } + + .xl\:bui-mb-1\.5 { + margin-bottom: var(--bui-space-1_5); + } + + .xl\:bui-mb-2 { + margin-bottom: var(--bui-space-2); + } + + .xl\:bui-mb-3 { + margin-bottom: var(--bui-space-3); + } + + .xl\:bui-mb-4 { + margin-bottom: var(--bui-space-4); + } + + .xl\:bui-mb-5 { + margin-bottom: var(--bui-space-5); + } + + .xl\:bui-mb-6 { + margin-bottom: var(--bui-space-6); + } + + .xl\:bui-mb-7 { + margin-bottom: var(--bui-space-7); + } + + .xl\:bui-mb-8 { + margin-bottom: var(--bui-space-8); + } + + .xl\:bui-mb-9 { + margin-bottom: var(--bui-space-9); + } + + .xl\:bui-mb-10 { + margin-bottom: var(--bui-space-10); + } + + .xl\:bui-mb-11 { + margin-bottom: var(--bui-space-11); + } + + .xl\:bui-mb-12 { + margin-bottom: var(--bui-space-12); + } + + .xl\:bui-mb-13 { + margin-bottom: var(--bui-space-13); + } + + .xl\:bui-mb-14 { + margin-bottom: var(--bui-space-14); + } + } + + .bui-my { + margin-top: var(--my); + margin-bottom: var(--my); + } + + .bui-my-0\.5 { + margin-top: var(--bui-space-0_5); + margin-bottom: var(--bui-space-0_5); + } + + .bui-my-1 { + margin-top: var(--bui-space-1); + margin-bottom: var(--bui-space-1); + } + + .bui-my-1\.5 { + margin-top: var(--bui-space-1_5); + margin-bottom: var(--bui-space-1_5); + } + + .bui-my-2 { + margin-top: var(--bui-space-2); + margin-bottom: var(--bui-space-2); + } + + .bui-my-3 { + margin-top: var(--bui-space-3); + margin-bottom: var(--bui-space-3); + } + + .bui-my-4 { + margin-top: var(--bui-space-4); + margin-bottom: var(--bui-space-4); + } + + .bui-my-5 { + margin-top: var(--bui-space-5); + margin-bottom: var(--bui-space-5); + } + + .bui-my-6 { + margin-top: var(--bui-space-6); + margin-bottom: var(--bui-space-6); + } + + .bui-my-7 { + margin-top: var(--bui-space-7); + margin-bottom: var(--bui-space-7); + } + + .bui-my-8 { + margin-top: var(--bui-space-8); + margin-bottom: var(--bui-space-8); + } + + .bui-my-9 { + margin-top: var(--bui-space-9); + margin-bottom: var(--bui-space-9); + } + + .bui-my-10 { + margin-top: var(--bui-space-10); + margin-bottom: var(--bui-space-10); + } + + .bui-my-11 { + margin-top: var(--bui-space-11); + margin-bottom: var(--bui-space-11); + } + + .bui-my-12 { + margin-top: var(--bui-space-12); + margin-bottom: var(--bui-space-12); + } + + .bui-my-13 { + margin-top: var(--bui-space-13); + margin-bottom: var(--bui-space-13); + } + + .bui-my-14 { + margin-top: var(--bui-space-14); + margin-bottom: var(--bui-space-14); + } + + @media (width >= 640px) { + .xs\:bui-my { + margin-top: var(--my-xs); + margin-bottom: var(--my-xs); + } + + .xs\:bui-my-0\.5 { + margin-top: var(--bui-space-0_5); + margin-bottom: var(--bui-space-0_5); + } + + .xs\:bui-my-1 { + margin-top: var(--bui-space-1); + margin-bottom: var(--bui-space-1); + } + + .xs\:bui-my-1\.5 { + margin-top: var(--bui-space-1_5); + margin-bottom: var(--bui-space-1_5); + } + + .xs\:bui-my-2 { + margin-top: var(--bui-space-2); + margin-bottom: var(--bui-space-2); + } + + .xs\:bui-my-3 { + margin-top: var(--bui-space-3); + margin-bottom: var(--bui-space-3); + } + + .xs\:bui-my-4 { + margin-top: var(--bui-space-4); + margin-bottom: var(--bui-space-4); + } + + .xs\:bui-my-5 { + margin-top: var(--bui-space-5); + margin-bottom: var(--bui-space-5); + } + + .xs\:bui-my-6 { + margin-top: var(--bui-space-6); + margin-bottom: var(--bui-space-6); + } + + .xs\:bui-my-7 { + margin-top: var(--bui-space-7); + margin-bottom: var(--bui-space-7); + } + + .xs\:bui-my-8 { + margin-top: var(--bui-space-8); + margin-bottom: var(--bui-space-8); + } + + .xs\:bui-my-9 { + margin-top: var(--bui-space-9); + margin-bottom: var(--bui-space-9); + } + + .xs\:bui-my-10 { + margin-top: var(--bui-space-10); + margin-bottom: var(--bui-space-10); + } + + .xs\:bui-my-11 { + margin-top: var(--bui-space-11); + margin-bottom: var(--bui-space-11); + } + + .xs\:bui-my-12 { + margin-top: var(--bui-space-12); + margin-bottom: var(--bui-space-12); + } + + .xs\:bui-my-13 { + margin-top: var(--bui-space-13); + margin-bottom: var(--bui-space-13); + } + + .xs\:bui-my-14 { + margin-top: var(--bui-space-14); + margin-bottom: var(--bui-space-14); + } + } + + @media (width >= 768px) { + .sm\:bui-my { + margin-top: var(--my-sm); + margin-bottom: var(--my-sm); + } + + .sm\:bui-my-0\.5 { + margin-top: var(--bui-space-0_5); + margin-bottom: var(--bui-space-0_5); + } + + .sm\:bui-my-1 { + margin-top: var(--bui-space-1); + margin-bottom: var(--bui-space-1); + } + + .sm\:bui-my-1\.5 { + margin-top: var(--bui-space-1_5); + margin-bottom: var(--bui-space-1_5); + } + + .sm\:bui-my-2 { + margin-top: var(--bui-space-2); + margin-bottom: var(--bui-space-2); + } + + .sm\:bui-my-3 { + margin-top: var(--bui-space-3); + margin-bottom: var(--bui-space-3); + } + + .sm\:bui-my-4 { + margin-top: var(--bui-space-4); + margin-bottom: var(--bui-space-4); + } + + .sm\:bui-my-5 { + margin-top: var(--bui-space-5); + margin-bottom: var(--bui-space-5); + } + + .sm\:bui-my-6 { + margin-top: var(--bui-space-6); + margin-bottom: var(--bui-space-6); + } + + .sm\:bui-my-7 { + margin-top: var(--bui-space-7); + margin-bottom: var(--bui-space-7); + } + + .sm\:bui-my-8 { + margin-top: var(--bui-space-8); + margin-bottom: var(--bui-space-8); + } + + .sm\:bui-my-9 { + margin-top: var(--bui-space-9); + margin-bottom: var(--bui-space-9); + } + + .sm\:bui-my-10 { + margin-top: var(--bui-space-10); + margin-bottom: var(--bui-space-10); + } + + .sm\:bui-my-11 { + margin-top: var(--bui-space-11); + margin-bottom: var(--bui-space-11); + } + + .sm\:bui-my-12 { + margin-top: var(--bui-space-12); + margin-bottom: var(--bui-space-12); + } + + .sm\:bui-my-13 { + margin-top: var(--bui-space-13); + margin-bottom: var(--bui-space-13); + } + + .sm\:bui-my-14 { + margin-top: var(--bui-space-14); + margin-bottom: var(--bui-space-14); + } + } + + @media (width >= 1024px) { + .md\:bui-my { + margin-top: var(--my-md); + margin-bottom: var(--my-md); + } + + .md\:bui-my-0\.5 { + margin-top: var(--bui-space-0_5); + margin-bottom: var(--bui-space-0_5); + } + + .md\:bui-my-1 { + margin-top: var(--bui-space-1); + margin-bottom: var(--bui-space-1); + } + + .md\:bui-my-1\.5 { + margin-top: var(--bui-space-1_5); + margin-bottom: var(--bui-space-1_5); + } + + .md\:bui-my-2 { + margin-top: var(--bui-space-2); + margin-bottom: var(--bui-space-2); + } + + .md\:bui-my-3 { + margin-top: var(--bui-space-3); + margin-bottom: var(--bui-space-3); + } + + .md\:bui-my-4 { + margin-top: var(--bui-space-4); + margin-bottom: var(--bui-space-4); + } + + .md\:bui-my-5 { + margin-top: var(--bui-space-5); + margin-bottom: var(--bui-space-5); + } + + .md\:bui-my-6 { + margin-top: var(--bui-space-6); + margin-bottom: var(--bui-space-6); + } + + .md\:bui-my-7 { + margin-top: var(--bui-space-7); + margin-bottom: var(--bui-space-7); + } + + .md\:bui-my-8 { + margin-top: var(--bui-space-8); + margin-bottom: var(--bui-space-8); + } + + .md\:bui-my-9 { + margin-top: var(--bui-space-9); + margin-bottom: var(--bui-space-9); + } + + .md\:bui-my-10 { + margin-top: var(--bui-space-10); + margin-bottom: var(--bui-space-10); + } + + .md\:bui-my-11 { + margin-top: var(--bui-space-11); + margin-bottom: var(--bui-space-11); + } + + .md\:bui-my-12 { + margin-top: var(--bui-space-12); + margin-bottom: var(--bui-space-12); + } + + .md\:bui-my-13 { + margin-top: var(--bui-space-13); + margin-bottom: var(--bui-space-13); + } + + .md\:bui-my-14 { + margin-top: var(--bui-space-14); + margin-bottom: var(--bui-space-14); + } + } + + @media (width >= 1280px) { + .lg\:bui-my { + margin-top: var(--my-lg); + margin-bottom: var(--my-lg); + } + + .lg\:bui-my-0\.5 { + margin-top: var(--bui-space-0_5); + margin-bottom: var(--bui-space-0_5); + } + + .lg\:bui-my-1 { + margin-top: var(--bui-space-1); + margin-bottom: var(--bui-space-1); + } + + .lg\:bui-my-1\.5 { + margin-top: var(--bui-space-1_5); + margin-bottom: var(--bui-space-1_5); + } + + .lg\:bui-my-2 { + margin-top: var(--bui-space-2); + margin-bottom: var(--bui-space-2); + } + + .lg\:bui-my-3 { + margin-top: var(--bui-space-3); + margin-bottom: var(--bui-space-3); + } + + .lg\:bui-my-4 { + margin-top: var(--bui-space-4); + margin-bottom: var(--bui-space-4); + } + + .lg\:bui-my-5 { + margin-top: var(--bui-space-5); + margin-bottom: var(--bui-space-5); + } + + .lg\:bui-my-6 { + margin-top: var(--bui-space-6); + margin-bottom: var(--bui-space-6); + } + + .lg\:bui-my-7 { + margin-top: var(--bui-space-7); + margin-bottom: var(--bui-space-7); + } + + .lg\:bui-my-8 { + margin-top: var(--bui-space-8); + margin-bottom: var(--bui-space-8); + } + + .lg\:bui-my-9 { + margin-top: var(--bui-space-9); + margin-bottom: var(--bui-space-9); + } + + .lg\:bui-my-10 { + margin-top: var(--bui-space-10); + margin-bottom: var(--bui-space-10); + } + + .lg\:bui-my-11 { + margin-top: var(--bui-space-11); + margin-bottom: var(--bui-space-11); + } + + .lg\:bui-my-12 { + margin-top: var(--bui-space-12); + margin-bottom: var(--bui-space-12); + } + + .lg\:bui-my-13 { + margin-top: var(--bui-space-13); + margin-bottom: var(--bui-space-13); + } + + .lg\:bui-my-14 { + margin-top: var(--bui-space-14); + margin-bottom: var(--bui-space-14); + } + } + + @media (width >= 1536px) { + .xl\:bui-my { + margin-top: var(--my-xl); + margin-bottom: var(--my-xl); + } + + .xl\:bui-my-0\.5 { + margin-top: var(--bui-space-0_5); + margin-bottom: var(--bui-space-0_5); + } + + .xl\:bui-my-1 { + margin-top: var(--bui-space-1); + margin-bottom: var(--bui-space-1); + } + + .xl\:bui-my-1\.5 { + margin-top: var(--bui-space-1_5); + margin-bottom: var(--bui-space-1_5); + } + + .xl\:bui-my-2 { + margin-top: var(--bui-space-2); + margin-bottom: var(--bui-space-2); + } + + .xl\:bui-my-3 { + margin-top: var(--bui-space-3); + margin-bottom: var(--bui-space-3); + } + + .xl\:bui-my-4 { + margin-top: var(--bui-space-4); + margin-bottom: var(--bui-space-4); + } + + .xl\:bui-my-5 { + margin-top: var(--bui-space-5); + margin-bottom: var(--bui-space-5); + } + + .xl\:bui-my-6 { + margin-top: var(--bui-space-6); + margin-bottom: var(--bui-space-6); + } + + .xl\:bui-my-7 { + margin-top: var(--bui-space-7); + margin-bottom: var(--bui-space-7); + } + + .xl\:bui-my-8 { + margin-top: var(--bui-space-8); + margin-bottom: var(--bui-space-8); + } + + .xl\:bui-my-9 { + margin-top: var(--bui-space-9); + margin-bottom: var(--bui-space-9); + } + + .xl\:bui-my-10 { + margin-top: var(--bui-space-10); + margin-bottom: var(--bui-space-10); + } + + .xl\:bui-my-11 { + margin-top: var(--bui-space-11); + margin-bottom: var(--bui-space-11); + } + + .xl\:bui-my-12 { + margin-top: var(--bui-space-12); + margin-bottom: var(--bui-space-12); + } + + .xl\:bui-my-13 { + margin-top: var(--bui-space-13); + margin-bottom: var(--bui-space-13); + } + + .xl\:bui-my-14 { + margin-top: var(--bui-space-14); + margin-bottom: var(--bui-space-14); + } + } + + .bui-mx { + margin-left: var(--mx); + margin-right: var(--mx); + } + + .bui-mx-0\.5 { + margin-left: var(--bui-space-0_5); + margin-right: var(--bui-space-0_5); + } + + .bui-mx-1 { + margin-left: var(--bui-space-1); + margin-right: var(--bui-space-1); + } + + .bui-mx-1\.5 { + margin-left: var(--bui-space-1_5); + margin-right: var(--bui-space-1_5); + } + + .bui-mx-2 { + margin-left: var(--bui-space-2); + margin-right: var(--bui-space-2); + } + + .bui-mx-3 { + margin-left: var(--bui-space-3); + margin-right: var(--bui-space-3); + } + + .bui-mx-4 { + margin-left: var(--bui-space-4); + margin-right: var(--bui-space-4); + } + + .bui-mx-5 { + margin-left: var(--bui-space-5); + margin-right: var(--bui-space-5); + } + + .bui-mx-6 { + margin-left: var(--bui-space-6); + margin-right: var(--bui-space-6); + } + + .bui-mx-7 { + margin-left: var(--bui-space-7); + margin-right: var(--bui-space-7); + } + + .bui-mx-8 { + margin-left: var(--bui-space-8); + margin-right: var(--bui-space-8); + } + + .bui-mx-9 { + margin-left: var(--bui-space-9); + margin-right: var(--bui-space-9); + } + + .bui-mx-10 { + margin-left: var(--bui-space-10); + margin-right: var(--bui-space-10); + } + + .bui-mx-11 { + margin-left: var(--bui-space-11); + margin-right: var(--bui-space-11); + } + + .bui-mx-12 { + margin-left: var(--bui-space-12); + margin-right: var(--bui-space-12); + } + + .bui-mx-13 { + margin-left: var(--bui-space-13); + margin-right: var(--bui-space-13); + } + + .bui-mx-14 { + margin-left: var(--bui-space-14); + margin-right: var(--bui-space-14); + } + + @media (width >= 640px) { + .xs\:bui-mx { + margin-left: var(--mx-xs); + margin-right: var(--mx-xs); + } + + .xs\:bui-mx-0\.5 { + margin-left: var(--bui-space-0_5); + margin-right: var(--bui-space-0_5); + } + + .xs\:bui-mx-1 { + margin-left: var(--bui-space-1); + margin-right: var(--bui-space-1); + } + + .xs\:bui-mx-1\.5 { + margin-left: var(--bui-space-1_5); + margin-right: var(--bui-space-1_5); + } + + .xs\:bui-mx-2 { + margin-left: var(--bui-space-2); + margin-right: var(--bui-space-2); + } + + .xs\:bui-mx-3 { + margin-left: var(--bui-space-3); + margin-right: var(--bui-space-3); + } + + .xs\:bui-mx-4 { + margin-left: var(--bui-space-4); + margin-right: var(--bui-space-4); + } + + .xs\:bui-mx-5 { + margin-left: var(--bui-space-5); + margin-right: var(--bui-space-5); + } + + .xs\:bui-mx-6 { + margin-left: var(--bui-space-6); + margin-right: var(--bui-space-6); + } + + .xs\:bui-mx-7 { + margin-left: var(--bui-space-7); + margin-right: var(--bui-space-7); + } + + .xs\:bui-mx-8 { + margin-left: var(--bui-space-8); + margin-right: var(--bui-space-8); + } + + .xs\:bui-mx-9 { + margin-left: var(--bui-space-9); + margin-right: var(--bui-space-9); + } + + .xs\:bui-mx-10 { + margin-left: var(--bui-space-10); + margin-right: var(--bui-space-10); + } + + .xs\:bui-mx-11 { + margin-left: var(--bui-space-11); + margin-right: var(--bui-space-11); + } + + .xs\:bui-mx-12 { + margin-left: var(--bui-space-12); + margin-right: var(--bui-space-12); + } + + .xs\:bui-mx-13 { + margin-left: var(--bui-space-13); + margin-right: var(--bui-space-13); + } + + .xs\:bui-mx-14 { + margin-left: var(--bui-space-14); + margin-right: var(--bui-space-14); + } + } + + @media (width >= 768px) { + .sm\:bui-mx { + margin-left: var(--mx-sm); + margin-right: var(--mx-sm); + } + + .sm\:bui-mx-0\.5 { + margin-left: var(--bui-space-0_5); + margin-right: var(--bui-space-0_5); + } + + .sm\:bui-mx-1 { + margin-left: var(--bui-space-1); + margin-right: var(--bui-space-1); + } + + .sm\:bui-mx-1\.5 { + margin-left: var(--bui-space-1_5); + margin-right: var(--bui-space-1_5); + } + + .sm\:bui-mx-2 { + margin-left: var(--bui-space-2); + margin-right: var(--bui-space-2); + } + + .sm\:bui-mx-3 { + margin-left: var(--bui-space-3); + margin-right: var(--bui-space-3); + } + + .sm\:bui-mx-4 { + margin-left: var(--bui-space-4); + margin-right: var(--bui-space-4); + } + + .sm\:bui-mx-5 { + margin-left: var(--bui-space-5); + margin-right: var(--bui-space-5); + } + + .sm\:bui-mx-6 { + margin-left: var(--bui-space-6); + margin-right: var(--bui-space-6); + } + + .sm\:bui-mx-7 { + margin-left: var(--bui-space-7); + margin-right: var(--bui-space-7); + } + + .sm\:bui-mx-8 { + margin-left: var(--bui-space-8); + margin-right: var(--bui-space-8); + } + + .sm\:bui-mx-9 { + margin-left: var(--bui-space-9); + margin-right: var(--bui-space-9); + } + + .sm\:bui-mx-10 { + margin-left: var(--bui-space-10); + margin-right: var(--bui-space-10); + } + + .sm\:bui-mx-11 { + margin-left: var(--bui-space-11); + margin-right: var(--bui-space-11); + } + + .sm\:bui-mx-12 { + margin-left: var(--bui-space-12); + margin-right: var(--bui-space-12); + } + + .sm\:bui-mx-13 { + margin-left: var(--bui-space-13); + margin-right: var(--bui-space-13); + } + + .sm\:bui-mx-14 { + margin-left: var(--bui-space-14); + margin-right: var(--bui-space-14); + } + } + + @media (width >= 1024px) { + .md\:bui-mx { + margin-left: var(--mx-md); + margin-right: var(--mx-md); + } + + .md\:bui-mx-0\.5 { + margin-left: var(--bui-space-0_5); + margin-right: var(--bui-space-0_5); + } + + .md\:bui-mx-1 { + margin-left: var(--bui-space-1); + margin-right: var(--bui-space-1); + } + + .md\:bui-mx-1\.5 { + margin-left: var(--bui-space-1_5); + margin-right: var(--bui-space-1_5); + } + + .md\:bui-mx-2 { + margin-left: var(--bui-space-2); + margin-right: var(--bui-space-2); + } + + .md\:bui-mx-3 { + margin-left: var(--bui-space-3); + margin-right: var(--bui-space-3); + } + + .md\:bui-mx-4 { + margin-left: var(--bui-space-4); + margin-right: var(--bui-space-4); + } + + .md\:bui-mx-5 { + margin-left: var(--bui-space-5); + margin-right: var(--bui-space-5); + } + + .md\:bui-mx-6 { + margin-left: var(--bui-space-6); + margin-right: var(--bui-space-6); + } + + .md\:bui-mx-7 { + margin-left: var(--bui-space-7); + margin-right: var(--bui-space-7); + } + + .md\:bui-mx-8 { + margin-left: var(--bui-space-8); + margin-right: var(--bui-space-8); + } + + .md\:bui-mx-9 { + margin-left: var(--bui-space-9); + margin-right: var(--bui-space-9); + } + + .md\:bui-mx-10 { + margin-left: var(--bui-space-10); + margin-right: var(--bui-space-10); + } + + .md\:bui-mx-11 { + margin-left: var(--bui-space-11); + margin-right: var(--bui-space-11); + } + + .md\:bui-mx-12 { + margin-left: var(--bui-space-12); + margin-right: var(--bui-space-12); + } + + .md\:bui-mx-13 { + margin-left: var(--bui-space-13); + margin-right: var(--bui-space-13); + } + + .md\:bui-mx-14 { + margin-left: var(--bui-space-14); + margin-right: var(--bui-space-14); + } + } + + @media (width >= 1280px) { + .lg\:bui-mx { + margin-left: var(--mx-lg); + margin-right: var(--mx-lg); + } + + .lg\:bui-mx-0\.5 { + margin-left: var(--bui-space-0_5); + margin-right: var(--bui-space-0_5); + } + + .lg\:bui-mx-1 { + margin-left: var(--bui-space-1); + margin-right: var(--bui-space-1); + } + + .lg\:bui-mx-1\.5 { + margin-left: var(--bui-space-1_5); + margin-right: var(--bui-space-1_5); + } + + .lg\:bui-mx-2 { + margin-left: var(--bui-space-2); + margin-right: var(--bui-space-2); + } + + .lg\:bui-mx-3 { + margin-left: var(--bui-space-3); + margin-right: var(--bui-space-3); + } + + .lg\:bui-mx-4 { + margin-left: var(--bui-space-4); + margin-right: var(--bui-space-4); + } + + .lg\:bui-mx-5 { + margin-left: var(--bui-space-5); + margin-right: var(--bui-space-5); + } + + .lg\:bui-mx-6 { + margin-left: var(--bui-space-6); + margin-right: var(--bui-space-6); + } + + .lg\:bui-mx-7 { + margin-left: var(--bui-space-7); + margin-right: var(--bui-space-7); + } + + .lg\:bui-mx-8 { + margin-left: var(--bui-space-8); + margin-right: var(--bui-space-8); + } + + .lg\:bui-mx-9 { + margin-left: var(--bui-space-9); + margin-right: var(--bui-space-9); + } + + .lg\:bui-mx-10 { + margin-left: var(--bui-space-10); + margin-right: var(--bui-space-10); + } + + .lg\:bui-mx-11 { + margin-left: var(--bui-space-11); + margin-right: var(--bui-space-11); + } + + .lg\:bui-mx-12 { + margin-left: var(--bui-space-12); + margin-right: var(--bui-space-12); + } + + .lg\:bui-mx-13 { + margin-left: var(--bui-space-13); + margin-right: var(--bui-space-13); + } + + .lg\:bui-mx-14 { + margin-left: var(--bui-space-14); + margin-right: var(--bui-space-14); + } + } + + @media (width >= 1536px) { + .xl\:bui-mx { + margin-left: var(--mx-xl); + margin-right: var(--mx-xl); + } + + .xl\:bui-mx-0\.5 { + margin-left: var(--bui-space-0_5); + margin-right: var(--bui-space-0_5); + } + + .xl\:bui-mx-1 { + margin-left: var(--bui-space-1); + margin-right: var(--bui-space-1); + } + + .xl\:bui-mx-1\.5 { + margin-left: var(--bui-space-1_5); + margin-right: var(--bui-space-1_5); + } + + .xl\:bui-mx-2 { + margin-left: var(--bui-space-2); + margin-right: var(--bui-space-2); + } + + .xl\:bui-mx-3 { + margin-left: var(--bui-space-3); + margin-right: var(--bui-space-3); + } + + .xl\:bui-mx-4 { + margin-left: var(--bui-space-4); + margin-right: var(--bui-space-4); + } + + .xl\:bui-mx-5 { + margin-left: var(--bui-space-5); + margin-right: var(--bui-space-5); + } + + .xl\:bui-mx-6 { + margin-left: var(--bui-space-6); + margin-right: var(--bui-space-6); + } + + .xl\:bui-mx-7 { + margin-left: var(--bui-space-7); + margin-right: var(--bui-space-7); + } + + .xl\:bui-mx-8 { + margin-left: var(--bui-space-8); + margin-right: var(--bui-space-8); + } + + .xl\:bui-mx-9 { + margin-left: var(--bui-space-9); + margin-right: var(--bui-space-9); + } + + .xl\:bui-mx-10 { + margin-left: var(--bui-space-10); + margin-right: var(--bui-space-10); + } + + .xl\:bui-mx-11 { + margin-left: var(--bui-space-11); + margin-right: var(--bui-space-11); + } + + .xl\:bui-mx-12 { + margin-left: var(--bui-space-12); + margin-right: var(--bui-space-12); + } + + .xl\:bui-mx-13 { + margin-left: var(--bui-space-13); + margin-right: var(--bui-space-13); + } + + .xl\:bui-mx-14 { + margin-left: var(--bui-space-14); + margin-right: var(--bui-space-14); + } + } + + .bui-display-none { + display: none; + } + + .bui-display-inline { + display: inline; + } + + .bui-display-inline-block { + display: inline-block; + } + + .bui-display-block { + display: block; + } + + @media (width >= 640px) { + .xs\:bui-display-none { + display: none; + } + + .xs\:bui-display-inline { + display: inline; + } + + .xs\:bui-display-inline-block { + display: inline-block; + } + + .xs\:bui-display-block { display: block; } } -} -.bui-MenuItemListBox { - height: 2rem; - padding-inline: var(--bui-space-2); - border-radius: var(--bui-radius-2); - cursor: default; - color: var(--bui-fg-primary); - font-size: var(--bui-font-size-3); - justify-content: space-between; - align-items: center; - gap: var(--bui-space-6); - outline: none; - display: flex; - - &:hover { - background: var(--bui-bg-surface-2); - color: var(--bui-fg-primary); - } - - &[data-selected] .bui-MenuItemListBoxCheck { - & > svg { - opacity: 1; - color: var(--bui-fg-primary); - } - } -} - -.bui-MenuItemListBoxCheck { - justify-content: center; - align-items: center; - width: 1rem; - height: 1rem; - display: flex; - - & > svg { - opacity: 0; - width: 1rem; - height: 1rem; - } -} - -.bui-MenuItemContent { - align-items: center; - gap: var(--bui-space-2); - display: flex; - - & > svg { - width: 1rem; - height: 1rem; - } -} - -.bui-MenuItemArrow { - width: 1rem; - height: 1rem; - display: none; - - & > svg { - width: 1rem; - height: 1rem; - } -} - -.bui-MenuSection { - &:first-child .bui-MenuSectionHeader { - padding-top: 0; - } -} - -.bui-MenuSectionHeader { - height: 2rem; - padding-top: var(--bui-space-3); - padding-left: var(--bui-space-2); - color: var(--bui-fg-primary); - font-size: var(--bui-font-size-1); - letter-spacing: .05rem; - text-transform: uppercase; - align-items: center; - font-weight: bold; - display: flex; -} - -.bui-MenuSeparator { - background: var(--bui-border); - height: 1px; - margin-inline: var(--bui-space-1_5); - margin-block: var(--bui-space-1); -} - -.bui-MenuSearchField { - font-family: var(--bui-font-regular); - flex-shrink: 0; - width: 100%; - position: relative; - - &[data-empty] { - & .bui-MenuSearchFieldClear { + @media (width >= 768px) { + .sm\:bui-display-none { display: none; } - } -} -.bui-MenuSearchFieldInput { - padding: 0 var(--bui-space-3); - border: none; - border-bottom: 1px solid var(--bui-border); - background-color: var(--bui-bg-surface-1); - font-size: var(--bui-font-size-3); - font-family: var(--bui-font-regular); - font-weight: var(--bui-font-weight-regular); - color: var(--bui-fg-primary); - width: 100%; - height: 2rem; - cursor: inherit; - outline: none; - align-items: center; - display: flex; - - &::-webkit-search-cancel-button, &::-webkit-search-decoration { - -webkit-appearance: none; - } -} - -.bui-MenuSearchFieldClear { - right: var(--bui-space-2); - cursor: pointer; - color: var(--bui-fg-secondary); - background-color: #0000; - border: none; - justify-content: center; - align-items: center; - margin: 0; - padding: 0; - transition: color .2s ease-in-out; - display: flex; - position: absolute; - top: 0; - bottom: 0; - - & > svg { - width: 1rem; - height: 1rem; - } -} - -.bui-MenuEmptyState { - padding: var(--bui-space-1); - color: var(--bui-fg-secondary); - font-size: var(--bui-font-size-3); - font-family: var(--bui-font-regular); - font-weight: var(--bui-font-weight-regular); -} - -.bui-Popover { - background-color: var(--bui-bg-surface-1); - border: 1px solid var(--bui-border); - border-radius: var(--bui-radius-3); - padding-block: var(--bui-space-1); - margin-right: 12px; - overflow: scroll; - box-shadow: 0 4px 12px #0000001a; -} - -.bui-RadioGroup { - color: var(--bui-fg-primary); - flex-direction: column; - display: flex; -} - -.bui-RadioGroup[data-orientation="horizontal"] .bui-RadioGroupContent { - gap: var(--bui-space-4); - flex-direction: row; -} - -.bui-RadioGroupContent { - gap: var(--bui-space-2); - flex-direction: column; - display: flex; -} - -.bui-Radio { - align-items: center; - gap: var(--bui-space-2); - font-size: var(--bui-font-size-2); - color: var(--bui-fg-primary); - forced-color-adjust: none; - display: flex; - position: relative; - - &:before { - content: ""; - box-sizing: border-box; - border: .125rem solid var(--bui-border); - background: var(--bui-gray-1); - border-radius: var(--bui-radius-full); - width: 1rem; - height: 1rem; - transition: all .2s; - display: block; - } - - &[data-pressed]:before { - border-color: var(--bui-border); - } - - &[data-selected] { - &:before { - border-color: var(--bui-bg-solid); - border-width: .25rem; + .sm\:bui-display-inline { + display: inline; } - &[data-pressed]:before { - border-color: var(--bui-bg-solid); + .sm\:bui-display-inline-block { + display: inline-block; + } + + .sm\:bui-display-block { + display: block; } } - &[data-focus-visible]:before { - outline: 2px solid var(--bui-ring); - outline-offset: 2px; - } - - &[data-disabled] { - cursor: not-allowed; - color: var(--bui-fg-disabled); - - &:before { - border-color: var(--bui-border-disabled); - background: var(--bui-bg-disabled); - } - - &[data-selected]:before { - border-color: var(--bui-border-disabled); - } - } - - &[data-invalid]:before, &[data-invalid][data-selected]:before { - border-color: var(--bui-border-danger); - } - - &[data-disabled][data-invalid] { - color: var(--bui-fg-disabled); - - &:before { - border-color: var(--bui-border-disabled); - background: var(--bui-bg-disabled); - } - - &[data-selected]:before { - border-color: var(--bui-border-disabled); - } - } -} - -.bui-Table { - caption-side: bottom; - border-collapse: collapse; - width: 100%; -} - -.bui-TableHeader { - border-bottom: 1px solid var(--bui-border); - transition: color .2s ease-in-out; -} - -.bui-TableHead { - text-align: left; - padding: var(--bui-space-3); - font-size: var(--bui-font-size-3); - color: var(--bui-fg-primary); -} - -.bui-TableHeadSortButton { - cursor: pointer; - user-select: none; - align-items: center; - gap: var(--bui-space-1); - display: inline-flex; - - &:hover svg { - opacity: .5; - } - - & svg { - opacity: 0; - transition: opacity .1s ease-in-out, transform .1s ease-in-out; - } - - &[data-sort-order="asc"] svg { - opacity: 1; - transform: rotate(0); - } - - &[data-sort-order="desc"] svg { - opacity: 1; - transform: rotate(180deg); - } -} - -.bui-TableBody { - color: var(--bui-fg-primary); -} - -.bui-TableRow { - border-bottom: 1px solid var(--bui-border); - transition: color .2s ease-in-out; - - &[data-react-aria-pressable="true"] { - cursor: pointer; - } -} - -.bui-TableBody .bui-TableRow:hover { - background-color: var(--bui-gray-2); -} - -.bui-TableCell { - padding: var(--bui-space-3); - font-size: var(--bui-font-size-3); - padding: var(--bui-space-3); - font-size: var(--bui-font-size-3); -} - -.bui-TableCellContentWrapper { - align-items: center; - gap: var(--bui-space-2); - flex-direction: row; - display: inline-flex; -} - -.bui-TableCellIcon, .bui-TableCellIcon svg { - color: var(--bui-fg-primary); - align-items: center; - display: inline-flex; -} - -.bui-TableCellContent { - gap: var(--bui-space-0_5); - flex-direction: column; - display: flex; -} - -.bui-TableCellProfile { - gap: var(--bui-space-2); - flex-direction: row; - align-items: center; - display: flex; -} - -.bui-TableCellProfileAvatar { - vertical-align: middle; - user-select: none; - color: var(--bui-fg-primary); - background-color: var(--bui-bg-surface-2); - border-radius: 100%; - justify-content: center; - align-items: center; - width: 1.25rem; - height: 1.25rem; - font-size: 1rem; - font-weight: 500; - line-height: 1; - display: inline-flex; - overflow: hidden; -} - -.bui-TableCellProfileAvatarImage { - object-fit: cover; - width: 100%; - height: 100%; -} - -.bui-TableCellProfileAvatarFallback { - width: 100%; - height: 100%; - font-size: var(--bui-font-size-2); - font-weight: var(--bui-font-weight-regular); - box-shadow: inset 0 0 0 1px var(--bui-border); - border-radius: var(--bui-radius-full); - justify-content: center; - align-items: center; - display: flex; -} - -.bui-DataTablePagination { - padding-top: var(--bui-space-5); - justify-content: space-between; - align-items: center; - display: flex; -} - -.bui-DataTablePagination--left { - justify-content: space-between; - align-items: center; - display: flex; -} - -.bui-DataTablePagination--right { - justify-content: space-between; - align-items: center; - gap: var(--bui-space-2); - display: flex; -} - -.bui-DataTablePagination--select { - min-width: 10.5rem; -} - -.bui-Tabs { - --active-tab-left: 0px; - --active-tab-right: 0px; - --active-tab-top: 0px; - --active-tab-bottom: 0px; - --active-tab-width: 0px; - --active-tab-height: 0px; - --active-transition-duration: 0s; - --hovered-tab-left: 0px; - --hovered-tab-right: 0px; - --hovered-tab-top: 0px; - --hovered-tab-bottom: 0px; - --hovered-tab-width: 0px; - --hovered-tab-height: 0px; - --hovered-tab-opacity: 0; - --hovered-transition-duration: 0s; -} - -.bui-TabList { - flex-direction: row; - display: flex; -} - -.bui-TabListWrapper { - position: relative; -} - -.bui-Tab { - font-size: var(--bui-font-size-3); - font-family: var(--bui-font-regular); - font-weight: var(--bui-font-weight-regular); - color: var(--bui-fg-secondary); - cursor: pointer; - z-index: 2; - height: 36px; - padding-inline: var(--bui-space-2); - justify-content: center; - align-items: center; - display: flex; - position: relative; - - &[data-selected="true"] { - color: var(--bui-fg-primary); - } -} - -.bui-TabActive { - content: ""; - left: calc(var(--active-tab-left) + var(--bui-space-2)); - width: calc(var(--active-tab-width) - var(--bui-space-4)); - background-color: var(--bui-fg-primary); - height: 1px; - transition: left var(--active-transition-duration) ease-out, opacity .15s ease-out, width var(--active-transition-duration) ease-out; - opacity: 1; - border-radius: 4px; - position: absolute; - bottom: -1px; -} - -.bui-TabHovered { - content: ""; - left: var(--hovered-tab-left); - top: calc(var(--hovered-tab-top) + 4px); - width: var(--hovered-tab-width); - height: calc(var(--hovered-tab-height) - 8px); - background-color: var(--bui-gray-2); - opacity: var(--hovered-tab-opacity); - transition: left var(--hovered-transition-duration) ease-out, top var(--hovered-transition-duration) ease-out, width var(--hovered-transition-duration) ease-out, height var(--hovered-transition-duration) ease-out, opacity .15s ease-out; - border-radius: 4px; - position: absolute; -} - -.bui-TabPanel { - padding-inline: var(--bui-space-2); - padding-top: var(--bui-space-4); -} - -.bui-TagList { - gap: var(--bui-space-2); - flex-wrap: wrap; - display: flex; -} - -.bui-Tag { - color: var(--bui-fg-primary); - background-color: var(--bui-gray-2); - border-radius: var(--bui-radius-2); - font-weight: var(--bui-font-weight-regular); - justify-content: center; - align-items: center; - gap: var(--bui-space-1); - box-sizing: border-box; - transition-property: background-color, box-shadow, color; - transition-duration: .2s; - transition-timing-function: ease-in-out; - display: flex; -} - -.bui-Tag[data-size="small"] { - height: 26px; - padding: 0 var(--bui-space-2); - font-size: var(--bui-font-size-1); -} - -.bui-Tag[data-size="medium"] { - height: 32px; - padding: 0 var(--bui-space-2); - font-size: var(--bui-font-size-2); -} - -.bui-Tag[data-hovered] { - background-color: var(--bui-gray-3); - cursor: pointer; -} - -.bui-Tag[data-focus-visible] { - outline: 2px solid var(--bui-ring); - outline-offset: 1px; -} - -.bui-Tag[data-selected] { - box-shadow: inset 0 0 0 1px var(--bui-gray-8); -} - -.bui-Tag[data-disabled] { - color: var(--bui-fg-disabled); - cursor: not-allowed; -} - -.bui-TagRemoveButton { - cursor: pointer; - color: var(--bui-fg-primary); - background-color: #0000; - border: none; - width: 1rem; - height: 1rem; - margin: 0; - padding: 0; -} - -.bui-TagIcon { - justify-content: center; - align-items: center; - transition: color .2s ease-in-out; - display: flex; - - & svg { - width: 1rem; - height: 1rem; - } -} - -.bui-Text { - font-family: var(--bui-font-regular); - margin: 0; - padding: 0; -} - -.bui-Text[data-variant="title-large"] { - font-size: var(--bui-font-size-8); - line-height: 140%; -} - -.bui-Text[data-variant="title-medium"] { - font-size: var(--bui-font-size-7); - line-height: 140%; -} - -.bui-Text[data-variant="title-small"] { - font-size: var(--bui-font-size-6); - line-height: 140%; -} - -.bui-Text[data-variant="title-x-small"] { - font-size: var(--bui-font-size-5); - line-height: 140%; -} - -.bui-Text[data-variant="body-large"] { - font-size: var(--bui-font-size-4); - line-height: 140%; -} - -.bui-Text[data-variant="body-medium"] { - font-size: var(--bui-font-size-3); - line-height: 140%; -} - -.bui-Text[data-variant="body-small"] { - font-size: var(--bui-font-size-2); - line-height: 140%; -} - -.bui-Text[data-variant="body-x-small"] { - font-size: var(--bui-font-size-1); - line-height: 140%; -} - -.bui-Text[data-weight="regular"] { - font-weight: var(--bui-font-weight-regular); -} - -.bui-Text[data-weight="bold"] { - font-weight: var(--bui-font-weight-bold); -} - -.bui-Text[data-color="primary"] { - color: var(--bui-fg-primary); -} - -.bui-Text[data-color="secondary"] { - color: var(--bui-fg-secondary); -} - -.bui-Text[data-color="danger"] { - color: var(--bui-fg-danger); -} - -.bui-Text[data-color="warning"] { - color: var(--bui-fg-warning); -} - -.bui-Text[data-color="success"] { - color: var(--bui-fg-success); -} - -.bui-Text[data-truncate] { - text-overflow: ellipsis; - white-space: nowrap; - overflow: hidden; -} - -.bui-Text[data-as="span"], .bui-Text[data-as="label"], .bui-Text[data-as="strong"], .bui-Text[data-as="em"], .bui-Text[data-as="small"] { - display: inline-block; -} - -.bui-TextField { - font-family: var(--bui-font-regular); - flex-direction: column; - flex-shrink: 0; - width: 100%; - display: flex; -} - -.bui-InputWrapper { - position: relative; - - &[data-size="small"] .bui-Input { - height: 2rem; - } - - &[data-size="medium"] .bui-Input { - height: 2.5rem; - } - - &[data-size="small"] .bui-Input[data-icon] { - padding-left: var(--bui-space-8); - } - - &[data-size="medium"] .bui-Input[data-icon] { - padding-left: var(--bui-space-9); - } -} - -.bui-InputIcon { - left: var(--bui-space-3); - margin-right: var(--bui-space-1); - color: var(--bui-fg-primary); - pointer-events: none; - flex-shrink: 0; - transition: left .2s ease-in-out; - position: absolute; - top: 50%; - transform: translateY(-50%); - - &[data-size="small"], &[data-size="small"] svg { - width: 1rem; - height: 1rem; - } - - &[data-size="medium"], &[data-size="medium"] svg { - width: 1.25rem; - height: 1.25rem; - } -} - -.bui-Input { - padding: 0 var(--bui-space-3); - border-radius: var(--bui-radius-2); - border: 1px solid var(--bui-border); - background-color: var(--bui-bg-surface-1); - font-size: var(--bui-font-size-3); - font-family: var(--bui-font-regular); - font-weight: var(--bui-font-weight-regular); - color: var(--bui-fg-primary); - width: 100%; - height: 100%; - cursor: inherit; - align-items: center; - transition: border-color .2s ease-in-out, outline-color .2s ease-in-out; - display: flex; - - &::-webkit-search-cancel-button, &::-webkit-search-decoration { - -webkit-appearance: none; - } - - &::placeholder { - color: var(--bui-fg-secondary); - } - - &[data-focused] { - outline-color: var(--bui-border-pressed); - outline-width: 0; - } - - &[data-hovered] { - border-color: var(--bui-border-hover); - } - - &[data-focused] { - border-color: var(--bui-border-pressed); - outline-width: 0; - } - - &[data-invalid] { - border-color: var(--bui-fg-danger); - } - - &[data-disabled] { - opacity: .5; - cursor: not-allowed; - border: 1px solid var(--bui-border-disabled); - } -} - -.bui-SearchField { - flex: 1 0; - - &[data-empty] { - & .bui-InputClear { + @media (width >= 1024px) { + .md\:bui-display-none { display: none; } - } - &[data-start-collapsed="true"] { - flex: 0 auto; - padding: 0; - transition: flex-basis .3s ease-in-out; - - &[data-collapsed="true"] { - flex-basis: 200px; + .md\:bui-display-inline { + display: inline; } - &[data-collapsed="false"] { - cursor: pointer; - - &[data-size="medium"] { - flex-basis: 2.5rem; - height: 2.5rem; - } - - &[data-size="small"] { - flex-basis: 2rem; - height: 2rem; - } - - &[data-size="medium"] .bui-Input { - &::placeholder { - opacity: 0; - } - } - - &[data-size="small"] .bui-Input { - &::placeholder { - opacity: 0; - } - } - - & .bui-InputWrapper { - & .bui-Input[data-icon] { - padding-right: 0; - } - } - } - } -} - -.bui-SearchField .bui-Input { - transition: padding .3s ease-in-out, border-color .2s ease-in-out, outline-color .2s ease-in-out; - - &[data-hovered] { - border-color: var(--bui-border-hover); - } - - &[data-focused] { - border-color: var(--bui-border-pressed); - outline-width: 0; - } -} - -.bui-SearchField .bui-InputWrapper { - & .bui-Input[data-icon] { - padding-right: var(--bui-space-6); - } -} - -.bui-SearchField .bui-InputIcon { - justify-content: center; - display: flex; - left: 0; - - &[data-size="small"] { - width: var(--bui-space-8); - } - - &[data-size="medium"] { - width: var(--bui-space-10); - } -} - -.bui-InputClear { - cursor: pointer; - color: var(--bui-fg-secondary); - background-color: #0000; - border: none; - justify-content: center; - align-items: center; - margin: 0; - padding: 0; - transition: color .2s ease-in-out; - display: flex; - position: absolute; - top: 0; - bottom: 0; - right: 0; -} - -.bui-InputClear:hover { - color: var(--bui-fg-primary); -} - -.bui-InputClear[data-size="small"] { - width: 2rem; - height: 2rem; -} - -.bui-InputClear[data-size="medium"] { - width: 2.5rem; - height: 2.5rem; -} - -.bui-InputClear svg { - width: 1rem; - height: 1rem; -} - -.bui-Skeleton { - animation: var(--bui-animate-pulse); - background-color: var(--bui-bg-surface-2); - border-radius: var(--bui-radius-2); -} - -.bui-Skeleton[data-rounded="true"] { - border-radius: var(--bui-radius-full); -} - -.bui-Tooltip { - background: var(--bui-bg-surface-1); - border: 1px solid var(--bui-gray-3); - forced-color-adjust: none; - padding: var(--bui-space-2) var(--bui-space-3); - max-width: 240px; - font-size: var(--bui-font-size-3); - font-family: var(--bui-font-regular); - color: var(--bui-fg-primary); - border-radius: 4px; - outline: none; - transition: transform .2s, opacity .2s; - transform: translate3d(0, 0, 0); - box-shadow: 0 10px 15px -3px #0000001a, 0 4px 6px -4px #0000001a; - - &[data-entering], &[data-exiting] { - transform: var(--origin); - opacity: 0; - } - - --tooltip-offset: var(--bui-space-3); - - &[data-placement="top"] { - margin-bottom: var(--tooltip-offset); - --origin: translateY(4px); - } - - &[data-placement="right"] { - margin-left: var(--tooltip-offset); - --origin: translateX(-4px); - } - - &[data-placement="bottom"] { - margin-top: var(--tooltip-offset); - --origin: translateY(-4px); - } - - &[data-placement="left"] { - margin-right: var(--tooltip-offset); - --origin: translateX(4px); - } -} - -.bui-TooltipArrow { - & svg { - display: block; - - & path:first-child { - fill: var(--bui-bg-surface-1); + .md\:bui-display-inline-block { + display: inline-block; } - & path:nth-child(2) { - fill: var(--bui-gray-3); - } - - --tooltip-arrow-overlap: -2px; - } - - &[data-placement="top"] svg { - margin-top: var(--tooltip-arrow-overlap); - } - - &[data-placement="bottom"] svg { - margin-bottom: var(--tooltip-arrow-overlap); - transform: rotate(180deg); - } - - &[data-placement="right"] svg { - margin-right: var(--tooltip-arrow-overlap); - transform: rotate(90deg); - } - - &[data-placement="left"] svg { - margin-left: var(--tooltip-arrow-overlap); - transform: rotate(-90deg); - } -} - -[data-theme="dark"] { - & .bui-Tooltip { - background: var(--bui-bg-surface-2); - box-shadow: none; - border: 1px solid var(--bui-gray-4); - } - - & .bui-TooltipArrow { - & svg path:first-child { - fill: var(--bui-bg-surface-2); - } - - & svg path:nth-child(2) { - fill: var(--bui-gray-4); + .md\:bui-display-block { + display: block; } } -} -.bui-ScrollAreaRoot { - box-sizing: border-box; - width: 100%; -} + @media (width >= 1280px) { + .lg\:bui-display-none { + display: none; + } -.bui-ScrollAreaViewport { - overscroll-behavior: contain; - height: 100%; -} + .lg\:bui-display-inline { + display: inline; + } -.bui-ScrollAreaContent { - padding-block: .75rem; - flex-direction: column; - gap: 1rem; - padding-left: 1rem; - padding-right: 1.5rem; - display: flex; -} + .lg\:bui-display-inline-block { + display: inline-block; + } -.bui-ScrollAreaScrollbar { - background-color: var(--bui-scrollbar); - opacity: 0; - border-radius: .375rem; - justify-content: center; - width: .25rem; - margin: .5rem; - transition: opacity .15s .3s; - display: flex; - - &[data-hovering], &[data-scrolling] { - opacity: 1; - transition-duration: 75ms; - transition-delay: 0s; + .lg\:bui-display-block { + display: block; + } } - &:before { - content: ""; - width: 1.25rem; - height: 100%; + @media (width >= 1536px) { + .xl\:bui-display-none { + display: none; + } + + .xl\:bui-display-inline { + display: inline; + } + + .xl\:bui-display-inline-block { + display: inline-block; + } + + .xl\:bui-display-block { + display: block; + } + } + + .bui-w { + width: var(--width); + } + + .bui-min-w { + min-width: var(--min-width); + } + + .bui-max-w { + max-width: var(--max-width); + } + + @media (width >= 640px) { + .xs\:bui-w { + width: var(--width); + } + + .xs\:bui-min-w { + min-width: var(--min-width); + } + + .xs\:bui-max-w { + max-width: var(--max-width); + } + } + + @media (width >= 768px) { + .sm\:bui-w { + width: var(--width); + } + + .sm\:bui-min-w { + min-width: var(--min-width); + } + + .sm\:bui-max-w { + max-width: var(--max-width); + } + } + + @media (width >= 1024px) { + .md\:bui-w { + width: var(--width); + } + + .md\:bui-min-w { + min-width: var(--min-width); + } + + .md\:bui-max-w { + max-width: var(--max-width); + } + } + + @media (width >= 1280px) { + .lg\:bui-w { + width: var(--width); + } + + .lg\:bui-min-w { + min-width: var(--min-width); + } + + .lg\:bui-max-w { + max-width: var(--max-width); + } + } + + @media (width >= 1536px) { + .xl\:bui-w { + width: var(--width); + } + + .xl\:bui-min-w { + min-width: var(--min-width); + } + + .xl\:bui-max-w { + max-width: var(--max-width); + } + } + + .bui-h { + height: var(--height); + } + + .bui-min-h { + min-height: var(--min-height); + } + + .bui-max-h { + max-height: var(--max-height); + } + + @media (width >= 640px) { + .xs\:bui-h { + height: var(--height); + } + + .xs\:bui-min-h { + min-height: var(--min-height); + } + + .xs\:bui-max-h { + max-height: var(--max-height); + } + } + + @media (width >= 768px) { + .sm\:bui-h { + height: var(--height); + } + + .sm\:bui-min-h { + min-height: var(--min-height); + } + + .sm\:bui-max-h { + max-height: var(--max-height); + } + } + + @media (width >= 1024px) { + .md\:bui-h { + height: var(--height); + } + + .md\:bui-min-h { + min-height: var(--min-height); + } + + .md\:bui-max-h { + max-height: var(--max-height); + } + } + + @media (width >= 1280px) { + .lg\:bui-h { + height: var(--height); + } + + .lg\:bui-min-h { + min-height: var(--min-height); + } + + .lg\:bui-max-h { + max-height: var(--max-height); + } + } + + @media (width >= 1536px) { + .xl\:bui-h { + height: var(--height); + } + + .xl\:bui-min-h { + min-height: var(--min-height); + } + + .xl\:bui-max-h { + max-height: var(--max-height); + } + } + + .bui-position-absolute { position: absolute; } -} -.bui-ScrollAreaThumb { - border-radius: inherit; - background-color: var(--bui-scrollbar-thumb); - width: 100%; -} - -.bui-Select[data-invalid] { - & .bui-SelectTrigger { - border-color: var(--bui-fg-danger); - } -} - -.bui-SelectTrigger { - box-sizing: border-box; - border-radius: var(--bui-radius-3); - border: 1px solid var(--bui-border); - background-color: var(--bui-bg-surface-1); - cursor: pointer; - justify-content: space-between; - align-items: center; - gap: var(--bui-space-2); - width: 100%; - display: flex; - - & svg { - color: var(--bui-fg-secondary); - flex-shrink: 0; + .bui-position-fixed { + position: fixed; } - &[data-size="small"] { - height: 2rem; - padding-inline: var(--bui-space-3); + .bui-position-sticky { + position: sticky; } - &[data-size="medium"] { - height: 3rem; - padding-inline: var(--bui-space-4); - } - - &[data-size="small"] svg { - width: 1rem; - height: 1rem; - } - - &[data-size="medium"] svg { - width: 1.25rem; - height: 1.25rem; - } - - &::placeholder { - color: var(--bui-fg-secondary); - } - - &:hover { - border-color: var(--bui-border-hover); - transition: border-color .2s ease-in-out, outline-color .2s ease-in-out; - } - - &:focus-visible { - border-color: var(--bui-border-pressed); - outline: 0; - } - - &[data-invalid] { - border-color: var(--bui-fg-danger); - } - - &[data-invalid]:hover, &[data-invalid]:focus-visible { - border-width: 2px; - } - - &[disabled] { - cursor: not-allowed; - border-color: var(--bui-border-disabled); - color: var(--bui-fg-disabled); - } - - &[disabled] .bui-SelectValue { - color: var(--bui-fg-disabled); - } - - &[data-popup-open] .bui-SelectIcon { - transform: rotate(180deg); - } -} - -.bui-SelectValue { - text-overflow: ellipsis; - white-space: nowrap; - width: 100%; - font-size: var(--bui-font-size-3); - font-family: var(--bui-font-regular); - font-weight: var(--bui-font-weight-regular); - color: var(--bui-fg-primary); - text-align: left; - overflow: hidden; - - & .bui-SelectItemIndicator { - display: none; - } - - &[disabled] { - color: var(--bui-fg-disabled); - } -} - -.bui-SelectItem { - width: var(--anchor-width); - padding-block: var(--bui-space-2); - padding-left: var(--bui-space-3); - padding-right: var(--bui-space-4); - color: var(--bui-fg-primary); - border-radius: var(--bui-radius-3); - cursor: pointer; - user-select: none; - font-size: var(--bui-font-size-3); - align-items: center; - gap: var(--bui-space-1); - outline: none; - grid-template-columns: 1rem 1fr; - grid-template-areas: "icon text"; - display: grid; - position: relative; - - &[data-focused] { - z-index: 0; - color: var(--bui-fg-primary); + .bui-position-relative { position: relative; } - &[data-focused]:before { - content: ""; - z-index: -1; - background-color: var(--bui-bg-tint-hover); - border-radius: .25rem; - position: absolute; - inset-block: 0; - inset-inline: .25rem; + .bui-position-static { + position: static; } - &[data-disabled] { - cursor: not-allowed; - color: var(--bui-fg-disabled); - } + @media (width >= 640px) { + .xs\:bui-position-absolute { + position: absolute; + } - &[data-selected] .bui-SelectItemIndicator { - opacity: 1; - } -} + .xs\:bui-position-fixed { + position: fixed; + } -.bui-SelectItemIndicator { - opacity: 0; - grid-area: icon; - justify-content: center; - align-items: center; - transition: opacity .2s ease-in-out; - display: flex; -} + .xs\:bui-position-sticky { + position: sticky; + } -.bui-SelectItemLabel { - flex: 1; - grid-area: text; -} + .xs\:bui-position-relative { + position: relative; + } -.bui-Switch { - align-items: center; - gap: var(--bui-space-3); - font-size: var(--bui-font-size-3); - color: var(--bui-fg-primary); - cursor: pointer; - display: flex; - position: relative; - - &[data-pressed] .bui-SwitchIndicator { - &:before { - background: var(--bui-fg-solid); + .xs\:bui-position-static { + position: static; } } - &[data-selected] { - & .bui-SwitchIndicator { - background: var(--bui-bg-solid); - - &:before { - background: var(--bui-fg-solid); - transform: translateX(100%); - } + @media (width >= 768px) { + .sm\:bui-position-absolute { + position: absolute; } - &[data-pressed] { - & .indicator { - background: var(--bui-gray-3); - } + .sm\:bui-position-fixed { + position: fixed; + } + + .sm\:bui-position-sticky { + position: sticky; + } + + .sm\:bui-position-relative { + position: relative; + } + + .sm\:bui-position-static { + position: static; } } - &[data-focus-visible] .bui-SwitchIndicator { - outline-offset: 2px; - outline: 2px solid; - } -} - -.bui-SwitchIndicator { - background: var(--bui-gray-3); - border: 2px; - border-radius: 1.143rem; - width: 2rem; - height: 1.143rem; - transition: all .2s; - - &:before { - content: ""; - background: var(--bui-fg-solid); - border-radius: 16px; - width: .857rem; - height: .857rem; - margin: .143rem; - transition: all .2s; - display: block; + @media (width >= 1024px) { + .md\:bui-position-absolute { + position: absolute; + } + + .md\:bui-position-fixed { + position: fixed; + } + + .md\:bui-position-sticky { + position: sticky; + } + + .md\:bui-position-relative { + position: relative; + } + + .md\:bui-position-static { + position: static; + } + } + + @media (width >= 1280px) { + .lg\:bui-position-absolute { + position: absolute; + } + + .lg\:bui-position-fixed { + position: fixed; + } + + .lg\:bui-position-sticky { + position: sticky; + } + + .lg\:bui-position-relative { + position: relative; + } + + .lg\:bui-position-static { + position: static; + } + } + + @media (width >= 1536px) { + .xl\:bui-position-absolute { + position: absolute; + } + + .xl\:bui-position-fixed { + position: fixed; + } + + .xl\:bui-position-sticky { + position: sticky; + } + + .xl\:bui-position-relative { + position: relative; + } + + .xl\:bui-position-static { + position: static; + } + } + + .bui-columns-1 { + grid-template-columns: repeat(1, minmax(0, 1fr)); + } + + .bui-columns-2 { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + .bui-columns-3 { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } + + .bui-columns-4 { + grid-template-columns: repeat(4, minmax(0, 1fr)); + } + + .bui-columns-5 { + grid-template-columns: repeat(5, minmax(0, 1fr)); + } + + .bui-columns-6 { + grid-template-columns: repeat(6, minmax(0, 1fr)); + } + + .bui-columns-7 { + grid-template-columns: repeat(7, minmax(0, 1fr)); + } + + .bui-columns-8 { + grid-template-columns: repeat(8, minmax(0, 1fr)); + } + + .bui-columns-9 { + grid-template-columns: repeat(9, minmax(0, 1fr)); + } + + .bui-columns-10 { + grid-template-columns: repeat(10, minmax(0, 1fr)); + } + + .bui-columns-11 { + grid-template-columns: repeat(11, minmax(0, 1fr)); + } + + .bui-columns-12 { + grid-template-columns: repeat(12, minmax(0, 1fr)); + } + + .bui-columns-auto { + grid-template-columns: repeat(auto-fit, minmax(0, 1fr)); + } + + .bui-col-span-1 { + grid-column: span 1 / span 1; + } + + .bui-col-span-2 { + grid-column: span 2 / span 2; + } + + .bui-col-span-3 { + grid-column: span 3 / span 3; + } + + .bui-col-span-4 { + grid-column: span 4 / span 4; + } + + .bui-col-span-5 { + grid-column: span 5 / span 5; + } + + .bui-col-span-6 { + grid-column: span 6 / span 6; + } + + .bui-col-span-7 { + grid-column: span 7 / span 7; + } + + .bui-col-span-8 { + grid-column: span 8 / span 8; + } + + .bui-col-span-9 { + grid-column: span 9 / span 9; + } + + .bui-col-span-10 { + grid-column: span 10 / span 10; + } + + .bui-col-span-11 { + grid-column: span 11 / span 11; + } + + .bui-col-span-12 { + grid-column: span 12 / span 12; + } + + .bui-col-span-auto { + grid-column: span auto / span auto; + } + + .bui-col-start-1 { + grid-column-start: 1; + } + + .bui-col-start-2 { + grid-column-start: 2; + } + + .bui-col-start-3 { + grid-column-start: 3; + } + + .bui-col-start-4 { + grid-column-start: 4; + } + + .bui-col-start-5 { + grid-column-start: 5; + } + + .bui-col-start-6 { + grid-column-start: 6; + } + + .bui-col-start-7 { + grid-column-start: 7; + } + + .bui-col-start-8 { + grid-column-start: 8; + } + + .bui-col-start-9 { + grid-column-start: 9; + } + + .bui-col-start-10 { + grid-column-start: 10; + } + + .bui-col-start-11 { + grid-column-start: 11; + } + + .bui-col-start-12 { + grid-column-start: 12; + } + + .bui-col-start-13 { + grid-column-start: 13; + } + + .bui-col-start-auto { + grid-column-start: auto; + } + + .bui-col-end-1 { + grid-column-end: 1; + } + + .bui-col-end-2 { + grid-column-end: 2; + } + + .bui-col-end-3 { + grid-column-end: 3; + } + + .bui-col-end-4 { + grid-column-end: 4; + } + + .bui-col-end-5 { + grid-column-end: 5; + } + + .bui-col-end-6 { + grid-column-end: 6; + } + + .bui-col-end-7 { + grid-column-end: 7; + } + + .bui-col-end-8 { + grid-column-end: 8; + } + + .bui-col-end-9 { + grid-column-end: 9; + } + + .bui-col-end-10 { + grid-column-end: 10; + } + + .bui-col-end-11 { + grid-column-end: 11; + } + + .bui-col-end-12 { + grid-column-end: 12; + } + + .bui-col-end-13 { + grid-column-end: 13; + } + + .bui-col-end-auto { + grid-column-end: auto; + } + + .bui-row-span-1 { + grid-row: span 1 / span 1; + } + + .bui-row-span-2 { + grid-row: span 2 / span 2; + } + + .bui-row-span-3 { + grid-row: span 3 / span 3; + } + + .bui-row-span-4 { + grid-row: span 4 / span 4; + } + + .bui-row-span-5 { + grid-row: span 5 / span 5; + } + + .bui-row-span-6 { + grid-row: span 6 / span 6; + } + + .bui-row-span-7 { + grid-row: span 7 / span 7; + } + + .bui-row-span-8 { + grid-row: span 8 / span 8; + } + + .bui-row-span-9 { + grid-row: span 9 / span 9; + } + + .bui-row-span-10 { + grid-row: span 10 / span 10; + } + + .bui-row-span-11 { + grid-row: span 11 / span 11; + } + + .bui-row-span-12 { + grid-row: span 12 / span 12; + } + + .bui-row-span-auto { + grid-row: span auto / span auto; + } + + @media (width >= 640px) { + .xs\:bui-columns-1 { + grid-template-columns: repeat(1, minmax(0, 1fr)); + } + + .xs\:bui-columns-2 { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + .xs\:bui-columns-3 { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } + + .xs\:bui-columns-4 { + grid-template-columns: repeat(4, minmax(0, 1fr)); + } + + .xs\:bui-columns-5 { + grid-template-columns: repeat(5, minmax(0, 1fr)); + } + + .xs\:bui-columns-6 { + grid-template-columns: repeat(6, minmax(0, 1fr)); + } + + .xs\:bui-columns-7 { + grid-template-columns: repeat(7, minmax(0, 1fr)); + } + + .xs\:bui-columns-8 { + grid-template-columns: repeat(8, minmax(0, 1fr)); + } + + .xs\:bui-columns-9 { + grid-template-columns: repeat(9, minmax(0, 1fr)); + } + + .xs\:bui-columns-10 { + grid-template-columns: repeat(10, minmax(0, 1fr)); + } + + .xs\:bui-columns-11 { + grid-template-columns: repeat(11, minmax(0, 1fr)); + } + + .xs\:bui-columns-12 { + grid-template-columns: repeat(12, minmax(0, 1fr)); + } + + .xs\:bui-columns-auto { + grid-template-columns: repeat(auto-fit, minmax(0, 1fr)); + } + + .xs\:bui-col-span-1 { + grid-column: span 1 / span 1; + } + + .xs\:bui-col-span-2 { + grid-column: span 2 / span 2; + } + + .xs\:bui-col-span-3 { + grid-column: span 3 / span 3; + } + + .xs\:bui-col-span-4 { + grid-column: span 4 / span 4; + } + + .xs\:bui-col-span-5 { + grid-column: span 5 / span 5; + } + + .xs\:bui-col-span-6 { + grid-column: span 6 / span 6; + } + + .xs\:bui-col-span-7 { + grid-column: span 7 / span 7; + } + + .xs\:bui-col-span-8 { + grid-column: span 8 / span 8; + } + + .xs\:bui-col-span-9 { + grid-column: span 9 / span 9; + } + + .xs\:bui-col-span-10 { + grid-column: span 10 / span 10; + } + + .xs\:bui-col-span-11 { + grid-column: span 11 / span 11; + } + + .xs\:bui-col-span-12 { + grid-column: span 12 / span 12; + } + + .xs\:bui-col-span-auto { + grid-column: span auto / span auto; + } + + .xs\:bui-col-start-1 { + grid-column-start: 1; + } + + .xs\:bui-col-start-2 { + grid-column-start: 2; + } + + .xs\:bui-col-start-3 { + grid-column-start: 3; + } + + .xs\:bui-col-start-4 { + grid-column-start: 4; + } + + .xs\:bui-col-start-5 { + grid-column-start: 5; + } + + .xs\:bui-col-start-6 { + grid-column-start: 6; + } + + .xs\:bui-col-start-7 { + grid-column-start: 7; + } + + .xs\:bui-col-start-8 { + grid-column-start: 8; + } + + .xs\:bui-col-start-9 { + grid-column-start: 9; + } + + .xs\:bui-col-start-10 { + grid-column-start: 10; + } + + .xs\:bui-col-start-11 { + grid-column-start: 11; + } + + .xs\:bui-col-start-12 { + grid-column-start: 12; + } + + .xs\:bui-col-start-13 { + grid-column-start: 13; + } + + .xs\:bui-col-start-auto { + grid-column-start: auto; + } + + .xs\:bui-col-end-1 { + grid-column-end: 1; + } + + .xs\:bui-col-end-2 { + grid-column-end: 2; + } + + .xs\:bui-col-end-3 { + grid-column-end: 3; + } + + .xs\:bui-col-end-4 { + grid-column-end: 4; + } + + .xs\:bui-col-end-5 { + grid-column-end: 5; + } + + .xs\:bui-col-end-6 { + grid-column-end: 6; + } + + .xs\:bui-col-end-7 { + grid-column-end: 7; + } + + .xs\:bui-col-end-8 { + grid-column-end: 8; + } + + .xs\:bui-col-end-9 { + grid-column-end: 9; + } + + .xs\:bui-col-end-10 { + grid-column-end: 10; + } + + .xs\:bui-col-end-11 { + grid-column-end: 11; + } + + .xs\:bui-col-end-12 { + grid-column-end: 12; + } + + .xs\:bui-col-end-13 { + grid-column-end: 13; + } + + .xs\:bui-col-end-auto { + grid-column-end: auto; + } + + .xs\:bui-row-span-1 { + grid-row: span 1 / span 1; + } + + .xs\:bui-row-span-2 { + grid-row: span 2 / span 2; + } + + .xs\:bui-row-span-3 { + grid-row: span 3 / span 3; + } + + .xs\:bui-row-span-4 { + grid-row: span 4 / span 4; + } + + .xs\:bui-row-span-5 { + grid-row: span 5 / span 5; + } + + .xs\:bui-row-span-6 { + grid-row: span 6 / span 6; + } + + .xs\:bui-row-span-7 { + grid-row: span 7 / span 7; + } + + .xs\:bui-row-span-8 { + grid-row: span 8 / span 8; + } + + .xs\:bui-row-span-9 { + grid-row: span 9 / span 9; + } + + .xs\:bui-row-span-10 { + grid-row: span 10 / span 10; + } + + .xs\:bui-row-span-11 { + grid-row: span 11 / span 11; + } + + .xs\:bui-row-span-12 { + grid-row: span 12 / span 12; + } + + .xs\:bui-row-span-auto { + grid-row: span auto / span auto; + } + } + + @media (width >= 768px) { + .sm\:bui-columns-1 { + grid-template-columns: repeat(1, minmax(0, 1fr)); + } + + .sm\:bui-columns-2 { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + .sm\:bui-columns-3 { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } + + .sm\:bui-columns-4 { + grid-template-columns: repeat(4, minmax(0, 1fr)); + } + + .sm\:bui-columns-5 { + grid-template-columns: repeat(5, minmax(0, 1fr)); + } + + .sm\:bui-columns-6 { + grid-template-columns: repeat(6, minmax(0, 1fr)); + } + + .sm\:bui-columns-7 { + grid-template-columns: repeat(7, minmax(0, 1fr)); + } + + .sm\:bui-columns-8 { + grid-template-columns: repeat(8, minmax(0, 1fr)); + } + + .sm\:bui-columns-9 { + grid-template-columns: repeat(9, minmax(0, 1fr)); + } + + .sm\:bui-columns-10 { + grid-template-columns: repeat(10, minmax(0, 1fr)); + } + + .sm\:bui-columns-11 { + grid-template-columns: repeat(11, minmax(0, 1fr)); + } + + .sm\:bui-columns-12 { + grid-template-columns: repeat(12, minmax(0, 1fr)); + } + + .sm\:bui-columns-auto { + grid-template-columns: repeat(auto-fit, minmax(0, 1fr)); + } + + .sm\:bui-col-span-1 { + grid-column: span 1 / span 1; + } + + .sm\:bui-col-span-2 { + grid-column: span 2 / span 2; + } + + .sm\:bui-col-span-3 { + grid-column: span 3 / span 3; + } + + .sm\:bui-col-span-4 { + grid-column: span 4 / span 4; + } + + .sm\:bui-col-span-5 { + grid-column: span 5 / span 5; + } + + .sm\:bui-col-span-6 { + grid-column: span 6 / span 6; + } + + .sm\:bui-col-span-7 { + grid-column: span 7 / span 7; + } + + .sm\:bui-col-span-8 { + grid-column: span 8 / span 8; + } + + .sm\:bui-col-span-9 { + grid-column: span 9 / span 9; + } + + .sm\:bui-col-span-10 { + grid-column: span 10 / span 10; + } + + .sm\:bui-col-span-11 { + grid-column: span 11 / span 11; + } + + .sm\:bui-col-span-12 { + grid-column: span 12 / span 12; + } + + .sm\:bui-col-span-auto { + grid-column: span auto / span auto; + } + + .sm\:bui-col-start-1 { + grid-column-start: 1; + } + + .sm\:bui-col-start-2 { + grid-column-start: 2; + } + + .sm\:bui-col-start-3 { + grid-column-start: 3; + } + + .sm\:bui-col-start-4 { + grid-column-start: 4; + } + + .sm\:bui-col-start-5 { + grid-column-start: 5; + } + + .sm\:bui-col-start-6 { + grid-column-start: 6; + } + + .sm\:bui-col-start-7 { + grid-column-start: 7; + } + + .sm\:bui-col-start-8 { + grid-column-start: 8; + } + + .sm\:bui-col-start-9 { + grid-column-start: 9; + } + + .sm\:bui-col-start-10 { + grid-column-start: 10; + } + + .sm\:bui-col-start-11 { + grid-column-start: 11; + } + + .sm\:bui-col-start-12 { + grid-column-start: 12; + } + + .sm\:bui-col-start-13 { + grid-column-start: 13; + } + + .sm\:bui-col-start-auto { + grid-column-start: auto; + } + + .sm\:bui-col-end-1 { + grid-column-end: 1; + } + + .sm\:bui-col-end-2 { + grid-column-end: 2; + } + + .sm\:bui-col-end-3 { + grid-column-end: 3; + } + + .sm\:bui-col-end-4 { + grid-column-end: 4; + } + + .sm\:bui-col-end-5 { + grid-column-end: 5; + } + + .sm\:bui-col-end-6 { + grid-column-end: 6; + } + + .sm\:bui-col-end-7 { + grid-column-end: 7; + } + + .sm\:bui-col-end-8 { + grid-column-end: 8; + } + + .sm\:bui-col-end-9 { + grid-column-end: 9; + } + + .sm\:bui-col-end-10 { + grid-column-end: 10; + } + + .sm\:bui-col-end-11 { + grid-column-end: 11; + } + + .sm\:bui-col-end-12 { + grid-column-end: 12; + } + + .sm\:bui-col-end-13 { + grid-column-end: 13; + } + + .sm\:bui-col-end-auto { + grid-column-end: auto; + } + + .sm\:bui-row-span-1 { + grid-row: span 1 / span 1; + } + + .sm\:bui-row-span-2 { + grid-row: span 2 / span 2; + } + + .sm\:bui-row-span-3 { + grid-row: span 3 / span 3; + } + + .sm\:bui-row-span-4 { + grid-row: span 4 / span 4; + } + + .sm\:bui-row-span-5 { + grid-row: span 5 / span 5; + } + + .sm\:bui-row-span-6 { + grid-row: span 6 / span 6; + } + + .sm\:bui-row-span-7 { + grid-row: span 7 / span 7; + } + + .sm\:bui-row-span-8 { + grid-row: span 8 / span 8; + } + + .sm\:bui-row-span-9 { + grid-row: span 9 / span 9; + } + + .sm\:bui-row-span-10 { + grid-row: span 10 / span 10; + } + + .sm\:bui-row-span-11 { + grid-row: span 11 / span 11; + } + + .sm\:bui-row-span-12 { + grid-row: span 12 / span 12; + } + + .sm\:bui-row-span-auto { + grid-row: span auto / span auto; + } + } + + @media (width >= 1024px) { + .md\:bui-columns-1 { + grid-template-columns: repeat(1, minmax(0, 1fr)); + } + + .md\:bui-columns-2 { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + .md\:bui-columns-3 { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } + + .md\:bui-columns-4 { + grid-template-columns: repeat(4, minmax(0, 1fr)); + } + + .md\:bui-columns-5 { + grid-template-columns: repeat(5, minmax(0, 1fr)); + } + + .md\:bui-columns-6 { + grid-template-columns: repeat(6, minmax(0, 1fr)); + } + + .md\:bui-columns-7 { + grid-template-columns: repeat(7, minmax(0, 1fr)); + } + + .md\:bui-columns-8 { + grid-template-columns: repeat(8, minmax(0, 1fr)); + } + + .md\:bui-columns-9 { + grid-template-columns: repeat(9, minmax(0, 1fr)); + } + + .md\:bui-columns-10 { + grid-template-columns: repeat(10, minmax(0, 1fr)); + } + + .md\:bui-columns-11 { + grid-template-columns: repeat(11, minmax(0, 1fr)); + } + + .md\:bui-columns-12 { + grid-template-columns: repeat(12, minmax(0, 1fr)); + } + + .md\:bui-columns-auto { + grid-template-columns: repeat(auto-fit, minmax(0, 1fr)); + } + + .md\:bui-col-span-1 { + grid-column: span 1 / span 1; + } + + .md\:bui-col-span-2 { + grid-column: span 2 / span 2; + } + + .md\:bui-col-span-3 { + grid-column: span 3 / span 3; + } + + .md\:bui-col-span-4 { + grid-column: span 4 / span 4; + } + + .md\:bui-col-span-5 { + grid-column: span 5 / span 5; + } + + .md\:bui-col-span-6 { + grid-column: span 6 / span 6; + } + + .md\:bui-col-span-7 { + grid-column: span 7 / span 7; + } + + .md\:bui-col-span-8 { + grid-column: span 8 / span 8; + } + + .md\:bui-col-span-9 { + grid-column: span 9 / span 9; + } + + .md\:bui-col-span-10 { + grid-column: span 10 / span 10; + } + + .md\:bui-col-span-11 { + grid-column: span 11 / span 11; + } + + .md\:bui-col-span-12 { + grid-column: span 12 / span 12; + } + + .md\:bui-col-span-auto { + grid-column: span auto / span auto; + } + + .md\:bui-col-start-1 { + grid-column-start: 1; + } + + .md\:bui-col-start-2 { + grid-column-start: 2; + } + + .md\:bui-col-start-3 { + grid-column-start: 3; + } + + .md\:bui-col-start-4 { + grid-column-start: 4; + } + + .md\:bui-col-start-5 { + grid-column-start: 5; + } + + .md\:bui-col-start-6 { + grid-column-start: 6; + } + + .md\:bui-col-start-7 { + grid-column-start: 7; + } + + .md\:bui-col-start-8 { + grid-column-start: 8; + } + + .md\:bui-col-start-9 { + grid-column-start: 9; + } + + .md\:bui-col-start-10 { + grid-column-start: 10; + } + + .md\:bui-col-start-11 { + grid-column-start: 11; + } + + .md\:bui-col-start-12 { + grid-column-start: 12; + } + + .md\:bui-col-start-13 { + grid-column-start: 13; + } + + .md\:bui-col-start-auto { + grid-column-start: auto; + } + + .md\:bui-col-end-1 { + grid-column-end: 1; + } + + .md\:bui-col-end-2 { + grid-column-end: 2; + } + + .md\:bui-col-end-3 { + grid-column-end: 3; + } + + .md\:bui-col-end-4 { + grid-column-end: 4; + } + + .md\:bui-col-end-5 { + grid-column-end: 5; + } + + .md\:bui-col-end-6 { + grid-column-end: 6; + } + + .md\:bui-col-end-7 { + grid-column-end: 7; + } + + .md\:bui-col-end-8 { + grid-column-end: 8; + } + + .md\:bui-col-end-9 { + grid-column-end: 9; + } + + .md\:bui-col-end-10 { + grid-column-end: 10; + } + + .md\:bui-col-end-11 { + grid-column-end: 11; + } + + .md\:bui-col-end-12 { + grid-column-end: 12; + } + + .md\:bui-col-end-13 { + grid-column-end: 13; + } + + .md\:bui-col-end-auto { + grid-column-end: auto; + } + + .md\:bui-row-span-1 { + grid-row: span 1 / span 1; + } + + .md\:bui-row-span-2 { + grid-row: span 2 / span 2; + } + + .md\:bui-row-span-3 { + grid-row: span 3 / span 3; + } + + .md\:bui-row-span-4 { + grid-row: span 4 / span 4; + } + + .md\:bui-row-span-5 { + grid-row: span 5 / span 5; + } + + .md\:bui-row-span-6 { + grid-row: span 6 / span 6; + } + + .md\:bui-row-span-7 { + grid-row: span 7 / span 7; + } + + .md\:bui-row-span-8 { + grid-row: span 8 / span 8; + } + + .md\:bui-row-span-9 { + grid-row: span 9 / span 9; + } + + .md\:bui-row-span-10 { + grid-row: span 10 / span 10; + } + + .md\:bui-row-span-11 { + grid-row: span 11 / span 11; + } + + .md\:bui-row-span-12 { + grid-row: span 12 / span 12; + } + + .md\:bui-row-span-auto { + grid-row: span auto / span auto; + } + } + + @media (width >= 1280px) { + .lg\:bui-columns-1 { + grid-template-columns: repeat(1, minmax(0, 1fr)); + } + + .lg\:bui-columns-2 { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + .lg\:bui-columns-3 { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } + + .lg\:bui-columns-4 { + grid-template-columns: repeat(4, minmax(0, 1fr)); + } + + .lg\:bui-columns-5 { + grid-template-columns: repeat(5, minmax(0, 1fr)); + } + + .lg\:bui-columns-6 { + grid-template-columns: repeat(6, minmax(0, 1fr)); + } + + .lg\:bui-columns-7 { + grid-template-columns: repeat(7, minmax(0, 1fr)); + } + + .lg\:bui-columns-8 { + grid-template-columns: repeat(8, minmax(0, 1fr)); + } + + .lg\:bui-columns-9 { + grid-template-columns: repeat(9, minmax(0, 1fr)); + } + + .lg\:bui-columns-10 { + grid-template-columns: repeat(10, minmax(0, 1fr)); + } + + .lg\:bui-columns-11 { + grid-template-columns: repeat(11, minmax(0, 1fr)); + } + + .lg\:bui-columns-12 { + grid-template-columns: repeat(12, minmax(0, 1fr)); + } + + .lg\:bui-columns-auto { + grid-template-columns: repeat(auto-fit, minmax(0, 1fr)); + } + + .lg\:bui-col-span-1 { + grid-column: span 1 / span 1; + } + + .lg\:bui-col-span-2 { + grid-column: span 2 / span 2; + } + + .lg\:bui-col-span-3 { + grid-column: span 3 / span 3; + } + + .lg\:bui-col-span-4 { + grid-column: span 4 / span 4; + } + + .lg\:bui-col-span-5 { + grid-column: span 5 / span 5; + } + + .lg\:bui-col-span-6 { + grid-column: span 6 / span 6; + } + + .lg\:bui-col-span-7 { + grid-column: span 7 / span 7; + } + + .lg\:bui-col-span-8 { + grid-column: span 8 / span 8; + } + + .lg\:bui-col-span-9 { + grid-column: span 9 / span 9; + } + + .lg\:bui-col-span-10 { + grid-column: span 10 / span 10; + } + + .lg\:bui-col-span-11 { + grid-column: span 11 / span 11; + } + + .lg\:bui-col-span-12 { + grid-column: span 12 / span 12; + } + + .lg\:bui-col-span-auto { + grid-column: span auto / span auto; + } + + .lg\:bui-col-start-1 { + grid-column-start: 1; + } + + .lg\:bui-col-start-2 { + grid-column-start: 2; + } + + .lg\:bui-col-start-3 { + grid-column-start: 3; + } + + .lg\:bui-col-start-4 { + grid-column-start: 4; + } + + .lg\:bui-col-start-5 { + grid-column-start: 5; + } + + .lg\:bui-col-start-6 { + grid-column-start: 6; + } + + .lg\:bui-col-start-7 { + grid-column-start: 7; + } + + .lg\:bui-col-start-8 { + grid-column-start: 8; + } + + .lg\:bui-col-start-9 { + grid-column-start: 9; + } + + .lg\:bui-col-start-10 { + grid-column-start: 10; + } + + .lg\:bui-col-start-11 { + grid-column-start: 11; + } + + .lg\:bui-col-start-12 { + grid-column-start: 12; + } + + .lg\:bui-col-start-13 { + grid-column-start: 13; + } + + .lg\:bui-col-start-auto { + grid-column-start: auto; + } + + .lg\:bui-col-end-1 { + grid-column-end: 1; + } + + .lg\:bui-col-end-2 { + grid-column-end: 2; + } + + .lg\:bui-col-end-3 { + grid-column-end: 3; + } + + .lg\:bui-col-end-4 { + grid-column-end: 4; + } + + .lg\:bui-col-end-5 { + grid-column-end: 5; + } + + .lg\:bui-col-end-6 { + grid-column-end: 6; + } + + .lg\:bui-col-end-7 { + grid-column-end: 7; + } + + .lg\:bui-col-end-8 { + grid-column-end: 8; + } + + .lg\:bui-col-end-9 { + grid-column-end: 9; + } + + .lg\:bui-col-end-10 { + grid-column-end: 10; + } + + .lg\:bui-col-end-11 { + grid-column-end: 11; + } + + .lg\:bui-col-end-12 { + grid-column-end: 12; + } + + .lg\:bui-col-end-13 { + grid-column-end: 13; + } + + .lg\:bui-col-end-auto { + grid-column-end: auto; + } + + .lg\:bui-row-span-1 { + grid-row: span 1 / span 1; + } + + .lg\:bui-row-span-2 { + grid-row: span 2 / span 2; + } + + .lg\:bui-row-span-3 { + grid-row: span 3 / span 3; + } + + .lg\:bui-row-span-4 { + grid-row: span 4 / span 4; + } + + .lg\:bui-row-span-5 { + grid-row: span 5 / span 5; + } + + .lg\:bui-row-span-6 { + grid-row: span 6 / span 6; + } + + .lg\:bui-row-span-7 { + grid-row: span 7 / span 7; + } + + .lg\:bui-row-span-8 { + grid-row: span 8 / span 8; + } + + .lg\:bui-row-span-9 { + grid-row: span 9 / span 9; + } + + .lg\:bui-row-span-10 { + grid-row: span 10 / span 10; + } + + .lg\:bui-row-span-11 { + grid-row: span 11 / span 11; + } + + .lg\:bui-row-span-12 { + grid-row: span 12 / span 12; + } + + .lg\:bui-row-span-auto { + grid-row: span auto / span auto; + } + } + + @media (width >= 1536px) { + .xl\:bui-columns-1 { + grid-template-columns: repeat(1, minmax(0, 1fr)); + } + + .xl\:bui-columns-2 { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + .xl\:bui-columns-3 { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } + + .xl\:bui-columns-4 { + grid-template-columns: repeat(4, minmax(0, 1fr)); + } + + .xl\:bui-columns-5 { + grid-template-columns: repeat(5, minmax(0, 1fr)); + } + + .xl\:bui-columns-6 { + grid-template-columns: repeat(6, minmax(0, 1fr)); + } + + .xl\:bui-columns-7 { + grid-template-columns: repeat(7, minmax(0, 1fr)); + } + + .xl\:bui-columns-8 { + grid-template-columns: repeat(8, minmax(0, 1fr)); + } + + .xl\:bui-columns-9 { + grid-template-columns: repeat(9, minmax(0, 1fr)); + } + + .xl\:bui-columns-10 { + grid-template-columns: repeat(10, minmax(0, 1fr)); + } + + .xl\:bui-columns-11 { + grid-template-columns: repeat(11, minmax(0, 1fr)); + } + + .xl\:bui-columns-12 { + grid-template-columns: repeat(12, minmax(0, 1fr)); + } + + .xl\:bui-columns-auto { + grid-template-columns: repeat(auto-fit, minmax(0, 1fr)); + } + + .xl\:bui-col-span-1 { + grid-column: span 1 / span 1; + } + + .xl\:bui-col-span-2 { + grid-column: span 2 / span 2; + } + + .xl\:bui-col-span-3 { + grid-column: span 3 / span 3; + } + + .xl\:bui-col-span-4 { + grid-column: span 4 / span 4; + } + + .xl\:bui-col-span-5 { + grid-column: span 5 / span 5; + } + + .xl\:bui-col-span-6 { + grid-column: span 6 / span 6; + } + + .xl\:bui-col-span-7 { + grid-column: span 7 / span 7; + } + + .xl\:bui-col-span-8 { + grid-column: span 8 / span 8; + } + + .xl\:bui-col-span-9 { + grid-column: span 9 / span 9; + } + + .xl\:bui-col-span-10 { + grid-column: span 10 / span 10; + } + + .xl\:bui-col-span-11 { + grid-column: span 11 / span 11; + } + + .xl\:bui-col-span-12 { + grid-column: span 12 / span 12; + } + + .xl\:bui-col-span-auto { + grid-column: span auto / span auto; + } + + .xl\:bui-col-start-1 { + grid-column-start: 1; + } + + .xl\:bui-col-start-2 { + grid-column-start: 2; + } + + .xl\:bui-col-start-3 { + grid-column-start: 3; + } + + .xl\:bui-col-start-4 { + grid-column-start: 4; + } + + .xl\:bui-col-start-5 { + grid-column-start: 5; + } + + .xl\:bui-col-start-6 { + grid-column-start: 6; + } + + .xl\:bui-col-start-7 { + grid-column-start: 7; + } + + .xl\:bui-col-start-8 { + grid-column-start: 8; + } + + .xl\:bui-col-start-9 { + grid-column-start: 9; + } + + .xl\:bui-col-start-10 { + grid-column-start: 10; + } + + .xl\:bui-col-start-11 { + grid-column-start: 11; + } + + .xl\:bui-col-start-12 { + grid-column-start: 12; + } + + .xl\:bui-col-start-13 { + grid-column-start: 13; + } + + .xl\:bui-col-start-auto { + grid-column-start: auto; + } + + .xl\:bui-col-end-1 { + grid-column-end: 1; + } + + .xl\:bui-col-end-2 { + grid-column-end: 2; + } + + .xl\:bui-col-end-3 { + grid-column-end: 3; + } + + .xl\:bui-col-end-4 { + grid-column-end: 4; + } + + .xl\:bui-col-end-5 { + grid-column-end: 5; + } + + .xl\:bui-col-end-6 { + grid-column-end: 6; + } + + .xl\:bui-col-end-7 { + grid-column-end: 7; + } + + .xl\:bui-col-end-8 { + grid-column-end: 8; + } + + .xl\:bui-col-end-9 { + grid-column-end: 9; + } + + .xl\:bui-col-end-10 { + grid-column-end: 10; + } + + .xl\:bui-col-end-11 { + grid-column-end: 11; + } + + .xl\:bui-col-end-12 { + grid-column-end: 12; + } + + .xl\:bui-col-end-13 { + grid-column-end: 13; + } + + .xl\:bui-col-end-auto { + grid-column-end: auto; + } + + .xl\:bui-row-span-1 { + grid-row: span 1 / span 1; + } + + .xl\:bui-row-span-2 { + grid-row: span 2 / span 2; + } + + .xl\:bui-row-span-3 { + grid-row: span 3 / span 3; + } + + .xl\:bui-row-span-4 { + grid-row: span 4 / span 4; + } + + .xl\:bui-row-span-5 { + grid-row: span 5 / span 5; + } + + .xl\:bui-row-span-6 { + grid-row: span 6 / span 6; + } + + .xl\:bui-row-span-7 { + grid-row: span 7 / span 7; + } + + .xl\:bui-row-span-8 { + grid-row: span 8 / span 8; + } + + .xl\:bui-row-span-9 { + grid-row: span 9 / span 9; + } + + .xl\:bui-row-span-10 { + grid-row: span 10 / span 10; + } + + .xl\:bui-row-span-11 { + grid-row: span 11 / span 11; + } + + .xl\:bui-row-span-12 { + grid-row: span 12 / span 12; + } + + .xl\:bui-row-span-auto { + grid-row: span auto / span auto; + } + } + + .bui-gap { + gap: var(--gap); + } + + .bui-gap-0\.5 { + gap: var(--bui-space-0_5); + } + + .bui-gap-1 { + gap: var(--bui-space-1); + } + + .bui-gap-1\.5 { + gap: var(--bui-space-1_5); + } + + .bui-gap-2 { + gap: var(--bui-space-2); + } + + .bui-gap-3 { + gap: var(--bui-space-3); + } + + .bui-gap-4 { + gap: var(--bui-space-4); + } + + .bui-gap-5 { + gap: var(--bui-space-5); + } + + .bui-gap-6 { + gap: var(--bui-space-6); + } + + .bui-gap-7 { + gap: var(--bui-space-7); + } + + .bui-gap-8 { + gap: var(--bui-space-8); + } + + .bui-gap-9 { + gap: var(--bui-space-9); + } + + .bui-gap-10 { + gap: var(--bui-space-10); + } + + .bui-gap-11 { + gap: var(--bui-space-11); + } + + .bui-gap-12 { + gap: var(--bui-space-12); + } + + .bui-gap-13 { + gap: var(--bui-space-13); + } + + .bui-gap-14 { + gap: var(--bui-space-14); + } + + @media (width >= 640px) { + .xs\:bui-gap { + gap: var(--gap-xs); + } + + .xs\:bui-gap-0\.5 { + gap: var(--bui-space-0_5); + } + + .xs\:bui-gap-1 { + gap: var(--bui-space-1); + } + + .xs\:bui-gap-1\.5 { + gap: var(--bui-space-1_5); + } + + .xs\:bui-gap-2 { + gap: var(--bui-space-2); + } + + .xs\:bui-gap-3 { + gap: var(--bui-space-3); + } + + .xs\:bui-gap-4 { + gap: var(--bui-space-4); + } + + .xs\:bui-gap-5 { + gap: var(--bui-space-5); + } + + .xs\:bui-gap-6 { + gap: var(--bui-space-6); + } + + .xs\:bui-gap-7 { + gap: var(--bui-space-7); + } + + .xs\:bui-gap-8 { + gap: var(--bui-space-8); + } + + .xs\:bui-gap-9 { + gap: var(--bui-space-9); + } + + .xs\:bui-gap-10 { + gap: var(--bui-space-10); + } + + .xs\:bui-gap-11 { + gap: var(--bui-space-11); + } + + .xs\:bui-gap-12 { + gap: var(--bui-space-12); + } + + .xs\:bui-gap-13 { + gap: var(--bui-space-13); + } + + .xs\:bui-gap-14 { + gap: var(--bui-space-14); + } + } + + @media (width >= 768px) { + .sm\:bui-gap { + gap: var(--gap-sm); + } + + .sm\:bui-gap-0\.5 { + gap: var(--bui-space-0_5); + } + + .sm\:bui-gap-1 { + gap: var(--bui-space-1); + } + + .sm\:bui-gap-1\.5 { + gap: var(--bui-space-1_5); + } + + .sm\:bui-gap-2 { + gap: var(--bui-space-2); + } + + .sm\:bui-gap-3 { + gap: var(--bui-space-3); + } + + .sm\:bui-gap-4 { + gap: var(--bui-space-4); + } + + .sm\:bui-gap-5 { + gap: var(--bui-space-5); + } + + .sm\:bui-gap-6 { + gap: var(--bui-space-6); + } + + .sm\:bui-gap-7 { + gap: var(--bui-space-7); + } + + .sm\:bui-gap-8 { + gap: var(--bui-space-8); + } + + .sm\:bui-gap-9 { + gap: var(--bui-space-9); + } + + .sm\:bui-gap-10 { + gap: var(--bui-space-10); + } + + .sm\:bui-gap-11 { + gap: var(--bui-space-11); + } + + .sm\:bui-gap-12 { + gap: var(--bui-space-12); + } + + .sm\:bui-gap-13 { + gap: var(--bui-space-13); + } + + .sm\:bui-gap-14 { + gap: var(--bui-space-14); + } + } + + @media (width >= 1024px) { + .md\:bui-gap { + gap: var(--gap-md); + } + + .md\:bui-gap-0\.5 { + gap: var(--bui-space-0_5); + } + + .md\:bui-gap-1 { + gap: var(--bui-space-1); + } + + .md\:bui-gap-1\.5 { + gap: var(--bui-space-1_5); + } + + .md\:bui-gap-2 { + gap: var(--bui-space-2); + } + + .md\:bui-gap-3 { + gap: var(--bui-space-3); + } + + .md\:bui-gap-4 { + gap: var(--bui-space-4); + } + + .md\:bui-gap-5 { + gap: var(--bui-space-5); + } + + .md\:bui-gap-6 { + gap: var(--bui-space-6); + } + + .md\:bui-gap-7 { + gap: var(--bui-space-7); + } + + .md\:bui-gap-8 { + gap: var(--bui-space-8); + } + + .md\:bui-gap-9 { + gap: var(--bui-space-9); + } + + .md\:bui-gap-10 { + gap: var(--bui-space-10); + } + + .md\:bui-gap-11 { + gap: var(--bui-space-11); + } + + .md\:bui-gap-12 { + gap: var(--bui-space-12); + } + + .md\:bui-gap-13 { + gap: var(--bui-space-13); + } + + .md\:bui-gap-14 { + gap: var(--bui-space-14); + } + } + + @media (width >= 1280px) { + .lg\:bui-gap { + gap: var(--gap-lg); + } + + .lg\:bui-gap-0\.5 { + gap: var(--bui-space-0_5); + } + + .lg\:bui-gap-1 { + gap: var(--bui-space-1); + } + + .lg\:bui-gap-1\.5 { + gap: var(--bui-space-1_5); + } + + .lg\:bui-gap-2 { + gap: var(--bui-space-2); + } + + .lg\:bui-gap-3 { + gap: var(--bui-space-3); + } + + .lg\:bui-gap-4 { + gap: var(--bui-space-4); + } + + .lg\:bui-gap-5 { + gap: var(--bui-space-5); + } + + .lg\:bui-gap-6 { + gap: var(--bui-space-6); + } + + .lg\:bui-gap-7 { + gap: var(--bui-space-7); + } + + .lg\:bui-gap-8 { + gap: var(--bui-space-8); + } + + .lg\:bui-gap-9 { + gap: var(--bui-space-9); + } + + .lg\:bui-gap-10 { + gap: var(--bui-space-10); + } + + .lg\:bui-gap-11 { + gap: var(--bui-space-11); + } + + .lg\:bui-gap-12 { + gap: var(--bui-space-12); + } + + .lg\:bui-gap-13 { + gap: var(--bui-space-13); + } + + .lg\:bui-gap-14 { + gap: var(--bui-space-14); + } + } + + @media (width >= 1536px) { + .xl\:bui-gap { + gap: var(--gap-xl); + } + + .xl\:bui-gap-0\.5 { + gap: var(--bui-space-0_5); + } + + .xl\:bui-gap-1 { + gap: var(--bui-space-1); + } + + .xl\:bui-gap-1\.5 { + gap: var(--bui-space-1_5); + } + + .xl\:bui-gap-2 { + gap: var(--bui-space-2); + } + + .xl\:bui-gap-3 { + gap: var(--bui-space-3); + } + + .xl\:bui-gap-4 { + gap: var(--bui-space-4); + } + + .xl\:bui-gap-5 { + gap: var(--bui-space-5); + } + + .xl\:bui-gap-6 { + gap: var(--bui-space-6); + } + + .xl\:bui-gap-7 { + gap: var(--bui-space-7); + } + + .xl\:bui-gap-8 { + gap: var(--bui-space-8); + } + + .xl\:bui-gap-9 { + gap: var(--bui-space-9); + } + + .xl\:bui-gap-10 { + gap: var(--bui-space-10); + } + + .xl\:bui-gap-11 { + gap: var(--bui-space-11); + } + + .xl\:bui-gap-12 { + gap: var(--bui-space-12); + } + + .xl\:bui-gap-13 { + gap: var(--bui-space-13); + } + + .xl\:bui-gap-14 { + gap: var(--bui-space-14); + } + } + + .bui-align-start { + align-items: start; + } + + .bui-align-center { + align-items: center; + } + + .bui-align-end { + align-items: end; + } + + .bui-align-baseline { + align-items: baseline; + } + + .bui-align-stretch { + align-items: stretch; + } + + .bui-jc-start { + justify-content: start; + } + + .bui-jc-center { + justify-content: center; + } + + .bui-jc-end { + justify-content: end; + } + + .bui-jc-between { + justify-content: space-between; + } + + .bui-fd-row { + flex-direction: row; + } + + .bui-fd-column { + flex-direction: column; + } + + .bui-fd-row-reverse { + flex-direction: row-reverse; + } + + .bui-fd-column-reverse { + flex-direction: column-reverse; + } + + @media (width >= 640px) { + .xs\:bui-align-start { + align-items: start; + } + + .xs\:bui-align-center { + align-items: center; + } + + .xs\:bui-align-end { + align-items: end; + } + + .xs\:bui-align-stretch { + align-items: stretch; + } + + .xs\:bui-jc-start { + justify-content: start; + } + + .xs\:bui-jc-center { + justify-content: center; + } + + .xs\:bui-jc-end { + justify-content: end; + } + + .xs\:bui-jc-between { + justify-content: space-between; + } + + .xs\:bui-fd-row { + flex-direction: row; + } + + .xs\:bui-fd-column { + flex-direction: column; + } + + .xs\:bui-fd-row-reverse { + flex-direction: row-reverse; + } + + .xs\:bui-fd-column-reverse { + flex-direction: column-reverse; + } + } + + @media (width >= 768px) { + .sm\:bui-align-start { + align-items: start; + } + + .sm\:bui-align-center { + align-items: center; + } + + .sm\:bui-align-end { + align-items: end; + } + + .sm\:bui-align-stretch { + align-items: stretch; + } + + .sm\:bui-jc-start { + justify-content: start; + } + + .sm\:bui-jc-center { + justify-content: center; + } + + .sm\:bui-jc-end { + justify-content: end; + } + + .sm\:bui-jc-between { + justify-content: space-between; + } + + .sm\:bui-fd-row { + flex-direction: row; + } + + .sm\:bui-fd-column { + flex-direction: column; + } + + .sm\:bui-fd-row-reverse { + flex-direction: row-reverse; + } + + .sm\:bui-fd-column-reverse { + flex-direction: column-reverse; + } + } + + @media (width >= 1024px) { + .md\:bui-align-start { + align-items: start; + } + + .md\:bui-align-center { + align-items: center; + } + + .md\:bui-align-end { + align-items: end; + } + + .md\:bui-align-stretch { + align-items: stretch; + } + + .md\:bui-jc-start { + justify-content: start; + } + + .md\:bui-jc-center { + justify-content: center; + } + + .md\:bui-jc-end { + justify-content: end; + } + + .md\:bui-jc-between { + justify-content: space-between; + } + + .md\:bui-fd-row { + flex-direction: row; + } + + .md\:bui-fd-column { + flex-direction: column; + } + + .md\:bui-fd-row-reverse { + flex-direction: row-reverse; + } + + .md\:bui-fd-column-reverse { + flex-direction: column-reverse; + } + } + + @media (width >= 1280px) { + .lg\:bui-align-start { + align-items: start; + } + + .lg\:bui-align-center { + align-items: center; + } + + .lg\:bui-align-end { + align-items: end; + } + + .lg\:bui-align-stretch { + align-items: stretch; + } + + .lg\:bui-jc-start { + justify-content: start; + } + + .lg\:bui-jc-center { + justify-content: center; + } + + .lg\:bui-jc-end { + justify-content: end; + } + + .lg\:bui-jc-between { + justify-content: space-between; + } + + .lg\:bui-fd-row { + flex-direction: row; + } + + .lg\:bui-fd-column { + flex-direction: column; + } + + .lg\:bui-fd-row-reverse { + flex-direction: row-reverse; + } + + .lg\:bui-fd-column-reverse { + flex-direction: column-reverse; + } + } + + @media (width >= 1536px) { + .xl\:bui-align-start { + align-items: start; + } + + .xl\:bui-align-center { + align-items: center; + } + + .xl\:bui-align-end { + align-items: end; + } + + .xl\:bui-align-stretch { + align-items: stretch; + } + + .xl\:bui-jc-start { + justify-content: start; + } + + .xl\:bui-jc-center { + justify-content: center; + } + + .xl\:bui-jc-end { + justify-content: end; + } + + .xl\:bui-jc-between { + justify-content: space-between; + } + + .xl\:bui-fd-row { + flex-direction: row; + } + + .xl\:bui-fd-column { + flex-direction: column; + } + + .xl\:bui-fd-row-reverse { + flex-direction: row-reverse; + } + + .xl\:bui-fd-column-reverse { + flex-direction: column-reverse; + } } } diff --git a/packages/ui/package.json b/packages/ui/package.json index b4432c8b6b..767af01601 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/ui", - "version": "0.7.2-next.0", + "version": "0.9.0-next.3", "backstage": { "role": "web-library" }, @@ -19,7 +19,9 @@ "directory": "packages/ui" }, "license": "Apache-2.0", - "sideEffects": true, + "sideEffects": [ + "*.css" + ], "main": "src/index.ts", "types": "src/index.ts", "files": [ @@ -38,11 +40,10 @@ "test": "backstage-cli package test" }, "dependencies": { - "@base-ui-components/react": "1.0.0-alpha.7", "@remixicon/react": "^4.6.0", "@tanstack/react-table": "^8.21.3", "clsx": "^2.1.1", - "react-aria-components": "^1.10.1" + "react-aria-components": "^1.13.0" }, "devDependencies": { "@backstage/cli": "workspace:^", diff --git a/packages/ui/report.api.md b/packages/ui/report.api.md index 5d04bf8626..4f6087b0f0 100644 --- a/packages/ui/report.api.md +++ b/packages/ui/report.api.md @@ -3,16 +3,21 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { Avatar as Avatar_2 } from '@base-ui-components/react/avatar'; import { ButtonProps as ButtonProps_2 } from 'react-aria-components'; import { CellProps as CellProps_2 } from 'react-aria-components'; -import { Collapsible as Collapsible_2 } from '@base-ui-components/react/collapsible'; -import { ColumnProps } from 'react-aria-components'; +import { CheckboxProps as CheckboxProps_2 } from 'react-aria-components'; +import { ColumnProps as ColumnProps_2 } from 'react-aria-components'; import { ComponentProps } from 'react'; import type { ComponentPropsWithRef } from 'react'; -import { Context } from 'react'; +import { DetailedHTMLProps } from 'react'; +import type { DialogTriggerProps as DialogTriggerProps_2 } from 'react-aria-components'; +import type { DisclosureGroupProps } from 'react-aria-components'; +import type { DisclosurePanelProps } from 'react-aria-components'; +import type { DisclosureProps } from 'react-aria-components'; import type { ElementType } from 'react'; import { ForwardRefExoticComponent } from 'react'; +import type { HeadingProps } from 'react-aria-components'; +import { HTMLAttributes } from 'react'; import { JSX as JSX_2 } from 'react/jsx-runtime'; import { LinkProps as LinkProps_2 } from 'react-aria-components'; import type { ListBoxItemProps } from 'react-aria-components'; @@ -21,15 +26,14 @@ import type { MenuItemProps as MenuItemProps_2 } from 'react-aria-components'; import type { MenuProps as MenuProps_2 } from 'react-aria-components'; import type { MenuSectionProps as MenuSectionProps_2 } from 'react-aria-components'; import type { MenuTriggerProps as MenuTriggerProps_2 } from 'react-aria-components'; +import type { ModalOverlayProps } from 'react-aria-components'; import type { PopoverProps } from 'react-aria-components'; import type { RadioGroupProps as RadioGroupProps_2 } from 'react-aria-components'; import type { RadioProps as RadioProps_2 } from 'react-aria-components'; import { ReactElement } from 'react'; import { ReactNode } from 'react'; import { RefAttributes } from 'react'; -import type { RemixiconComponentType } from '@remixicon/react'; import { RowProps } from 'react-aria-components'; -import { ScrollArea as ScrollArea_2 } from '@base-ui-components/react/scroll-area'; import type { SearchFieldProps as SearchFieldProps_2 } from 'react-aria-components'; import type { SelectProps as SelectProps_2 } from 'react-aria-components'; import type { SeparatorProps } from 'react-aria-components'; @@ -50,38 +54,71 @@ import { TooltipProps as TooltipProps_2 } from 'react-aria-components'; import { TooltipTriggerComponentProps } from 'react-aria-components'; // @public (undocumented) -export type AlignItems = 'stretch' | 'start' | 'center' | 'end'; - -// @public (undocumented) -export type ArbitraryStylingPropDef = { - className: string; - customProperties: `--${string}`[]; - parseValue?: (value: string) => string | undefined; -}; - -// @public (undocumented) -export const Avatar: ForwardRefExoticComponent< - AvatarProps & RefAttributes +export const Accordion: ForwardRefExoticComponent< + AccordionProps & RefAttributes >; // @public (undocumented) -export interface AvatarProps - extends React.ComponentPropsWithoutRef { +export const AccordionGroup: ForwardRefExoticComponent< + AccordionGroupProps & RefAttributes +>; + +// @public +export interface AccordionGroupProps extends DisclosureGroupProps { + allowsMultiple?: boolean; // (undocumented) - name: string; - // (undocumented) - size?: 'small' | 'medium' | 'large'; - // (undocumented) - src: string; + className?: string; } // @public (undocumented) -export type BooleanPropDef = { - type: 'boolean'; - default?: boolean; - required?: boolean; +export const AccordionPanel: ForwardRefExoticComponent< + AccordionPanelProps & RefAttributes +>; + +// @public +export interface AccordionPanelProps extends DisclosurePanelProps { + // (undocumented) className?: string; -}; +} + +// @public +export interface AccordionProps extends DisclosureProps { + // (undocumented) + className?: string; +} + +// @public (undocumented) +export const AccordionTrigger: ForwardRefExoticComponent< + AccordionTriggerProps & RefAttributes +>; + +// @public +export interface AccordionTriggerProps extends HeadingProps { + // (undocumented) + children?: React.ReactNode; + // (undocumented) + className?: string; + // (undocumented) + subtitle?: string; + // (undocumented) + title?: string; +} + +// @public (undocumented) +export type AlignItems = 'stretch' | 'start' | 'center' | 'end'; + +// @public (undocumented) +export const Avatar: ForwardRefExoticComponent< + AvatarProps & RefAttributes +>; + +// @public (undocumented) +export interface AvatarProps extends React.ComponentPropsWithoutRef<'div'> { + name: string; + purpose?: 'decoration' | 'informative'; + size?: 'x-small' | 'small' | 'medium' | 'large' | 'x-large'; + src: string; +} // @public (undocumented) export type Border = 'none' | 'base' | 'error' | 'warning' | 'selected'; @@ -102,52 +139,39 @@ export const Box: ForwardRefExoticComponent< BoxProps & RefAttributes >; -// @public (undocumented) -export type BoxOwnProps = GetPropDefTypes; - -// @public (undocumented) -export const boxPropDefs: { - as: { - type: 'enum'; - values: readonly ['div', 'span']; - default: 'div'; - }; -}; - // @public (undocumented) export interface BoxProps extends SpaceProps { // (undocumented) - as?: BoxOwnProps['as']; + as?: keyof JSX.IntrinsicElements; // (undocumented) children?: React.ReactNode; // (undocumented) className?: string; // (undocumented) - display?: DisplayProps['display']; + display?: Responsive<'none' | 'flex' | 'block' | 'inline'>; // (undocumented) - height?: HeightProps['height']; + height?: Responsive; // (undocumented) - maxHeight?: HeightProps['maxHeight']; + maxHeight?: Responsive; // (undocumented) - maxWidth?: WidthProps['maxWidth']; + maxWidth?: Responsive; // (undocumented) - minHeight?: HeightProps['minHeight']; + minHeight?: Responsive; // (undocumented) - minWidth?: WidthProps['minWidth']; + minWidth?: Responsive; // (undocumented) - position?: PositionProps['position']; + position?: Responsive< + 'static' | 'relative' | 'absolute' | 'fixed' | 'sticky' + >; // (undocumented) style?: React.CSSProperties; // (undocumented) - width?: WidthProps['width']; + width?: Responsive; } // @public (undocumented) export type Breakpoint = 'initial' | 'xs' | 'sm' | 'md' | 'lg' | 'xl'; -// @public (undocumented) -export const breakpoints: Breakpoint[]; - // @public (undocumented) export const Button: ForwardRefExoticComponent< ButtonProps & RefAttributes @@ -163,6 +187,8 @@ export interface ButtonIconProps extends ButtonProps_2 { // (undocumented) icon?: ReactElement; // (undocumented) + loading?: boolean; + // (undocumented) size?: 'small' | 'medium' | Partial>; // (undocumented) variant?: @@ -204,6 +230,8 @@ export interface ButtonProps extends ButtonProps_2 { // (undocumented) iconStart?: ReactElement; // (undocumented) + loading?: boolean; + // (undocumented) size?: 'small' | 'medium' | Partial>; // (undocumented) variant?: @@ -296,64 +324,42 @@ export interface CellProps extends CellProps_2 { // @public (undocumented) export const Checkbox: ForwardRefExoticComponent< - CheckboxProps & RefAttributes + CheckboxProps & RefAttributes >; // @public (undocumented) -export interface CheckboxProps { +export interface CheckboxProps extends CheckboxProps_2 { // (undocumented) - checked?: boolean; - // (undocumented) - className?: string; - // (undocumented) - defaultChecked?: boolean; - // (undocumented) - disabled?: boolean; - // (undocumented) - label?: string; - // (undocumented) - name?: string; - // (undocumented) - onChange?: (checked: boolean) => void; - // (undocumented) - required?: boolean; - // (undocumented) - style?: React.CSSProperties; - // (undocumented) - value?: string; + children: React.ReactNode; } // @public export type ClassNamesMap = Record; -// @public -export const Collapsible: { - Root: ForwardRefExoticComponent< - Omit, 'ref'> & - RefAttributes - >; - Trigger: ForwardRefExoticComponent< - Omit< - Collapsible_2.Trigger.Props & RefAttributes, - 'ref' - > & - RefAttributes - >; - Panel: ForwardRefExoticComponent< - Omit, 'ref'> & - RefAttributes - >; -}; +// @public (undocumented) +export const Column: (props: ColumnProps) => JSX_2.Element; // @public (undocumented) -export const Column: ( - props: Omit & { - children?: React.ReactNode; - }, -) => JSX_2.Element; +export interface ColumnProps extends Omit { + // (undocumented) + children?: React.ReactNode; +} // @public (undocumented) -export type Columns = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 'auto'; +export type Columns = + | '1' + | '2' + | '3' + | '4' + | '5' + | '6' + | '7' + | '8' + | '9' + | '10' + | '11' + | '12' + | 'auto'; // @public export type ComponentClassNames = @@ -365,6 +371,8 @@ export interface ComponentDefinition { classNames: ClassNamesMap; // (undocumented) dataAttributes?: DataAttributesMap; + // (undocumented) + utilityProps?: string[]; } // @public @@ -386,19 +394,48 @@ export const componentDefinitions: { readonly classNames: { readonly root: 'bui-Box'; }; + readonly utilityProps: [ + 'm', + 'mb', + 'ml', + 'mr', + 'mt', + 'mx', + 'my', + 'p', + 'pb', + 'pl', + 'pr', + 'pt', + 'px', + 'py', + 'position', + 'display', + 'width', + 'minWidth', + 'maxWidth', + 'height', + 'minHeight', + 'maxHeight', + ]; }; readonly Button: { readonly classNames: { readonly root: 'bui-Button'; + readonly content: 'bui-ButtonContent'; + readonly spinner: 'bui-ButtonSpinner'; }; readonly dataAttributes: { readonly size: readonly ['small', 'medium', 'large']; readonly variant: readonly ['primary', 'secondary', 'tertiary']; + readonly loading: readonly [true, false]; }; }; readonly ButtonIcon: { readonly classNames: { readonly root: 'bui-ButtonIcon'; + readonly content: 'bui-ButtonIconContent'; + readonly spinner: 'bui-ButtonIconSpinner'; }; }; readonly ButtonLink: { @@ -416,25 +453,45 @@ export const componentDefinitions: { }; readonly Checkbox: { readonly classNames: { - readonly root: 'bui-CheckboxRoot'; - readonly label: 'bui-CheckboxLabel'; + readonly root: 'bui-Checkbox'; readonly indicator: 'bui-CheckboxIndicator'; }; readonly dataAttributes: { - readonly checked: readonly [true, false]; - }; - }; - readonly Collapsible: { - readonly classNames: { - readonly root: 'bui-CollapsibleRoot'; - readonly trigger: 'bui-CollapsibleTrigger'; - readonly panel: 'bui-CollapsiblePanel'; + readonly selected: readonly [true, false]; }; }; readonly Container: { readonly classNames: { readonly root: 'bui-Container'; }; + readonly utilityProps: ['my', 'mt', 'mb', 'py', 'pt', 'pb', 'display']; + }; + readonly Dialog: { + readonly classNames: { + readonly overlay: 'bui-DialogOverlay'; + readonly dialog: 'bui-Dialog'; + readonly header: 'bui-DialogHeader'; + readonly headerTitle: 'bui-DialogHeaderTitle'; + readonly body: 'bui-DialogBody'; + readonly footer: 'bui-DialogFooter'; + }; + }; + readonly Accordion: { + readonly classNames: { + readonly root: 'bui-Accordion'; + readonly trigger: 'bui-AccordionTrigger'; + readonly triggerButton: 'bui-AccordionTriggerButton'; + readonly triggerTitle: 'bui-AccordionTriggerTitle'; + readonly triggerSubtitle: 'bui-AccordionTriggerSubtitle'; + readonly triggerIcon: 'bui-AccordionTriggerIcon'; + readonly panel: 'bui-AccordionPanel'; + readonly group: 'bui-AccordionGroup'; + }; + }; + readonly FieldError: { + readonly classNames: { + readonly root: 'bui-FieldError'; + }; }; readonly FieldLabel: { readonly classNames: { @@ -448,12 +505,55 @@ export const componentDefinitions: { readonly classNames: { readonly root: 'bui-Flex'; }; + readonly utilityProps: [ + 'm', + 'mb', + 'ml', + 'mr', + 'mt', + 'mx', + 'my', + 'p', + 'pb', + 'pl', + 'pr', + 'pt', + 'px', + 'py', + 'gap', + 'align', + 'justify', + 'direction', + ]; }; readonly Grid: { readonly classNames: { readonly root: 'bui-Grid'; - readonly item: 'bui-GridItem'; }; + readonly utilityProps: [ + 'columns', + 'gap', + 'm', + 'mb', + 'ml', + 'mr', + 'mt', + 'mx', + 'my', + 'p', + 'pb', + 'pl', + 'pr', + 'pt', + 'px', + 'py', + ]; + }; + readonly GridItem: { + readonly classNames: { + readonly root: 'bui-GridItem'; + }; + readonly utilityProps: ['colSpan', 'colEnd', 'colStart', 'rowSpan']; }; readonly Header: { readonly classNames: { @@ -497,6 +597,14 @@ export const componentDefinitions: { readonly dataAttributes: { readonly variant: readonly ['subtitle', 'body', 'caption', 'label']; readonly weight: readonly ['regular', 'bold']; + readonly color: readonly [ + 'primary', + 'secondary', + 'danger', + 'warning', + 'success', + ]; + readonly truncate: readonly [true, false]; }; }; readonly List: { @@ -516,6 +624,7 @@ export const componentDefinitions: { readonly item: 'bui-MenuItem'; readonly itemListBox: 'bui-MenuItemListBox'; readonly itemListBoxCheck: 'bui-MenuItemListBoxCheck'; + readonly itemWrapper: 'bui-MenuItemWrapper'; readonly itemContent: 'bui-MenuItemContent'; readonly itemArrow: 'bui-MenuItemArrow'; readonly separator: 'bui-MenuSeparator'; @@ -525,6 +634,18 @@ export const componentDefinitions: { readonly emptyState: 'bui-MenuEmptyState'; }; }; + readonly PasswordField: { + readonly classNames: { + readonly root: 'bui-PasswordField'; + readonly inputWrapper: 'bui-PasswordFieldInputWrapper'; + readonly input: 'bui-PasswordFieldInput'; + readonly inputIcon: 'bui-PasswordFieldIcon'; + readonly inputVisibility: 'bui-PasswordFieldVisibility'; + }; + readonly dataAttributes: { + readonly size: readonly ['small', 'medium']; + }; + }; readonly Popover: { readonly classNames: { readonly root: 'bui-Popover'; @@ -537,33 +658,34 @@ export const componentDefinitions: { readonly radio: 'bui-Radio'; }; }; - readonly ScrollArea: { - readonly classNames: { - readonly root: 'bui-ScrollAreaRoot'; - readonly viewport: 'bui-ScrollAreaViewport'; - readonly scrollbar: 'bui-ScrollAreaScrollbar'; - readonly thumb: 'bui-ScrollAreaThumb'; - }; - }; readonly SearchField: { readonly classNames: { readonly root: 'bui-SearchField'; - readonly clear: 'bui-InputClear'; + readonly clear: 'bui-SearchFieldClear'; + readonly inputWrapper: 'bui-SearchFieldInputWrapper'; + readonly input: 'bui-SearchFieldInput'; + readonly inputIcon: 'bui-SearchFieldInputIcon'; }; readonly dataAttributes: { readonly startCollapsed: readonly [true, false]; + readonly size: readonly ['small', 'medium']; }; }; readonly Select: { readonly classNames: { readonly root: 'bui-Select'; + readonly popover: 'bui-SelectPopover'; readonly trigger: 'bui-SelectTrigger'; + readonly chevron: 'bui-SelectTriggerChevron'; readonly value: 'bui-SelectValue'; - readonly icon: 'bui-SelectIcon'; readonly list: 'bui-SelectList'; readonly item: 'bui-SelectItem'; readonly itemIndicator: 'bui-SelectItemIndicator'; readonly itemLabel: 'bui-SelectItemLabel'; + readonly searchWrapper: 'bui-SelectSearchWrapper'; + readonly search: 'bui-SelectSearch'; + readonly searchClear: 'bui-SelectSearchClear'; + readonly noResults: 'bui-SelectNoResults'; }; readonly dataAttributes: { readonly size: readonly ['small', 'medium']; @@ -587,6 +709,7 @@ export const componentDefinitions: { readonly body: 'bui-TableBody'; readonly row: 'bui-TableRow'; readonly head: 'bui-TableHead'; + readonly headContent: 'bui-TableHeadContent'; readonly headSortButton: 'bui-TableHeadSortButton'; readonly caption: 'bui-TableCaption'; readonly cell: 'bui-TableCell'; @@ -600,6 +723,14 @@ export const componentDefinitions: { readonly cellProfileLink: 'bui-TableCellProfileLink'; }; }; + readonly TablePagination: { + readonly classNames: { + readonly root: 'bui-TablePagination'; + readonly left: 'bui-TablePaginationLeft'; + readonly right: 'bui-TablePaginationRight'; + readonly select: 'bui-TablePaginationSelect'; + }; + }; readonly Tabs: { readonly classNames: { readonly tabs: 'bui-Tabs'; @@ -643,10 +774,12 @@ export const componentDefinitions: { readonly inputWrapper: 'bui-InputWrapper'; readonly input: 'bui-Input'; readonly inputIcon: 'bui-InputIcon'; + readonly inputAction: 'bui-InputAction'; }; readonly dataAttributes: { readonly invalid: readonly [true, false]; readonly disabled: readonly [true, false]; + readonly size: readonly ['small', 'medium']; }; }; readonly Tooltip: { @@ -655,6 +788,11 @@ export const componentDefinitions: { readonly arrow: 'bui-TooltipArrow'; }; }; + readonly VisuallyHidden: { + readonly classNames: { + readonly root: 'bui-VisuallyHidden'; + }; + }; }; // @public (undocumented) @@ -690,45 +828,75 @@ export type DataAttributesMap = Record; // @public export type DataAttributeValues = readonly (string | number | boolean)[]; +// @public (undocumented) +export const Dialog: ForwardRefExoticComponent< + DialogProps & RefAttributes +>; + +// @public (undocumented) +export const DialogBody: ForwardRefExoticComponent< + DialogBodyProps & RefAttributes +>; + +// @public +export interface DialogBodyProps { + // (undocumented) + children?: React.ReactNode; + // (undocumented) + className?: string; +} + +// @public (undocumented) +export const DialogFooter: ForwardRefExoticComponent< + Omit< + DetailedHTMLProps, HTMLDivElement>, + 'ref' + > & + RefAttributes +>; + +// @public (undocumented) +export const DialogHeader: ForwardRefExoticComponent< + DialogHeaderProps & RefAttributes +>; + +// @public +export interface DialogHeaderProps extends HeadingProps { + // (undocumented) + children?: React.ReactNode; + // (undocumented) + className?: string; +} + +// @public +export interface DialogProps extends ModalOverlayProps { + // (undocumented) + children?: React.ReactNode; + // (undocumented) + className?: string; + // (undocumented) + height?: number | string; + // (undocumented) + width?: number | string; +} + +// @public (undocumented) +export const DialogTrigger: (props: DialogTriggerProps) => JSX_2.Element; + +// @public +export interface DialogTriggerProps extends DialogTriggerProps_2 {} + // @public (undocumented) export type Display = 'none' | 'flex' | 'block' | 'inline'; -// @public (undocumented) -export const displayPropDefs: { - display: { - type: 'enum'; - className: string; - values: readonly ['none', 'inline', 'inline-block', 'block']; - responsive: true; - }; -}; - -// @public (undocumented) -export type DisplayProps = GetPropDefTypes; - -// @public (undocumented) -export type EnumOrStringPropDef = { - type: 'enum | string'; - values: readonly T[]; - default?: T | string; - required?: boolean; -}; - -// @public (undocumented) -export type EnumPropDef = { - type: 'enum'; - values: readonly T[]; - default?: T; - required?: boolean; -}; - // @public (undocumented) export const FieldLabel: ForwardRefExoticComponent< FieldLabelProps & RefAttributes >; // @public (undocumented) -export interface FieldLabelProps { +export interface FieldLabelProps + extends Pick, 'className'> { description?: string | null; htmlFor?: string; id?: string; @@ -744,45 +912,20 @@ export const Flex: ForwardRefExoticComponent< // @public (undocumented) export type FlexDirection = 'row' | 'column'; -// @public (undocumented) -export type FlexOwnProps = GetPropDefTypes; - -// @public (undocumented) -export const flexPropDefs: { - align: { - type: 'enum'; - className: string; - values: readonly ['start', 'center', 'end', 'baseline', 'stretch']; - responsive: true; - }; - direction: { - type: 'enum'; - className: string; - values: readonly ['row', 'column', 'row-reverse', 'column-reverse']; - responsive: true; - }; - justify: { - type: 'enum'; - className: string; - values: readonly ['start', 'center', 'end', 'between']; - responsive: true; - }; -}; - // @public (undocumented) export interface FlexProps extends SpaceProps { // (undocumented) - align?: FlexOwnProps['align']; + align?: Responsive<'start' | 'center' | 'end' | 'baseline' | 'stretch'>; // (undocumented) - children: React.ReactNode; + children?: React.ReactNode; // (undocumented) className?: string; // (undocumented) - direction?: FlexOwnProps['direction']; + direction?: Responsive<'row' | 'column' | 'row-reverse' | 'column-reverse'>; // (undocumented) - gap?: GapProps['gap']; + gap?: Responsive; // (undocumented) - justify?: FlexOwnProps['justify']; + justify?: Responsive<'start' | 'center' | 'end' | 'between'>; // (undocumented) style?: React.CSSProperties; } @@ -790,49 +933,6 @@ export interface FlexProps extends SpaceProps { // @public (undocumented) export type FlexWrap = 'wrap' | 'nowrap' | 'wrap-reverse'; -// @public (undocumented) -export const gapPropDefs: { - gap: { - type: 'enum | string'; - className: string; - customProperties: '--gap'[]; - values: string[]; - responsive: true; - default: string; - }; -}; - -// @public (undocumented) -export type GapProps = GetPropDefTypes; - -// @public (undocumented) -export type GetPropDefType = Def extends BooleanPropDef - ? Def extends ResponsivePropDef - ? Responsive - : boolean - : Def extends StringPropDef - ? Def extends ResponsivePropDef - ? Responsive - : string - : Def extends ReactNodePropDef - ? Def extends ResponsivePropDef - ? Responsive - : ReactNode - : Def extends EnumOrStringPropDef - ? Def extends ResponsivePropDef - ? Responsive - : string | Type - : Def extends EnumPropDef - ? Def extends ResponsivePropDef - ? Responsive - : Type - : never; - -// @public (undocumented) -export type GetPropDefTypes

    = { - [K in keyof P]?: GetPropDefType; -}; - // @public (undocumented) export const Grid: { Root: ForwardRefExoticComponent>; @@ -841,97 +941,6 @@ export const Grid: { >; }; -// @public (undocumented) -export type GridItemOwnProps = GetPropDefTypes; - -// @public (undocumented) -export const gridItemPropDefs: { - colSpan: { - type: 'enum | string'; - className: string; - customProperties: '--col-span'[]; - values: readonly [ - '1', - '2', - '3', - '4', - '5', - '6', - '7', - '8', - '9', - '10', - '11', - '12', - 'auto', - ]; - responsive: true; - }; - colEnd: { - type: 'enum | string'; - className: string; - customProperties: '--col-end'[]; - values: readonly [ - '1', - '2', - '3', - '4', - '5', - '6', - '7', - '8', - '9', - '10', - '11', - '12', - 'auto', - ]; - responsive: true; - }; - colStart: { - type: 'enum | string'; - className: string; - customProperties: '--col-start'[]; - values: readonly [ - '1', - '2', - '3', - '4', - '5', - '6', - '7', - '8', - '9', - '10', - '11', - '12', - 'auto', - ]; - responsive: true; - }; - rowSpan: { - type: 'enum | string'; - className: string; - customProperties: '--row-span'[]; - values: readonly [ - '1', - '2', - '3', - '4', - '5', - '6', - '7', - '8', - '9', - '10', - '11', - '12', - 'auto', - ]; - responsive: true; - }; -}; - // @public (undocumented) export interface GridItemProps { // (undocumented) @@ -939,46 +948,17 @@ export interface GridItemProps { // (undocumented) className?: string; // (undocumented) - colEnd?: GridItemOwnProps['colEnd']; + colEnd?: Responsive; // (undocumented) - colSpan?: GridItemOwnProps['colSpan']; + colSpan?: Responsive; // (undocumented) - colStart?: GridItemOwnProps['colStart']; + colStart?: Responsive; // (undocumented) - rowSpan?: GridItemOwnProps['rowSpan']; + rowSpan?: Responsive; // (undocumented) style?: React.CSSProperties; } -// @public (undocumented) -export type GridOwnProps = GetPropDefTypes; - -// @public (undocumented) -export const gridPropDefs: { - columns: { - type: 'enum | string'; - className: string; - customProperties: '--columns'[]; - values: readonly [ - '1', - '2', - '3', - '4', - '5', - '6', - '7', - '8', - '9', - '10', - '11', - '12', - 'auto', - ]; - responsive: true; - default: string; - }; -}; - // @public (undocumented) export interface GridProps extends SpaceProps { // (undocumented) @@ -986,9 +966,9 @@ export interface GridProps extends SpaceProps { // (undocumented) className?: string; // (undocumented) - columns?: GridOwnProps['columns']; + columns?: Responsive; // (undocumented) - gap?: GapProps['gap']; + gap?: Responsive; // (undocumented) style?: React.CSSProperties; } @@ -1012,6 +992,8 @@ export interface HeaderPageProps { // (undocumented) breadcrumbs?: HeaderPageBreadcrumb[]; // (undocumented) + className?: string; + // (undocumented) customActions?: React.ReactNode; // (undocumented) tabs?: HeaderTab[]; @@ -1021,6 +1003,8 @@ export interface HeaderPageProps { // @public export interface HeaderProps { + // (undocumented) + className?: string; // (undocumented) customActions?: React.ReactNode; // (undocumented) @@ -1038,7 +1022,7 @@ export interface HeaderProps { // @public export interface HeaderTab { // (undocumented) - href?: string; + href: string; // (undocumented) id: string; // (undocumented) @@ -1046,127 +1030,6 @@ export interface HeaderTab { matchStrategy?: TabMatchStrategy; } -// @public (undocumented) -export const heightPropDefs: { - height: { - type: 'string'; - className: string; - customProperties: '--height'[]; - responsive: true; - }; - minHeight: { - type: 'string'; - className: string; - customProperties: '--min-height'[]; - responsive: true; - }; - maxHeight: { - type: 'string'; - className: string; - customProperties: '--max-height'[]; - responsive: true; - }; -}; - -// @public (undocumented) -export type HeightProps = GetPropDefTypes; - -// @public (undocumented) -export const Icon: (props: IconProps) => JSX_2.Element | null; - -// @public (undocumented) -export const IconContext: Context; - -// @public (undocumented) -export interface IconContextProps { - // (undocumented) - icons: IconMap; -} - -// @public (undocumented) -export type IconMap = Partial>; - -// @public (undocumented) -export type IconNames = - | 'account-circle' - | 'alert' - | 'arrow-down' - | 'arrow-down-circle' - | 'caret-down' - | 'caret-left' - | 'caret-right' - | 'caret-up' - | 'arrow-left' - | 'arrow-left-circle' - | 'arrow-left-down' - | 'arrow-left-up' - | 'arrow-right' - | 'arrow-right-circle' - | 'arrow-right-down' - | 'arrow-right-up' - | 'arrow-up' - | 'arrow-up-circle' - | 'braces' - | 'brackets' - | 'bug' - | 'check' - | 'check-double' - | 'chevron-down' - | 'chevron-left' - | 'chevron-right' - | 'chevron-up' - | 'close' - | 'cloud' - | 'code' - | 'discord' - | 'download' - | 'external-link' - | 'eye' - | 'eye-off' - | 'filter' - | 'flower' - | 'github' - | 'git-repository' - | 'group' - | 'heart' - | 'moon' - | 'plus' - | 'search' - | 'sidebar-fold' - | 'sidebar-unfold' - | 'sparkling' - | 'star' - | 'sun' - | 'terminal' - | 'trash' - | 'upload' - | 'user' - | 'youtube' - | 'zoom-in' - | 'zoom-out'; - -// @public (undocumented) -export type IconProps = { - name: IconNames; - size?: number; - className?: string; - style?: React.CSSProperties; -}; - -// @public (undocumented) -export const IconProvider: (props: IconProviderProps) => JSX_2.Element; - -// @public (undocumented) -export interface IconProviderProps { - // (undocumented) - children?: ReactNode; - // (undocumented) - overrides?: Partial>; -} - -// @public (undocumented) -export const icons: IconMap; - // @public (undocumented) export type JustifyContent = | 'stretch' @@ -1196,62 +1059,6 @@ export interface LinkProps extends LinkProps_2 { weight?: TextWeights | Partial>; } -// @public (undocumented) -export const marginPropDefs: (spacingValues: string[]) => { - m: { - type: 'enum | string'; - values: string[]; - className: string; - customProperties: '--m'[]; - responsive: true; - }; - mx: { - type: 'enum | string'; - values: string[]; - className: string; - customProperties: '--mx'[]; - responsive: true; - }; - my: { - type: 'enum | string'; - values: string[]; - className: string; - customProperties: '--my'[]; - responsive: true; - }; - mt: { - type: 'enum | string'; - values: string[]; - className: string; - customProperties: '--mt'[]; - responsive: true; - }; - mr: { - type: 'enum | string'; - values: string[]; - className: string; - customProperties: '--mr'[]; - responsive: true; - }; - mb: { - type: 'enum | string'; - values: string[]; - className: string; - customProperties: '--mb'[]; - responsive: true; - }; - ml: { - type: 'enum | string'; - values: string[]; - className: string; - customProperties: '--ml'[]; - responsive: true; - }; -}; - -// @public (undocumented) -export type MarginProps = GetPropDefTypes; - // @public (undocumented) export const Menu: (props: MenuProps) => JSX_2.Element; @@ -1270,9 +1077,15 @@ export interface MenuAutocompleteListBoxProps extends ListBoxProps, Omit, 'children'> { // (undocumented) + maxHeight?: string; + // (undocumented) + maxWidth?: string; + // (undocumented) placeholder?: string; // (undocumented) placement?: PopoverProps['placement']; + // (undocumented) + virtualized?: boolean; } // @public (undocumented) @@ -1280,9 +1093,15 @@ export interface MenuAutocompleteProps extends MenuProps_2, Omit, 'children'> { // (undocumented) + maxHeight?: string; + // (undocumented) + maxWidth?: string; + // (undocumented) placeholder?: string; // (undocumented) placement?: PopoverProps['placement']; + // (undocumented) + virtualized?: boolean; } // @public (undocumented) @@ -1319,7 +1138,13 @@ export interface MenuListBoxProps extends ListBoxProps, Omit, 'children'> { // (undocumented) + maxHeight?: string; + // (undocumented) + maxWidth?: string; + // (undocumented) placement?: PopoverProps['placement']; + // (undocumented) + virtualized?: boolean; } // @public (undocumented) @@ -1327,7 +1152,13 @@ export interface MenuProps extends MenuProps_2, Omit, 'children'> { // (undocumented) + maxHeight?: string; + // (undocumented) + maxWidth?: string; + // (undocumented) placement?: PopoverProps['placement']; + // (undocumented) + virtualized?: boolean; } // @public (undocumented) @@ -1356,83 +1187,12 @@ export const MenuTrigger: (props: MenuTriggerProps) => JSX_2.Element; export interface MenuTriggerProps extends MenuTriggerProps_2 {} // @public (undocumented) -export type NonStylingPropDef = { - className?: never; - customProperties?: never; - parseValue?: never; +type Option_2 = { + value: string; + label: string; + disabled?: boolean; }; - -// @public (undocumented) -export const paddingPropDefs: (spacingValues: string[]) => { - p: { - type: 'enum | string'; - className: string; - customProperties: '--p'[]; - values: string[]; - responsive: true; - }; - px: { - type: 'enum | string'; - className: string; - customProperties: '--px'[]; - values: string[]; - responsive: true; - }; - py: { - type: 'enum | string'; - className: string; - customProperties: '--py'[]; - values: string[]; - responsive: true; - }; - pt: { - type: 'enum | string'; - className: string; - customProperties: '--pt'[]; - values: string[]; - responsive: true; - }; - pr: { - type: 'enum | string'; - className: string; - customProperties: '--pr'[]; - values: string[]; - responsive: true; - }; - pb: { - type: 'enum | string'; - className: string; - customProperties: '--pb'[]; - values: string[]; - responsive: true; - }; - pl: { - type: 'enum | string'; - className: string; - customProperties: '--pl'[]; - values: string[]; - responsive: true; - }; -}; - -// @public (undocumented) -export type PaddingProps = GetPropDefTypes; - -// @public (undocumented) -export const positionPropDefs: { - position: { - type: 'enum'; - className: string; - values: readonly ['static', 'relative', 'absolute', 'fixed', 'sticky']; - responsive: true; - }; -}; - -// @public (undocumented) -export type PositionProps = GetPropDefTypes; - -// @public (undocumented) -export type PropDef = RegularPropDef | ResponsivePropDef; +export { Option_2 as Option }; // @public (undocumented) export const Radio: ForwardRefExoticComponent< @@ -1447,7 +1207,7 @@ export const RadioGroup: ForwardRefExoticComponent< // @public (undocumented) export interface RadioGroupProps extends Omit, - Omit { + Omit { // (undocumented) children?: ReactNode; } @@ -1455,60 +1215,11 @@ export interface RadioGroupProps // @public (undocumented) export interface RadioProps extends RadioProps_2 {} -// @public (undocumented) -export type ReactNodePropDef = { - type: 'ReactNode'; - default?: ReactNode; - required?: boolean; -}; - -// @public (undocumented) -export type RegularPropDef = - | ReactNodePropDef - | BooleanPropDef - | (StringPropDef & ArbitraryStylingPropDef) - | (StringPropDef & NonStylingPropDef) - | (EnumPropDef & StylingPropDef) - | (EnumPropDef & NonStylingPropDef) - | (EnumOrStringPropDef & ArbitraryStylingPropDef) - | (EnumOrStringPropDef & NonStylingPropDef); - // @public (undocumented) export type Responsive = T | Partial>; // @public (undocumented) -export type ResponsivePropDef = RegularPropDef & { - responsive: true; -}; - -// @public (undocumented) -export function Row({ - id, - columns, - children, - href, - ...otherProps -}: RowProps): JSX_2.Element; - -// @public (undocumented) -export const ScrollArea: { - Root: ForwardRefExoticComponent< - Omit, 'ref'> & - RefAttributes - >; - Viewport: ForwardRefExoticComponent< - Omit, 'ref'> & - RefAttributes - >; - Scrollbar: ForwardRefExoticComponent< - Omit, 'ref'> & - RefAttributes - >; - Thumb: ForwardRefExoticComponent< - Omit, 'ref'> & - RefAttributes - >; -}; +export function Row(props: RowProps): JSX_2.Element; // @public (undocumented) export const SearchField: ForwardRefExoticComponent< @@ -1518,7 +1229,7 @@ export const SearchField: ForwardRefExoticComponent< // @public (undocumented) export interface SearchFieldProps extends SearchFieldProps_2, - Omit { + Omit { icon?: ReactNode | false; placeholder?: string; size?: 'small' | 'medium' | Partial>; @@ -1527,22 +1238,18 @@ export interface SearchFieldProps // @public (undocumented) export const Select: ForwardRefExoticComponent< - SelectProps & RefAttributes + SelectProps<'multiple' | 'single'> & RefAttributes >; // @public (undocumented) -export interface SelectProps - extends SelectProps_2<{ - name: string; - value: string; - }>, - Omit { +export interface SelectProps + extends SelectProps_2, + Omit { icon?: ReactNode; - options?: Array<{ - value: string; - label: string; - disabled?: boolean; - }>; + options?: Array; + searchable?: boolean; + searchPlaceholder?: string; + selectionMode?: T; size?: 'small' | 'medium' | Partial>; } @@ -1611,19 +1318,6 @@ export interface SpaceProps { py?: Responsive; } -// @public (undocumented) -export type StringPropDef = { - type: 'string'; - default?: string; - required?: boolean; -}; - -// @public (undocumented) -export type StylingPropDef = { - className: string; - parseValue?: (value: string) => string | undefined; -}; - // @public (undocumented) export const SubmenuTrigger: (props: SubmenuTriggerProps) => JSX_2.Element; @@ -1652,10 +1346,9 @@ export const TableBody: ( ) => JSX_2.Element; // @public (undocumented) -export const TableHeader: ({ - columns, - children, -}: TableHeaderProps) => JSX_2.Element; +export const TableHeader: ( + props: TableHeaderProps, +) => JSX_2.Element; // @public export function TablePagination(props: TablePaginationProps): JSX_2.Element; @@ -1713,12 +1406,9 @@ export interface TabsProps extends TabsProps_2 {} export const Tag: (props: TagProps) => JSX_2.Element; // @public -export const TagGroup: ({ - items, - children, - renderEmptyState, - ...props -}: TagGroupProps) => JSX_2.Element; +export const TagGroup: ( + props: TagGroupProps, +) => JSX_2.Element; // @public export interface TagGroupProps @@ -1756,10 +1446,11 @@ export const TextField: ForwardRefExoticComponent< // @public (undocumented) export interface TextFieldProps extends TextFieldProps_2, - Omit { + Omit { icon?: ReactNode; placeholder?: string; size?: 'small' | 'medium' | Partial>; + type?: 'text' | 'email' | 'tel' | 'url'; } // @public (undocumented) @@ -1829,9 +1520,6 @@ export const useBreakpoint: () => { down: (key: Breakpoint) => boolean; }; -// @public (undocumented) -export const useIcons: () => IconContextProps; - // @public export function useTable( config?: UseTableConfig, @@ -1906,28 +1594,12 @@ export interface UtilityProps extends SpaceProps { rowSpan?: Responsive; } -// @public (undocumented) -export const widthPropDefs: { - width: { - type: 'string'; - className: string; - customProperties: '--width'[]; - responsive: true; - }; - minWidth: { - type: 'string'; - className: string; - customProperties: '--min-width'[]; - responsive: true; - }; - maxWidth: { - type: 'string'; - className: string; - customProperties: '--max-width'[]; - responsive: true; - }; -}; +// @public +export const VisuallyHidden: (props: VisuallyHiddenProps) => JSX_2.Element; -// @public (undocumented) -export type WidthProps = GetPropDefTypes; +// @public +export interface VisuallyHiddenProps extends ComponentProps<'div'> { + // (undocumented) + children?: React.ReactNode; +} ``` diff --git a/packages/ui/scripts/build-css.mjs b/packages/ui/scripts/build-css.mjs index 245b3cf282..c2443e93e4 100644 --- a/packages/ui/scripts/build-css.mjs +++ b/packages/ui/scripts/build-css.mjs @@ -50,7 +50,13 @@ async function buildCSS(logs = true) { code: bundleCode, minify: false, }); - fs.writeFileSync(distFile, code); + + // Prepend the layer order declaration that lightningcss removes during bundling + // This is crucial to maintain the correct layer cascade order + const layerDeclaration = '@layer tokens, base, components, utilities;\n\n'; + const finalCode = layerDeclaration + code; + + fs.writeFileSync(distFile, finalCode); if (logs) { console.log(chalk.blue('CSS transformed and minified: ') + 'styles.css'); console.log(chalk.green('CSS file built successfully!')); diff --git a/packages/ui/src/components/Accordion/Accordion.module.css b/packages/ui/src/components/Accordion/Accordion.module.css new file mode 100644 index 0000000000..a86400986d --- /dev/null +++ b/packages/ui/src/components/Accordion/Accordion.module.css @@ -0,0 +1,93 @@ +/* + * Copyright 2025 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@layer tokens, base, components, utilities; + +@layer components { + .bui-Accordion { + width: 100%; + background-color: var(--bui-bg-surface-1); + border-radius: var(--bui-radius-3); + padding: var(--bui-space-3); + } + + .bui-AccordionTrigger { + all: unset; + width: 100%; + display: flex; + align-items: center; + justify-content: space-between; + } + + .bui-AccordionTriggerButton { + all: unset; + width: 100%; + color: var(--bui-fg-primary); + display: flex; + align-items: center; + justify-content: space-between; + cursor: pointer; + text-align: left; + + &:focus-visible { + outline: none; + transition: none; + box-shadow: inset 0 0 0 2px var(--bui-ring); + } + + &[data-disabled='true'] { + background-color: transparent; + color: var(--bui-fg-disabled); + cursor: not-allowed; + } + } + + .bui-AccordionTriggerTitle { + font-size: var(--bui-font-size-4); + font-weight: var(--bui-font-weight-bold); + line-height: 140%; + } + + .bui-AccordionTriggerSubtitle { + font-size: var(--bui-font-size-2); + line-height: 140%; + color: var(--bui-fg-secondary); + } + + .bui-AccordionTriggerIcon { + transition: transform 150ms ease-out; + flex-shrink: 0; + width: 1rem; + height: 1rem; + + [data-expanded='true'] & { + transform: rotate(180deg); + } + } + + .bui-AccordionPanel { + [data-expanded='true'] & { + padding-top: var(--bui-space-1); + } + } + + .bui-AccordionGroup { + display: flex; + flex-direction: column; + gap: var(--bui-space-3); + width: 100%; + } +} diff --git a/packages/ui/src/components/Accordion/Accordion.stories.tsx b/packages/ui/src/components/Accordion/Accordion.stories.tsx new file mode 100644 index 0000000000..6640bd5d6a --- /dev/null +++ b/packages/ui/src/components/Accordion/Accordion.stories.tsx @@ -0,0 +1,177 @@ +/* + * Copyright 2025 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { Meta, StoryObj } from '@storybook/react-vite'; +import { + Accordion, + AccordionTrigger, + AccordionPanel, + AccordionGroup, +} from './Accordion'; +import { Box } from '../Box'; +import { Text } from '../Text'; + +const Content = () => ( + + + It's the edge of the world and all of Western civilization + + + The sun may rise in the East, at least it settled in a final location + + It's understood that Hollywood sells Californication + +); + +const meta = { + title: 'Backstage UI/Accordion', + component: Accordion, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + render: () => ( + + + + + + + ), +}; + +export const WithSubtitle: Story = { + render: () => ( + + + + + + + ), +}; + +export const CustomTrigger: Story = { + render: () => ( + + + + + Custom Multi-line Trigger + + + Click to expand additional details and configuration options + + + + + + + + ), +}; + +export const DefaultExpanded: Story = { + render: () => ( + + + + + + + ), +}; + +export const GroupSingleOpen: Story = { + render: () => ( + + + + + + + It's the edge of the world and all of Western civilization + + + + + + + + + + The sun may rise in the East, at least it settled in a final + location + + + + + + + + + + It's understood that Hollywood sells Californication + + + + + + ), +}; + +export const GroupMultipleOpen: Story = { + render: () => ( + + + + + + + It's the edge of the world and all of Western civilization + + + + + + + + + + The sun may rise in the East, at least it settled in a final + location + + + + + + + + + + It's understood that Hollywood sells Californication + + + + + + ), +}; diff --git a/packages/ui/src/components/Accordion/Accordion.tsx b/packages/ui/src/components/Accordion/Accordion.tsx new file mode 100644 index 0000000000..3f35ab7554 --- /dev/null +++ b/packages/ui/src/components/Accordion/Accordion.tsx @@ -0,0 +1,153 @@ +/* + * Copyright 2025 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { forwardRef } from 'react'; +import { + Disclosure as RADisclosure, + Button as RAButton, + DisclosurePanel as RADisclosurePanel, + DisclosureGroup as RADisclosureGroup, + Heading as RAHeading, +} from 'react-aria-components'; +import { RiArrowDownSLine } from '@remixicon/react'; +import clsx from 'clsx'; +import type { + AccordionProps, + AccordionTriggerProps, + AccordionPanelProps, + AccordionGroupProps, +} from './types'; +import { useStyles } from '../../hooks/useStyles'; +import styles from './Accordion.module.css'; +import { Flex } from '../Flex'; + +/** @public */ +export const Accordion = forwardRef< + React.ElementRef, + AccordionProps +>(({ className, ...props }, ref) => { + const { classNames, cleanedProps } = useStyles('Accordion', props); + + return ( + + ); +}); + +Accordion.displayName = 'Accordion'; + +/** @public */ +export const AccordionTrigger = forwardRef< + React.ElementRef, + AccordionTriggerProps +>(({ className, title, subtitle, children, ...props }, ref) => { + const { classNames, cleanedProps } = useStyles('Accordion', props); + + return ( + + + {children ? ( + children + ) : ( + + + {title} + + {subtitle && ( + + {subtitle} + + )} + + )} + + + + + ); +}); + +AccordionTrigger.displayName = 'AccordionTrigger'; + +/** @public */ +export const AccordionPanel = forwardRef< + React.ElementRef, + AccordionPanelProps +>(({ className, ...props }, ref) => { + const { classNames, cleanedProps } = useStyles('Accordion', props); + + return ( + + ); +}); + +AccordionPanel.displayName = 'AccordionPanel'; + +/** @public */ +export const AccordionGroup = forwardRef< + React.ElementRef, + AccordionGroupProps +>(({ className, allowsMultiple = false, ...props }, ref) => { + const { classNames, cleanedProps } = useStyles('Accordion', props); + + return ( + + ); +}); + +AccordionGroup.displayName = 'AccordionGroup'; diff --git a/packages/ui/src/components/ButtonIcon/styles.css b/packages/ui/src/components/Accordion/index.ts similarity index 68% rename from packages/ui/src/components/ButtonIcon/styles.css rename to packages/ui/src/components/Accordion/index.ts index cbd19071a9..cd34b4bc87 100644 --- a/packages/ui/src/components/ButtonIcon/styles.css +++ b/packages/ui/src/components/Accordion/index.ts @@ -1,5 +1,5 @@ /* - * Copyright 2024 The Backstage Authors + * Copyright 2025 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,17 +14,15 @@ * limitations under the License. */ -.bui-ButtonIcon { - align-items: center; - justify-content: center; -} - -.bui-ButtonIcon[data-size='small'] { - padding: 0; - width: 2rem; -} - -.bui-ButtonIcon[data-size='medium'] { - padding: 0; - width: 2.5rem; -} +export { + Accordion, + AccordionTrigger, + AccordionPanel, + AccordionGroup, +} from './Accordion'; +export type { + AccordionProps, + AccordionTriggerProps, + AccordionPanelProps, + AccordionGroupProps, +} from './types'; diff --git a/packages/ui/src/components/Accordion/types.ts b/packages/ui/src/components/Accordion/types.ts new file mode 100644 index 0000000000..06c8549ebd --- /dev/null +++ b/packages/ui/src/components/Accordion/types.ts @@ -0,0 +1,62 @@ +/* + * Copyright 2025 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { + DisclosureProps as RADisclosureProps, + HeadingProps as RAHeadingProps, + DisclosurePanelProps as RADisclosurePanelProps, + DisclosureGroupProps as RADisclosureGroupProps, +} from 'react-aria-components'; + +/** + * Props for the Accordion component. + * @public + */ +export interface AccordionProps extends RADisclosureProps { + className?: string; +} + +/** + * Props for the AccordionTrigger component. + * @public + */ +export interface AccordionTriggerProps extends RAHeadingProps { + className?: string; + title?: string; + subtitle?: string; + children?: React.ReactNode; +} + +/** + * Props for the AccordionPanel component. + * @public + */ +export interface AccordionPanelProps extends RADisclosurePanelProps { + className?: string; +} + +/** + * Props for the AccordionGroup component. + * @public + */ +export interface AccordionGroupProps extends RADisclosureGroupProps { + className?: string; + /** + * Whether multiple accordions can be expanded at the same time. + * @defaultValue false + */ + allowsMultiple?: boolean; +} diff --git a/packages/ui/src/components/Avatar/Avatar.module.css b/packages/ui/src/components/Avatar/Avatar.module.css new file mode 100644 index 0000000000..c1fec3df9b --- /dev/null +++ b/packages/ui/src/components/Avatar/Avatar.module.css @@ -0,0 +1,80 @@ +/* + * Copyright 2025 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@layer tokens, base, components, utilities; + +@layer components { + .bui-AvatarRoot { + display: inline-flex; + justify-content: center; + align-items: center; + vertical-align: middle; + border-radius: 100%; + user-select: none; + font-weight: 500; + color: var(--bui-fg-primary); + background-color: var(--bui-bg-surface-2); + font-size: 1rem; + line-height: 1; + overflow: hidden; + height: 2rem; + width: 2rem; + } + + .bui-AvatarRoot[data-size='x-small'] { + height: 1.25rem; + width: 1.25rem; + } + + .bui-AvatarRoot[data-size='small'] { + height: 1.5rem; + width: 1.5rem; + } + + .bui-AvatarRoot[data-size='medium'] { + height: 2rem; + width: 2rem; + } + + .bui-AvatarRoot[data-size='large'] { + height: 2.5rem; + width: 2.5rem; + } + + .bui-AvatarRoot[data-size='x-large'] { + height: 3rem; + width: 3rem; + } + + .bui-AvatarImage { + object-fit: cover; + height: 100%; + width: 100%; + display: block; + } + + .bui-AvatarFallback { + align-items: center; + display: flex; + justify-content: center; + height: 100%; + width: 100%; + font-size: var(--bui-font-size-3); + font-weight: var(--bui-font-weight-regular); + box-shadow: inset 0 0 0 1px var(--bui-border); + border-radius: var(--bui-radius-full); + } +} diff --git a/packages/ui/src/components/Avatar/Avatar.stories.tsx b/packages/ui/src/components/Avatar/Avatar.stories.tsx index c91fe736db..582d82393e 100644 --- a/packages/ui/src/components/Avatar/Avatar.stories.tsx +++ b/packages/ui/src/components/Avatar/Avatar.stories.tsx @@ -16,7 +16,7 @@ import type { Meta, StoryObj } from '@storybook/react-vite'; import { Avatar } from './index'; -import { Flex } from '../..'; +import { Flex, Text } from '../..'; const meta = { title: 'Backstage UI/Avatar', @@ -45,10 +45,52 @@ export const Sizes: Story = { ...Default.args, }, render: args => ( - - - - + + + + + + + + + + + + + + + + + ), +}; + +export const Purpose: Story = { + args: { + ...Default.args, + }, + render: args => ( + + + Informative (default) + + Use when avatar appears alone. Announced as "{args.name}" to screen + readers: + + + + + + + Decoration + + Use when name appears adjacent to avatar. Hidden from screen readers + to avoid redundancy: + + + + {args.name} + + ), }; diff --git a/packages/ui/src/components/Avatar/Avatar.styles.css b/packages/ui/src/components/Avatar/Avatar.styles.css deleted file mode 100644 index ce3afea3b5..0000000000 --- a/packages/ui/src/components/Avatar/Avatar.styles.css +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2025 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -.bui-AvatarRoot { - display: inline-flex; - justify-content: center; - align-items: center; - vertical-align: middle; - border-radius: 100%; - user-select: none; - font-weight: 500; - color: var(--bui-fg-primary); - background-color: var(--bui-bg-surface-2); - font-size: 1rem; - line-height: 1; - overflow: hidden; - height: 2rem; - width: 2rem; -} - -.bui-AvatarRoot[data-size='small'] { - height: 1.5rem; - width: 1.5rem; -} - -.bui-AvatarRoot[data-size='medium'] { - height: 2rem; - width: 2rem; -} - -.bui-AvatarRoot[data-size='large'] { - height: 3rem; - width: 3rem; -} - -.bui-AvatarImage { - object-fit: cover; - height: 100%; - width: 100%; -} - -.bui-AvatarFallback { - align-items: center; - display: flex; - justify-content: center; - height: 100%; - width: 100%; - font-size: var(--bui-font-size-3); - font-weight: var(--bui-font-weight-regular); - box-shadow: inset 0 0 0 1px var(--bui-border); - border-radius: var(--bui-radius-full); -} diff --git a/packages/ui/src/components/Avatar/Avatar.tsx b/packages/ui/src/components/Avatar/Avatar.tsx index 4e899170de..7cbb4f80c7 100644 --- a/packages/ui/src/components/Avatar/Avatar.tsx +++ b/packages/ui/src/components/Avatar/Avatar.tsx @@ -14,40 +14,76 @@ * limitations under the License. */ -import { forwardRef, ElementRef } from 'react'; -import { Avatar as AvatarPrimitive } from '@base-ui-components/react/avatar'; +import { forwardRef, useState, useEffect } from 'react'; import clsx from 'clsx'; import { AvatarProps } from './types'; import { useStyles } from '../../hooks/useStyles'; +import styles from './Avatar.module.css'; /** @public */ -export const Avatar = forwardRef< - ElementRef, - AvatarProps ->((props, ref) => { - const { className, src, name, size = 'medium', ...rest } = props; - const { classNames } = useStyles('Avatar', { - size, +export const Avatar = forwardRef((props, ref) => { + const { classNames, dataAttributes, cleanedProps } = useStyles('Avatar', { + size: 'medium', + purpose: 'informative', + ...props, }); + const { className, src, name, purpose, ...rest } = cleanedProps; + + const [imageStatus, setImageStatus] = useState< + 'loading' | 'loaded' | 'error' + >('loading'); + + useEffect(() => { + setImageStatus('loading'); + const img = new Image(); + img.onload = () => setImageStatus('loaded'); + img.onerror = () => setImageStatus('error'); + img.src = src; + + return () => { + img.onload = null; + img.onerror = null; + }; + }, [src]); + + const initialsCount = ['x-small', 'small'].includes(cleanedProps.size) + ? 1 + : 2; + + const initials = name + .split(' ') + .map(word => word[0]) + .join('') + .toLocaleUpperCase('en-US') + .slice(0, initialsCount); + return ( - - - - {(name || '') - .split(' ') - .map(word => word[0]) - .join('') - .toLocaleUpperCase('en-US') - .slice(0, 2)} - - + {imageStatus === 'loaded' ? ( + + ) : ( + + )} + ); }); -Avatar.displayName = AvatarPrimitive.Root.displayName; +Avatar.displayName = 'Avatar'; diff --git a/packages/ui/src/components/Avatar/types.ts b/packages/ui/src/components/Avatar/types.ts index b2e6cf39cf..a252861ea4 100644 --- a/packages/ui/src/components/Avatar/types.ts +++ b/packages/ui/src/components/Avatar/types.ts @@ -14,12 +14,29 @@ * limitations under the License. */ -import { Avatar } from '@base-ui-components/react/avatar'; - /** @public */ -export interface AvatarProps - extends React.ComponentPropsWithoutRef { +export interface AvatarProps extends React.ComponentPropsWithoutRef<'div'> { + /** + * URL of the image to display + */ src: string; + + /** + * Name of the person - used for generating initials and accessibility labels + */ name: string; - size?: 'small' | 'medium' | 'large'; + + /** + * Size of the avatar + * @defaultValue 'medium' + */ + size?: 'x-small' | 'small' | 'medium' | 'large' | 'x-large'; + + /** + * Determines how the avatar is presented to assistive technologies. + * - 'informative': Avatar is announced as "\{name\}" to screen readers + * - 'decoration': Avatar is hidden from screen readers (use when name appears in adjacent text) + * @defaultValue 'informative' + */ + purpose?: 'decoration' | 'informative'; } diff --git a/packages/ui/src/components/Collapsible/Collapsible.styles.css b/packages/ui/src/components/Box/Box.module.css similarity index 75% rename from packages/ui/src/components/Collapsible/Collapsible.styles.css rename to packages/ui/src/components/Box/Box.module.css index e1fbf00a82..688358fda0 100644 --- a/packages/ui/src/components/Collapsible/Collapsible.styles.css +++ b/packages/ui/src/components/Box/Box.module.css @@ -14,14 +14,12 @@ * limitations under the License. */ -.bui-CollapsiblePanel { - display: flex; - height: var(--collapsible-panel-height); - overflow: hidden; - transition: all 150ms ease-out; +@layer tokens, base, components, utilities; - &[data-starting-style], - &[data-ending-style] { - height: 0; +@layer components { + .bui-Box { + font-family: var(--bui-font-regular); + font-weight: var(--bui-font-weight-regular); + color: var(--bui-fg-primary); } } diff --git a/packages/ui/src/components/Box/Box.stories.tsx b/packages/ui/src/components/Box/Box.stories.tsx index f0086c3d25..6637f45740 100644 --- a/packages/ui/src/components/Box/Box.stories.tsx +++ b/packages/ui/src/components/Box/Box.stories.tsx @@ -36,35 +36,277 @@ const meta = { export default meta; type Story = StoryObj; -const Card = () => { - return ( -
    - ); -}; +const diagonalStripePattern = (() => { + const svg = ` + + + + + + `.trim(); + return `data:image/svg+xml,${encodeURIComponent(svg)}`; +})(); export const Default: Story = { args: { - children: 'Hello World', - mb: '4', + width: '64px', + height: '64px', + style: { + background: '#eaf2fd', + borderRadius: '4px', + border: '1px solid #2563eb', + backgroundImage: `url("${diagonalStripePattern}")`, + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + fontWeight: 'bold', + color: '#2563eb', + }, }, }; -export const Preview: Story = { - args: { - children: , - display: 'inline', - }, +export const Margin: Story = { + args: { ...Default.args }, + render: args => ( + + + + + + + + + + + + + + + + + + + + + ), +}; + +export const ResponsiveMargin: Story = { + args: { ...Default.args }, + render: args => ( + + + + + + + + + + + + + + + + + + + + + ), +}; + +export const CustomMargin: Story = { + args: { ...Default.args }, + render: args => ( + + + + + + + + + + + + + + + + + + + + + ), +}; + +export const CustomResponsiveMargin: Story = { + args: { ...Default.args }, + render: args => ( + + + + + + + + + + + + + + + + + + + + + ), +}; + +export const Padding: Story = { + args: { ...Default.args }, + render: args => ( + + + + + + + + + + + + + + + + + + + + + ), +}; + +export const ResponsivePadding: Story = { + args: { ...Default.args }, + render: args => ( + + + + + + + + + + + + + + + + + + + + + ), +}; + +export const CustomPadding: Story = { + args: { ...Default.args }, + render: args => ( + + + + + + + + + + + + + + + + + + + + + ), +}; + +export const CustomResponsivePadding: Story = { + args: { ...Default.args }, + render: args => ( + + + + + + + + + + + + + + + + + + + + + ), }; const CardDisplay = ({ children }: { children?: ReactNode }) => { @@ -105,151 +347,3 @@ export const Display: Story = { ), }; - -const styleInsideBox = { - background: 'rgb(196, 202, 251)', - color: 'white', - borderRadius: '4px', -}; - -export const Padding: Story = { - args: { - style: { - background: '#1f47ff', - color: 'white', - borderRadius: '4px', - padding: '12px 12px', - }, - }, - render: args => ( - - - - Padding - - - Padding X - - - Padding Y - - - - - Padding Top - - - Padding Right - - - Padding Bottom - - - Padding Left - - - - - Custom Padding - - - Custom Padding X - - - Custom Padding Y - - - - - Custom Padding Top Bottom - - - Custom Padding Left Right - - - - ), -}; - -export const Margin: Story = { - args: { - style: { - background: '#1f47ff', - color: 'white', - borderRadius: '4px', - padding: '12px 12px', - }, - }, - render: args => ( - - - - - Margin - - - - - Margin X - - - - - Margin Y - - - - - - - Margin Top - - - - - Margin Right - - - - - Margin Bottom - - - - - Margin Left - - - - - - - Custom Margin - - - - - Custom Margin X - - - - - Custom Margin Y - - - - - - - Custom Margin Top Bottom - - - - - Custom Margin Left Right - - - - - ), -}; diff --git a/packages/ui/src/components/Box/Box.tsx b/packages/ui/src/components/Box/Box.tsx index 4273ee86f9..fdcee3d382 100644 --- a/packages/ui/src/components/Box/Box.tsx +++ b/packages/ui/src/components/Box/Box.tsx @@ -17,37 +17,33 @@ import { createElement, forwardRef } from 'react'; import { BoxProps } from './types'; import clsx from 'clsx'; -import { extractProps } from '../../utils/extractProps'; -import { spacingPropDefs } from '../../props/spacing.props'; -import { boxPropDefs } from './Box.props'; -import { widthPropDefs } from '../../props/width.props'; -import { heightPropDefs } from '../../props/height.props'; -import { positionPropDefs } from '../../props/position.props'; -import { displayPropDefs } from '../../props/display.props'; import { useStyles } from '../../hooks/useStyles'; +import styles from './Box.module.css'; /** @public */ export const Box = forwardRef((props, ref) => { - const { children } = props; + const { classNames, utilityClasses, style, cleanedProps } = useStyles( + 'Box', + props, + ); - const propDefs = { - ...spacingPropDefs, - ...widthPropDefs, - ...heightPropDefs, - ...positionPropDefs, - ...displayPropDefs, - ...boxPropDefs, - }; + const { as = 'div', children, className, ...rest } = cleanedProps; - const { classNames } = useStyles('Box'); - const { className, style } = extractProps(props, propDefs); - - return createElement(props.as || 'div', { - ref, - className: clsx(classNames.root, className), - style, + return createElement( + as, + { + ref, + className: clsx( + classNames.root, + styles[classNames.root], + utilityClasses, + className, + ), + style, + ...rest, + }, children, - }); + ); }); Box.displayName = 'Box'; diff --git a/packages/ui/src/components/Box/index.tsx b/packages/ui/src/components/Box/index.tsx index 087a940a2d..d47e653dfd 100644 --- a/packages/ui/src/components/Box/index.tsx +++ b/packages/ui/src/components/Box/index.tsx @@ -15,5 +15,3 @@ */ export { Box } from './Box'; export type * from './types'; -export type { BoxOwnProps } from './Box.props'; -export { boxPropDefs } from './Box.props'; diff --git a/packages/ui/src/components/Box/styles.css b/packages/ui/src/components/Box/styles.css deleted file mode 100644 index c499657230..0000000000 --- a/packages/ui/src/components/Box/styles.css +++ /dev/null @@ -1,5 +0,0 @@ -.bui-Box { - font-family: var(--bui-font-regular); - font-weight: var(--bui-font-weight-regular); - color: var(--bui-fg-primary); -} diff --git a/packages/ui/src/components/Box/types.ts b/packages/ui/src/components/Box/types.ts index c70012dd56..c0f4a71035 100644 --- a/packages/ui/src/components/Box/types.ts +++ b/packages/ui/src/components/Box/types.ts @@ -14,24 +14,21 @@ * limitations under the License. */ -import type { HeightProps } from '../../props/height.props'; -import type { WidthProps } from '../../props/width.props'; -import type { PositionProps } from '../../props/position.props'; -import type { DisplayProps } from '../../props/display.props'; -import type { SpaceProps } from '../../types'; -import type { BoxOwnProps } from './Box.props'; +import type { SpaceProps, Responsive } from '../../types'; /** @public */ export interface BoxProps extends SpaceProps { - display?: DisplayProps['display']; - as?: BoxOwnProps['as']; - width?: WidthProps['width']; - minWidth?: WidthProps['minWidth']; - maxWidth?: WidthProps['maxWidth']; - height?: HeightProps['height']; - minHeight?: HeightProps['minHeight']; - maxHeight?: HeightProps['maxHeight']; - position?: PositionProps['position']; + display?: Responsive<'none' | 'flex' | 'block' | 'inline'>; + as?: keyof JSX.IntrinsicElements; + width?: Responsive; + minWidth?: Responsive; + maxWidth?: Responsive; + height?: Responsive; + minHeight?: Responsive; + maxHeight?: Responsive; + position?: Responsive< + 'static' | 'relative' | 'absolute' | 'fixed' | 'sticky' + >; children?: React.ReactNode; className?: string; style?: React.CSSProperties; diff --git a/packages/ui/src/components/Button/Button.module.css b/packages/ui/src/components/Button/Button.module.css new file mode 100644 index 0000000000..a87f5d67cf --- /dev/null +++ b/packages/ui/src/components/Button/Button.module.css @@ -0,0 +1,203 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@layer tokens, base, components, utilities; + +@layer components { + .bui-Button { + --loading-duration: 200ms; + position: relative; + display: inline-flex; + border: none; + user-select: none; + font-family: var(--bui-font-regular); + font-weight: var(--bui-font-weight-bold); + padding: 0; + cursor: pointer; + border-radius: var(--bui-radius-2); + flex-shrink: 0; + transition: background-color var(--loading-duration) ease-out, + box-shadow var(--loading-duration) ease-out; + + &[data-disabled='true'] { + cursor: not-allowed; + } + + &[data-loading='true'] { + cursor: wait; + } + } + + .bui-Button[data-variant='primary'] { + background-color: var(--bui-bg-solid); + color: var(--bui-fg-solid); + + &:hover { + background-color: var(--bui-bg-solid-hover); + transition: background-color 150ms ease; + } + + &:active { + background-color: var(--bui-bg-solid-pressed); + } + + &:focus-visible { + outline: 2px solid var(--bui-ring); + outline-offset: 2px; + } + + &[data-disabled='true'], + &[data-loading='true'] { + background-color: var(--bui-bg-solid-disabled); + color: var(--bui-fg-solid-disabled); + } + } + + .bui-Button[data-variant='secondary'] { + background-color: var(--bui-bg-surface-1); + box-shadow: inset 0 0 0 1px var(--bui-border); + color: var(--bui-fg-primary); + + &:hover { + box-shadow: inset 0 0 0 1px var(--bui-border-hover); + transition: box-shadow 150ms ease; + } + + &:active { + box-shadow: inset 0 0 0 1px var(--bui-border-pressed); + } + + &:focus-visible { + outline: none; + transition: none; + box-shadow: inset 0 0 0 2px var(--bui-ring); + } + + &[data-disabled='true'], + &[data-loading='true'] { + box-shadow: inset 0 0 0 1px var(--bui-border-disabled); + color: var(--bui-fg-disabled); + } + } + + .bui-Button[data-variant='tertiary'] { + background-color: transparent; + color: var(--bui-fg-primary); + + &:hover { + background-color: var(--bui-bg-surface-1); + transition: background-color 200ms ease; + } + + &:active { + background-color: var(--bui-bg-surface-2); + } + + &:focus-visible { + outline: none; + transition: none; + box-shadow: inset 0 0 0 2px var(--bui-ring); + } + + &[data-disabled='true'], + &[data-loading='true'] { + background-color: transparent; + color: var(--bui-fg-disabled); + } + } + + .bui-Button[data-size='small'] { + font-size: var(--bui-font-size-3); + padding: 0 var(--bui-space-2); + height: 2rem; + + svg { + width: 1rem; + height: 1rem; + } + } + + .bui-Button[data-size='medium'] { + font-size: var(--bui-font-size-4); + padding: 0 var(--bui-space-3); + height: 2.5rem; + + svg { + width: 1.25rem; + height: 1.25rem; + } + } + + .bui-ButtonContent { + display: inline-flex; + align-items: center; + justify-content: center; + gap: var(--bui-space-1_5); + height: 100%; + width: 100%; + transition: opacity var(--loading-duration) ease-out; + + .bui-Button[data-loading='true'] & { + opacity: 0; + } + } + + .bui-ButtonSpinner { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + display: flex; + opacity: 0; + transition: opacity var(--loading-duration) ease-in; + + .bui-Button[data-loading='true'] & { + opacity: 1; + } + + & svg { + animation: bui-spin 1s linear infinite; + } + } + + @media (prefers-reduced-motion: reduce) { + .bui-Button { + transition-duration: 50ms; + } + + .bui-ButtonContent { + transition-duration: 50ms; + } + + .bui-ButtonSpinner { + transition-duration: 50ms; + } + + .bui-ButtonSpinner svg { + animation: none; + } + } + + @keyframes bui-spin { + from { + transform: rotate(0deg); + } + + to { + transform: rotate(360deg); + } + } +} diff --git a/packages/ui/src/components/Button/Button.stories.tsx b/packages/ui/src/components/Button/Button.stories.tsx index db8e999f98..4734770aa4 100644 --- a/packages/ui/src/components/Button/Button.stories.tsx +++ b/packages/ui/src/components/Button/Button.stories.tsx @@ -18,7 +18,8 @@ import type { Meta, StoryObj } from '@storybook/react-vite'; import { Button } from './Button'; import { Flex } from '../Flex'; import { Text } from '../Text'; -import { Icon } from '../Icon'; +import { RiArrowRightSLine, RiCloudLine } from '@remixicon/react'; +import { useState } from 'react'; const meta = { title: 'Backstage UI/Button', @@ -57,13 +58,13 @@ export const Variants: Story = { }, render: () => ( - - - @@ -76,10 +77,10 @@ export const Sizes: Story = { }, render: () => ( - - @@ -92,12 +93,12 @@ export const WithIcons: Story = { }, render: args => ( - + - + ); + }, +}; + +export const LoadingVariants: Story = { + render: () => ( + + Primary + + + + + + + Secondary + + + + + + + Tertiary + + + + + + + Loading vs Disabled + + + + + + + ), +}; diff --git a/packages/ui/src/components/Button/Button.tsx b/packages/ui/src/components/Button/Button.tsx index 40879e51ed..45228ff6ef 100644 --- a/packages/ui/src/components/Button/Button.tsx +++ b/packages/ui/src/components/Button/Button.tsx @@ -16,38 +16,53 @@ import clsx from 'clsx'; import { forwardRef, Ref } from 'react'; -import { Button as RAButton } from 'react-aria-components'; +import { Button as RAButton, ProgressBar } from 'react-aria-components'; +import { RiLoader4Line } from '@remixicon/react'; import type { ButtonProps } from './types'; import { useStyles } from '../../hooks/useStyles'; +import styles from './Button.module.css'; /** @public */ export const Button = forwardRef( (props: ButtonProps, ref: Ref) => { - const { - size = 'small', - variant = 'primary', - iconStart, - iconEnd, - children, - className, - ...rest - } = props; - - const { classNames, dataAttributes } = useStyles('Button', { - size, - variant, + const { classNames, dataAttributes, cleanedProps } = useStyles('Button', { + size: 'small', + variant: 'primary', + ...props, }); + const { children, className, iconStart, iconEnd, loading, ...rest } = + cleanedProps; + return ( - {iconStart} - {children} - {iconEnd} + {({ isPending }) => ( + <> + + {iconStart} + {children} + {iconEnd} + + + {isPending && ( + + + )} + + )} ); }, diff --git a/packages/ui/src/components/Button/styles.css b/packages/ui/src/components/Button/styles.css deleted file mode 100644 index 03138a7c44..0000000000 --- a/packages/ui/src/components/Button/styles.css +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright 2024 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -.bui-Button { - border: none; - display: inline-flex; - align-items: center; - justify-content: center; - user-select: none; - font-family: var(--bui-font-regular); - font-weight: var(--bui-font-weight-bold); - padding: 0; - cursor: pointer; - border-radius: var(--bui-radius-2); - gap: var(--bui-space-1_5); - flex-shrink: 0; - - &[data-disabled='true'] { - cursor: not-allowed; - } -} - -.bui-Button[data-variant='primary'] { - background-color: var(--bui-bg-solid); - color: var(--bui-fg-solid); - - &:hover { - background-color: var(--bui-bg-solid-hover); - transition: background-color 150ms ease; - } - - &:active { - background-color: var(--bui-bg-solid-pressed); - } - - &:focus-visible { - outline: 2px solid var(--bui-ring); - outline-offset: 2px; - } - - &[data-disabled='true'] { - background-color: var(--bui-bg-solid-disabled); - color: var(--bui-fg-solid-disabled); - } -} - -.bui-Button[data-variant='secondary'] { - background-color: var(--bui-bg-surface-1); - box-shadow: inset 0 0 0 1px var(--bui-border); - color: var(--bui-fg-primary); - - &:hover { - box-shadow: inset 0 0 0 1px var(--bui-border-hover); - transition: box-shadow 150ms ease; - } - - &:active { - box-shadow: inset 0 0 0 1px var(--bui-border-pressed); - } - - &:focus-visible { - outline: none; - transition: none; - box-shadow: inset 0 0 0 2px var(--bui-ring); - } - - &[data-disabled='true'] { - box-shadow: inset 0 0 0 1px var(--bui-border-disabled); - color: var(--bui-fg-disabled); - } -} - -.bui-Button[data-variant='tertiary'] { - background-color: transparent; - color: var(--bui-fg-primary); - - &:hover { - background-color: var(--bui-bg-surface-1); - transition: background-color 200ms ease; - } - - &:active { - background-color: var(--bui-bg-surface-2); - } - - &:focus-visible { - outline: none; - transition: none; - box-shadow: inset 0 0 0 2px var(--bui-ring); - } - - &[data-disabled='true'] { - background-color: transparent; - color: var(--bui-fg-disabled); - } -} - -.bui-Button[data-size='medium'] { - font-size: var(--bui-font-size-4); - padding: 0 var(--bui-space-3); - height: 2.5rem; -} - -.bui-Button[data-size='small'] { - font-size: var(--bui-font-size-3); - padding: 0 var(--bui-space-2); - height: 2rem; -} - -.bui-Button[data-size='small'] svg { - width: 1rem; - height: 1rem; -} - -.bui-Button[data-size='medium'] svg { - width: 1.25rem; - height: 1.25rem; -} diff --git a/packages/ui/src/components/Button/types.ts b/packages/ui/src/components/Button/types.ts index cbe5bd3608..daeba7653f 100644 --- a/packages/ui/src/components/Button/types.ts +++ b/packages/ui/src/components/Button/types.ts @@ -33,4 +33,5 @@ export interface ButtonProps extends RAButtonProps { iconStart?: ReactElement; iconEnd?: ReactElement; children?: ReactNode; + loading?: boolean; } diff --git a/packages/ui/src/components/ButtonIcon/ButtonIcon.module.css b/packages/ui/src/components/ButtonIcon/ButtonIcon.module.css new file mode 100644 index 0000000000..d26df27ec7 --- /dev/null +++ b/packages/ui/src/components/ButtonIcon/ButtonIcon.module.css @@ -0,0 +1,34 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@layer tokens, base, components, utilities; + +@layer components { + .bui-ButtonIcon { + align-items: center; + justify-content: center; + } + + .bui-ButtonIcon[data-size='small'] { + padding: 0; + width: 2rem; + } + + .bui-ButtonIcon[data-size='medium'] { + padding: 0; + width: 2.5rem; + } +} diff --git a/packages/ui/src/components/ButtonIcon/ButtonIcon.stories.tsx b/packages/ui/src/components/ButtonIcon/ButtonIcon.stories.tsx index 4dcc858084..97072c3664 100644 --- a/packages/ui/src/components/ButtonIcon/ButtonIcon.stories.tsx +++ b/packages/ui/src/components/ButtonIcon/ButtonIcon.stories.tsx @@ -18,7 +18,8 @@ import type { Meta, StoryObj } from '@storybook/react-vite'; import { ButtonIcon } from './ButtonIcon'; import { Flex } from '../Flex'; import { Text } from '../Text'; -import { Icon } from '../Icon'; +import { RiCloudLine } from '@remixicon/react'; +import { useState } from 'react'; const meta = { title: 'Backstage UI/ButtonIcon', @@ -39,15 +40,15 @@ export default meta; type Story = StoryObj; export const Default: Story = { - render: () => } />, + render: () => } />, }; export const Variants: Story = { render: () => ( - } variant="primary" /> - } variant="secondary" /> - } variant="tertiary" /> + } variant="primary" /> + } variant="secondary" /> + } variant="tertiary" /> ), }; @@ -55,8 +56,8 @@ export const Variants: Story = { export const Sizes: Story = { render: () => ( - } size="small" /> - } size="medium" /> + } size="small" /> + } size="medium" /> ), }; @@ -64,9 +65,9 @@ export const Sizes: Story = { export const Disabled: Story = { render: () => ( - } variant="primary" /> - } variant="secondary" /> - } variant="tertiary" /> + } variant="primary" /> + } variant="secondary" /> + } variant="tertiary" /> ), }; @@ -82,44 +83,93 @@ export const Responsive: Story = { sm: 'medium', }, }, - render: args => } />, + render: args => } />, }; -const variants = ['primary', 'secondary'] as const; -const sizes = ['small', 'medium'] as const; +export const Loading: Story = { + render: () => { + const [isLoading, setIsLoading] = useState(false); -export const Playground: Story = { - render: args => ( - - {variants.map(variant => ( - - {variant} - {sizes.map(size => ( - - } - /> - } - aria-label="Chevron right icon button" - variant={variant} - size={size} - /> - } - aria-label="Chevron right icon button" - variant={variant} - size={size} - /> - - ))} - - ))} + const handleClick = () => { + setIsLoading(true); + setTimeout(() => { + setIsLoading(false); + }, 3000); + }; + + return ( + } + loading={isLoading} + onPress={handleClick} + /> + ); + }, +}; + +export const LoadingVariants: Story = { + render: () => ( + + Primary + + } + loading + /> + } + loading + /> + + + Secondary + + } + loading + /> + } + loading + /> + + + Tertiary + + } + loading + /> + } + loading + /> + + + Loading vs Disabled + + } loading /> + } isDisabled /> + } + loading + isDisabled + /> + ), }; diff --git a/packages/ui/src/components/ButtonIcon/ButtonIcon.tsx b/packages/ui/src/components/ButtonIcon/ButtonIcon.tsx index 072ed20963..fecccd6a37 100644 --- a/packages/ui/src/components/ButtonIcon/ButtonIcon.tsx +++ b/packages/ui/src/components/ButtonIcon/ButtonIcon.tsx @@ -16,37 +16,71 @@ import clsx from 'clsx'; import { forwardRef, Ref } from 'react'; -import { Button as RAButton } from 'react-aria-components'; +import { Button as RAButton, ProgressBar } from 'react-aria-components'; +import { RiLoader4Line } from '@remixicon/react'; import type { ButtonIconProps } from './types'; import { useStyles } from '../../hooks/useStyles'; +import stylesButtonIcon from './ButtonIcon.module.css'; +import stylesButton from '../Button/Button.module.css'; /** @public */ export const ButtonIcon = forwardRef( (props: ButtonIconProps, ref: Ref) => { - const { - size = 'small', - variant = 'primary', - icon, - className, - style, - ...rest - } = props; - - const { classNames, dataAttributes } = useStyles('Button', { - size, - variant, + const { classNames, dataAttributes, cleanedProps } = useStyles('Button', { + size: 'small', + variant: 'primary', + ...props, }); const { classNames: classNamesButtonIcon } = useStyles('ButtonIcon'); + const { className, icon, loading, ...rest } = cleanedProps; + return ( - {icon} + {({ isPending }) => ( + <> + + {icon} + + + {isPending && ( + + + )} + + )} ); }, diff --git a/packages/ui/src/components/ButtonIcon/types.ts b/packages/ui/src/components/ButtonIcon/types.ts index bc358b6d7d..6af3d12361 100644 --- a/packages/ui/src/components/ButtonIcon/types.ts +++ b/packages/ui/src/components/ButtonIcon/types.ts @@ -31,4 +31,5 @@ export interface ButtonIconProps extends RAButtonProps { | 'tertiary' | Partial>; icon?: ReactElement; + loading?: boolean; } diff --git a/packages/ui/src/components/ButtonLink/ButtonLink.stories.tsx b/packages/ui/src/components/ButtonLink/ButtonLink.stories.tsx index b4667f017b..7d288065a4 100644 --- a/packages/ui/src/components/ButtonLink/ButtonLink.stories.tsx +++ b/packages/ui/src/components/ButtonLink/ButtonLink.stories.tsx @@ -14,15 +14,22 @@ * limitations under the License. */ -import type { Meta, StoryObj } from '@storybook/react-vite'; +import type { Meta, StoryFn, StoryObj } from '@storybook/react-vite'; import { ButtonLink } from './ButtonLink'; import { Flex } from '../Flex'; -import { Text } from '../Text'; -import { Icon } from '../Icon'; +import { MemoryRouter } from 'react-router-dom'; +import { RiArrowRightSLine, RiCloudLine } from '@remixicon/react'; const meta = { title: 'Backstage UI/ButtonLink', component: ButtonLink, + decorators: [ + (Story: StoryFn) => ( + + + + ), + ], argTypes: { size: { control: 'select', @@ -48,7 +55,7 @@ export const Variants: Story = { render: () => ( } + iconStart={} variant="primary" href="https://ui.backstage.io" target="_blank" @@ -56,7 +63,7 @@ export const Variants: Story = { Button } + iconStart={} variant="secondary" href="https://ui.backstage.io" target="_blank" @@ -64,7 +71,7 @@ export const Variants: Story = { Button } + iconStart={} variant="tertiary" href="https://ui.backstage.io" target="_blank" @@ -81,10 +88,10 @@ export const Sizes: Story = { }, render: () => ( - }> + }> Small - }> + }> Medium @@ -97,12 +104,12 @@ export const WithIcons: Story = { }, render: args => ( - } /> - } /> + } /> + } /> } - iconEnd={} + iconStart={} + iconEnd={} /> ), @@ -114,12 +121,12 @@ export const FullWidth: Story = { }, render: args => ( - } /> - } /> + } /> + } /> } - iconEnd={} + iconStart={} + iconEnd={} /> ), @@ -154,70 +161,3 @@ export const Responsive: Story = { }, }, }; - -const variants = ['primary', 'secondary'] as const; -const sizes = ['small', 'medium'] as const; - -export const Playground: Story = { - args: { - children: 'Button', - }, - render: () => ( - - {variants.map(variant => ( - - {variant} - {sizes.map(size => ( - - - Button - - } - variant={variant} - size={size} - > - Button - - } - variant={variant} - size={size} - > - Button - - } - iconEnd={} - style={{ width: '200px' }} - variant={variant} - size={size} - > - Button - - - Button - - } - variant={variant} - size={size} - isDisabled - > - Button - - } - variant={variant} - size={size} - isDisabled - > - Button - - - ))} - - ))} - - ), -}; diff --git a/packages/ui/src/components/ButtonLink/ButtonLink.tsx b/packages/ui/src/components/ButtonLink/ButtonLink.tsx index 153643a530..28a67105bb 100644 --- a/packages/ui/src/components/ButtonLink/ButtonLink.tsx +++ b/packages/ui/src/components/ButtonLink/ButtonLink.tsx @@ -16,42 +16,65 @@ import clsx from 'clsx'; import { forwardRef, Ref } from 'react'; -import { Link as RALink } from 'react-aria-components'; +import { Link as RALink, RouterProvider } from 'react-aria-components'; +import { useNavigate, useHref } from 'react-router-dom'; import type { ButtonLinkProps } from './types'; import { useStyles } from '../../hooks/useStyles'; +import { isExternalLink } from '../../utils/isExternalLink'; +import stylesButton from '../Button/Button.module.css'; /** @public */ export const ButtonLink = forwardRef( (props: ButtonLinkProps, ref: Ref) => { - const { - size = 'small', - variant = 'primary', - iconStart, - iconEnd, - children, - className, - ...rest - } = props; + const navigate = useNavigate(); - const { classNames, dataAttributes } = useStyles('Button', { - size, - variant, + const { classNames, dataAttributes, cleanedProps } = useStyles('Button', { + size: 'small', + variant: 'primary', + ...props, }); const { classNames: classNamesButtonLink } = useStyles('ButtonLink'); - return ( + const { children, className, iconStart, iconEnd, href, ...rest } = + cleanedProps; + + const isExternal = isExternalLink(href); + + const linkButton = ( - {iconStart} - {children} - {iconEnd} + + {iconStart} + {children} + {iconEnd} + ); + + // If it's an external link, render RALink without RouterProvider + if (isExternal) { + return linkButton; + } + + // For internal links, use RouterProvider + return ( + + {linkButton} + + ); }, ); diff --git a/packages/ui/src/components/Card/Card.module.css b/packages/ui/src/components/Card/Card.module.css new file mode 100644 index 0000000000..85ae86901b --- /dev/null +++ b/packages/ui/src/components/Card/Card.module.css @@ -0,0 +1,48 @@ +/* + * Copyright 2025 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@layer tokens, base, components, utilities; + +@layer components { + .bui-Card { + display: flex; + flex-direction: column; + gap: var(--bui-space-3); + background-color: var(--bui-bg-surface-1); + border-radius: var(--bui-radius-3); + padding-block: var(--bui-space-3); + color: var(--bui-fg-primary); + border: 1px solid var(--bui-border); + overflow: hidden; + min-height: 0; + width: 100%; + } + + .bui-CardBody { + flex: 1; + min-height: 0; + overflow: auto; + padding-inline: var(--bui-space-3); + } + + .bui-CardHeader { + padding-inline: var(--bui-space-3); + } + + .bui-CardFooter { + padding-inline: var(--bui-space-3); + } +} diff --git a/packages/ui/src/components/Card/Card.stories.tsx b/packages/ui/src/components/Card/Card.stories.tsx index 382de8fb8a..170d2b7984 100644 --- a/packages/ui/src/components/Card/Card.stories.tsx +++ b/packages/ui/src/components/Card/Card.stories.tsx @@ -16,13 +16,7 @@ import type { Meta, StoryObj } from '@storybook/react-vite'; import { Card, CardHeader, CardBody, CardFooter } from './Card'; -import { IconNames, Text } from '../..'; - -export interface ListItem { - id: string; - name: string; - icon?: IconNames; -} +import { Text } from '../..'; const meta = { title: 'Backstage UI/Card', diff --git a/packages/ui/src/components/Card/Card.styles.css b/packages/ui/src/components/Card/Card.styles.css deleted file mode 100644 index 1643ba0b8a..0000000000 --- a/packages/ui/src/components/Card/Card.styles.css +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2024 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -.bui-Card { - display: flex; - flex-direction: column; - gap: var(--bui-space-3); - background-color: var(--bui-bg-surface-1); - border-radius: var(--bui-radius-3); - padding-block: var(--bui-space-3); - color: var(--bui-fg-primary); - border: 1px solid var(--bui-border); - overflow: hidden; - min-height: 0; - width: 100%; -} - -.bui-CardBody { - flex: 1; - min-height: 0; - overflow: auto; -} - -.bui-CardHeader { - padding-inline: var(--bui-space-3); -} - -.bui-CardFooter { - padding-inline: var(--bui-space-3); -} diff --git a/packages/ui/src/components/Card/Card.tsx b/packages/ui/src/components/Card/Card.tsx index 7af09d3ec2..b942b70649 100644 --- a/packages/ui/src/components/Card/Card.tsx +++ b/packages/ui/src/components/Card/Card.tsx @@ -15,7 +15,6 @@ */ import { forwardRef } from 'react'; -import { ScrollArea } from '../ScrollArea'; import clsx from 'clsx'; import { useStyles } from '../../hooks/useStyles'; import type { @@ -24,6 +23,7 @@ import type { CardBodyProps, CardFooterProps, } from './types'; +import styles from './Card.module.css'; /** * Card component. @@ -31,11 +31,15 @@ import type { * @public */ export const Card = forwardRef((props, ref) => { - const { className, ...rest } = props; - const { classNames } = useStyles('Card'); + const { classNames, cleanedProps } = useStyles('Card', props); + const { className, ...rest } = cleanedProps; return ( -
    +
    ); }); @@ -46,11 +50,19 @@ export const Card = forwardRef((props, ref) => { */ export const CardHeader = forwardRef( (props, ref) => { - const { className, ...rest } = props; - const { classNames } = useStyles('Card'); + const { classNames, cleanedProps } = useStyles('Card', props); + const { className, ...rest } = cleanedProps; return ( -
    +
    ); }, ); @@ -62,25 +74,15 @@ export const CardHeader = forwardRef( */ export const CardBody = forwardRef( (props, ref) => { - const { children, className, ...rest } = props; - const { classNames } = useStyles('Card'); + const { classNames, cleanedProps } = useStyles('Card', props); + const { className, ...rest } = cleanedProps; return ( - - - {children} - - - - - + /> ); }, ); @@ -92,11 +94,19 @@ export const CardBody = forwardRef( */ export const CardFooter = forwardRef( (props, ref) => { - const { className, ...rest } = props; - const { classNames } = useStyles('Card'); + const { classNames, cleanedProps } = useStyles('Card', props); + const { className, ...rest } = cleanedProps; return ( -
    +
    ); }, ); diff --git a/packages/ui/src/components/Checkbox/Checkbox.module.css b/packages/ui/src/components/Checkbox/Checkbox.module.css new file mode 100644 index 0000000000..bc4f700bce --- /dev/null +++ b/packages/ui/src/components/Checkbox/Checkbox.module.css @@ -0,0 +1,74 @@ +/* + * Copyright 2025 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@layer tokens, base, components, utilities; + +@layer components { + .bui-Checkbox { + display: flex; + flex-direction: row; + align-items: center; + gap: var(--bui-space-2); + font-size: var(--bui-font-size-3); + font-family: var(--bui-font-regular); + font-weight: var(--bui-font-weight-regular); + color: var(--bui-fg-primary); + user-select: none; + cursor: pointer; + } + + .bui-Checkbox[data-disabled] { + cursor: not-allowed; + opacity: 0.5; + } + + .bui-CheckboxIndicator { + border: none; + display: flex; + align-items: center; + justify-content: center; + width: 1rem; + height: 1rem; + box-shadow: inset 0 0 0 1px var(--bui-border); + border-radius: 2px; + transition: background-color 0.2s ease-in-out; + background-color: var(--bui-bg-surface-1); + padding: 0; + flex-shrink: 0; + color: var(--bui-fg-solid); + + @media (prefers-reduced-motion: reduce) { + & { + transition: none; + } + } + } + + .bui-Checkbox[data-focus-visible] .bui-CheckboxIndicator { + transition: none; + outline: 2px solid var(--bui-ring); + outline-offset: 2px; + } + + .bui-Checkbox[data-selected] .bui-CheckboxIndicator { + background-color: var(--bui-bg-solid); + box-shadow: none; + } + + .bui-Checkbox[data-hovered]:not([data-selected]) .bui-CheckboxIndicator { + box-shadow: inset 0 0 0 1px var(--bui-border-hover); + } +} diff --git a/packages/ui/src/components/Checkbox/Checkbox.stories.tsx b/packages/ui/src/components/Checkbox/Checkbox.stories.tsx index 080cae40b6..a41b7f2c8d 100644 --- a/packages/ui/src/components/Checkbox/Checkbox.stories.tsx +++ b/packages/ui/src/components/Checkbox/Checkbox.stories.tsx @@ -17,7 +17,6 @@ import type { Meta, StoryObj } from '@storybook/react-vite'; import { Checkbox } from './Checkbox'; import { Flex } from '../Flex'; -import { Text } from '../Text'; const meta = { title: 'Backstage UI/Checkbox', @@ -29,31 +28,20 @@ type Story = StoryObj; export const Default: Story = { args: { - label: 'Accept terms and conditions', + children: 'Accept terms and conditions', }, }; export const AllVariants: Story = { + ...Default, render: () => ( - - - - - - - ), -}; - -export const Playground: Story = { - render: () => ( - - All variants - - - - - - + + Unchecked + Checked + Disabled + + Checked & Disabled + ), }; diff --git a/packages/ui/src/components/Checkbox/Checkbox.tsx b/packages/ui/src/components/Checkbox/Checkbox.tsx index 51f1fad4de..49019370a4 100644 --- a/packages/ui/src/components/Checkbox/Checkbox.tsx +++ b/packages/ui/src/components/Checkbox/Checkbox.tsx @@ -15,54 +15,32 @@ */ import { forwardRef } from 'react'; -import { Checkbox as CheckboxPrimitive } from '@base-ui-components/react/checkbox'; -import { Icon } from '../..'; +import { Checkbox as RACheckbox } from 'react-aria-components'; import type { CheckboxProps } from './types'; import { useStyles } from '../../hooks/useStyles'; import clsx from 'clsx'; +import styles from './Checkbox.module.css'; +import { RiCheckLine } from '@remixicon/react'; /** @public */ -export const Checkbox = forwardRef( +export const Checkbox = forwardRef( (props, ref) => { - const { - label, - checked, - onChange, - disabled, - required, - className, - name, - value, - style, - } = props; - const { classNames } = useStyles('Checkbox'); + const { className, children, ...rest } = props; - const checkboxElement = ( - - - - - - ); - - return label ? ( - - ) : ( - checkboxElement +
    + +
    + {children} + ); }, ); diff --git a/packages/ui/src/components/Checkbox/styles.css b/packages/ui/src/components/Checkbox/styles.css deleted file mode 100644 index 75d57e72aa..0000000000 --- a/packages/ui/src/components/Checkbox/styles.css +++ /dev/null @@ -1,52 +0,0 @@ -.bui-CheckboxRoot { - border: none; - display: flex; - align-items: center; - justify-content: center; - width: 1rem; - height: 1rem; - box-shadow: inset 0 0 0 1px var(--bui-border); - cursor: pointer; - border-radius: 2px; - transition: background-color 0.2s ease-in-out; - background-color: var(--bui-bg-surface-1); - padding: 0; - flex-shrink: 0; -} - -.bui-CheckboxRoot:focus-visible { - transition: none; - outline: 2px solid var(--bui-ring); - outline-offset: 2px; -} - -.bui-CheckboxRoot[data-checked] { - background-color: var(--bui-bg-solid); - box-shadow: none; - color: var(--bui-fg-solid); -} - -.bui-CheckboxLabel { - display: flex; - flex-direction: row; - align-items: center; - gap: var(--bui-space-2); - font-size: var(--bui-font-size-3); - font-family: var(--bui-font-regular); - font-weight: var(--bui-font-weight-regular); - color: var(--bui-fg-primary); - user-select: none; - - &:hover { - & .bui-CheckboxRoot:not([data-checked]) { - box-shadow: inset 0 0 0 1px var(--bui-border-hover); - } - } -} - -.bui-CheckboxIndicator { - display: flex; - align-items: center; - justify-content: center; - color: var(--bui-fg-solid); -} diff --git a/packages/ui/src/components/Checkbox/types.ts b/packages/ui/src/components/Checkbox/types.ts index 0240954afa..86e4b620b8 100644 --- a/packages/ui/src/components/Checkbox/types.ts +++ b/packages/ui/src/components/Checkbox/types.ts @@ -13,17 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { CheckboxProps as RACheckboxProps } from 'react-aria-components'; /** @public */ -export interface CheckboxProps { - label?: string; - defaultChecked?: boolean; - checked?: boolean; - onChange?: (checked: boolean) => void; - disabled?: boolean; - required?: boolean; - className?: string; - name?: string; - value?: string; - style?: React.CSSProperties; +export interface CheckboxProps extends RACheckboxProps { + children: React.ReactNode; } diff --git a/packages/ui/src/components/Collapsible/Collapsible.stories.tsx b/packages/ui/src/components/Collapsible/Collapsible.stories.tsx deleted file mode 100644 index 7271e6f582..0000000000 --- a/packages/ui/src/components/Collapsible/Collapsible.stories.tsx +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright 2025 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import type { Meta, StoryObj } from '@storybook/react-vite'; -import { Collapsible } from './Collapsible'; -import { Button } from '../Button'; -import { Box } from '../Box'; -import { Text } from '../Text'; -import { Icon } from '../Icon'; - -const meta = { - title: 'Backstage UI/Collapsible', - component: Collapsible.Root, -} satisfies Meta; - -export default meta; -type Story = StoryObj; - -export const Default: Story = { - args: { - style: { - display: 'flex', - flexDirection: 'column', - gap: 'var(--bui-space-2)', - alignItems: 'center', - }, - children: ( - <> - ( - - )} - /> - - - - It's the edge of the world and all of Western civilization - - - The sun may rise in the East, at least it settled in a final - location - - It's understood that Hollywood sells Californication - - - - ), - }, -}; - -export const Open: Story = { - args: { - ...Default.args, - defaultOpen: true, - }, -}; diff --git a/packages/ui/src/components/Collapsible/Collapsible.tsx b/packages/ui/src/components/Collapsible/Collapsible.tsx deleted file mode 100644 index 64d55bdffc..0000000000 --- a/packages/ui/src/components/Collapsible/Collapsible.tsx +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2025 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { forwardRef } from 'react'; -import { Collapsible as CollapsiblePrimitive } from '@base-ui-components/react/collapsible'; -import clsx from 'clsx'; -import { useStyles } from '../../hooks/useStyles'; - -const CollapsibleRoot = forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => { - const { classNames } = useStyles('Collapsible'); - - return ( - - ); -}); -CollapsibleRoot.displayName = CollapsiblePrimitive.Root.displayName; - -const CollapsibleTrigger = forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => { - const { classNames } = useStyles('Collapsible'); - - return ( - - ); -}); -CollapsibleTrigger.displayName = CollapsiblePrimitive.Trigger.displayName; - -const CollapsiblePanel = forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => { - const { classNames } = useStyles('Collapsible'); - - return ( - - ); -}); -CollapsiblePanel.displayName = CollapsiblePrimitive.Panel.displayName; - -/** - * Collapsible is a component that allows you to collapse and expand content. - * It is a wrapper around the CollapsiblePrimitive component from base-ui-components. - * - * @public - */ -export const Collapsible = { - Root: CollapsibleRoot, - Trigger: CollapsibleTrigger, - Panel: CollapsiblePanel, -}; diff --git a/packages/ui/src/components/Container/Container.module.css b/packages/ui/src/components/Container/Container.module.css new file mode 100644 index 0000000000..a0dec1e581 --- /dev/null +++ b/packages/ui/src/components/Container/Container.module.css @@ -0,0 +1,32 @@ +/* + * Copyright 2025 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@layer tokens, base, components, utilities; + +@layer components { + .bui-Container { + max-width: 120rem; + padding-inline: var(--bui-space-4); + margin-inline: auto; + transition: padding 0.2s ease-in-out; + } + + @media (min-width: 640px) { + .bui-Container { + padding-inline: var(--bui-space-5); + } + } +} diff --git a/packages/ui/src/components/Container/Container.tsx b/packages/ui/src/components/Container/Container.tsx index 7be4e5fdce..9b15cd162e 100644 --- a/packages/ui/src/components/Container/Container.tsx +++ b/packages/ui/src/components/Container/Container.tsx @@ -14,42 +14,34 @@ * limitations under the License. */ -import { createElement, forwardRef } from 'react'; +import { forwardRef } from 'react'; import { ContainerProps } from './types'; import clsx from 'clsx'; -import { displayPropDefs } from '../../props/display.props'; -import { extractProps } from '../../utils/extractProps'; -import { spacingPropDefs } from '../../props/spacing.props'; import { useStyles } from '../../hooks/useStyles'; +import styles from './Container.module.css'; /** @public */ export const Container = forwardRef( (props, ref) => { - const { children } = props; + const { classNames, utilityClasses, style, cleanedProps } = useStyles( + 'Container', + props, + ); - const { classNames } = useStyles('Container'); + const { className, ...rest } = cleanedProps; - // Create a subset of spacing props that match the interface - const containerSpacingProps = { - my: spacingPropDefs.my, - mt: spacingPropDefs.mt, - mb: spacingPropDefs.mb, - py: spacingPropDefs.py, - pt: spacingPropDefs.pt, - pb: spacingPropDefs.pb, - }; - - const propDefs = { - ...displayPropDefs, - ...containerSpacingProps, - }; - const { className, style } = extractProps(props, propDefs); - - return createElement('div', { - ref, - className: clsx(classNames.root, className), - style, - children, - }); + return ( +
    + ); }, ); diff --git a/packages/ui/src/components/Container/styles.css b/packages/ui/src/components/Container/styles.css deleted file mode 100644 index a812b7c767..0000000000 --- a/packages/ui/src/components/Container/styles.css +++ /dev/null @@ -1,12 +0,0 @@ -.bui-Container { - max-width: 120rem; - padding-inline: var(--bui-space-4); - margin-inline: auto; - transition: padding 0.2s ease-in-out; -} - -@media (min-width: 640px) { - .bui-Container { - padding-inline: var(--bui-space-5); - } -} diff --git a/packages/ui/src/components/Dialog/Dialog.module.css b/packages/ui/src/components/Dialog/Dialog.module.css new file mode 100644 index 0000000000..5101cbf67c --- /dev/null +++ b/packages/ui/src/components/Dialog/Dialog.module.css @@ -0,0 +1,141 @@ +/* + * Copyright 2025 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@layer tokens, base, components, utilities; + +@layer components { + .bui-DialogOverlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: color-mix(in srgb, var(--bui-gray-2) 80%, transparent); + display: flex; + align-items: center; + justify-content: center; + z-index: 1000; + } + + [data-theme='dark'] .bui-Dialog { + background: rgba(0, 0, 0, 0.5); + } + + .bui-DialogOverlay[data-entering] { + animation: fade-in 200ms ease-out forwards; + } + + .bui-DialogOverlay[data-exiting] { + animation: fade-out 150ms ease-out forwards; + } + + .bui-Dialog { + background: var(--bui-bg-surface-1); + border-radius: 0.5rem; + border: 1px solid var(--bui-border); + color: var(--bui-fg-primary); + position: relative; + width: min(var(--bui-dialog-min-width, 400px), calc(100vw - 3rem)); + max-width: calc(100vw - 3rem); + height: min(var(--bui-dialog-min-height, auto), calc(100vh - 3rem)); + max-height: calc(100vh - 3rem); + display: flex; + flex-direction: column; + outline: none; + } + + /* Dialog entering animation */ + .bui-DialogOverlay[data-entering] .bui-Dialog { + animation: dialog-enter 150ms ease-out forwards; + } + + /* Dialog exiting animation */ + .bui-DialogOverlay[data-exiting] .bui-Dialog { + animation: dialog-exit 150ms ease-out forwards; + } + + .bui-DialogHeader { + display: flex; + justify-content: space-between; + align-items: center; + padding-inline: var(--bui-space-3); + padding-block: var(--bui-space-2); + border-bottom: 1px solid var(--bui-border); + } + + .bui-DialogHeaderTitle { + font-size: var(--bui-font-size-3); + font-weight: var(--bui-font-weight-bold); + margin: 0; + } + + .bui-DialogFooter { + display: flex; + align-items: center; + justify-content: end; + gap: var(--bui-space-2); + padding-inline: var(--bui-space-3); + padding-block: var(--bui-space-3); + border-top: 1px solid var(--bui-border); + } + + .bui-DialogBody { + padding: var(--bui-space-3); + flex: 1; + overflow-y: auto; + } + + /* Keyframe animations */ + @keyframes fade-in { + from { + opacity: 0; + } + to { + opacity: 1; + } + } + + @keyframes fade-out { + from { + opacity: 1; + } + to { + opacity: 0; + } + } + + @keyframes dialog-enter { + from { + opacity: 0.5; + transform: scale(0.8); + } + to { + opacity: 1; + transform: scale(1); + } + } + + @keyframes dialog-exit { + from { + opacity: 1; + transform: scale(1); + } + to { + opacity: 0; + transform: scale(0.95); + } + } +} diff --git a/packages/ui/src/components/Dialog/Dialog.stories.tsx b/packages/ui/src/components/Dialog/Dialog.stories.tsx new file mode 100644 index 0000000000..98d2492913 --- /dev/null +++ b/packages/ui/src/components/Dialog/Dialog.stories.tsx @@ -0,0 +1,254 @@ +/* + * Copyright 2025 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import type { Meta, StoryObj } from '@storybook/react-vite'; +import { + Dialog, + DialogTrigger, + DialogHeader, + DialogBody, + DialogFooter, +} from './Dialog'; +import { Button, Flex, Text, TextField, Select } from '@backstage/ui'; +import { useArgs } from 'storybook/preview-api'; + +const meta = { + title: 'Backstage UI/Dialog', + component: Dialog, + args: { + isOpen: undefined, + defaultOpen: undefined, + }, + argTypes: { + isOpen: { control: 'boolean' }, + defaultOpen: { control: 'boolean' }, + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + render: args => { + return ( + + + + Example Dialog + + This is a basic dialog example. + + + + + + + + ); + }, +}; + +export const Open: Story = { + args: { + ...Default.args, + defaultOpen: true, + }, + render: Default.render, +}; + +export const NoTrigger: Story = { + args: { + isOpen: true, + }, + render: args => { + const [{ isOpen }, updateArgs] = useArgs(); + + return ( + updateArgs({ isOpen: value })} + > + Example Dialog + + This is a basic dialog example. + + + + + + + ); + }, +}; + +export const FixedWidth: Story = { + args: { + defaultOpen: true, + width: 600, + }, + render: args => ( + + + + Long Content Dialog + + + + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do + eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut + enim ad minim veniam, quis nostrud exercitation ullamco laboris + nisi ut aliquip ex ea commodo consequat. + + + Duis aute irure dolor in reprehenderit in voluptate velit esse + cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat + cupidatat non proident, sunt in culpa qui officia deserunt mollit + anim id est laborum. + + + Sed ut perspiciatis unde omnis iste natus error sit voluptatem + accusantium doloremque laudantium, totam rem aperiam, eaque ipsa + quae ab illo inventore veritatis et quasi architecto beatae vitae + dicta sunt explicabo. + + + + + + + + + + ), +}; + +export const FixedHeight: Story = { + args: { + defaultOpen: true, + height: 500, + }, + render: FixedWidth.render, +}; + +export const FixedWidthAndHeight: Story = { + args: { + defaultOpen: true, + width: 600, + height: 400, + }, + render: FixedWidth.render, +}; + +export const FullWidthAndHeight: Story = { + args: { + defaultOpen: true, + width: '100%', + height: '100%', + }, + render: FixedWidth.render, +}; + +export const Confirmation: Story = { + args: { + isOpen: true, + }, + render: args => ( + + + + Confirm Delete + + + Are you sure you want to delete this item? This action cannot be + undone. + + + + + + + + + ), +}; + +export const WithForm: Story = { + args: { + isOpen: true, + }, + render: args => ( + + + + Create New User + + + + + + setIsVisible(v => !v)} + className={clsx( + classNamesPasswordField.inputVisibility, + stylesPasswordField[classNamesPasswordField.inputVisibility], + )} + > + {isVisible ? : } + +
    + + + ); + }, +); + +PasswordField.displayName = 'PasswordField'; diff --git a/packages/ui/src/components/ScrollArea/index.ts b/packages/ui/src/components/PasswordField/index.ts similarity index 91% rename from packages/ui/src/components/ScrollArea/index.ts rename to packages/ui/src/components/PasswordField/index.ts index 20dab2d8ec..df7874e1c2 100644 --- a/packages/ui/src/components/ScrollArea/index.ts +++ b/packages/ui/src/components/PasswordField/index.ts @@ -14,4 +14,5 @@ * limitations under the License. */ -export * from './ScrollArea'; +export * from './PasswordField'; +export * from './types'; diff --git a/packages/ui/src/components/PasswordField/types.ts b/packages/ui/src/components/PasswordField/types.ts new file mode 100644 index 0000000000..6fade7496a --- /dev/null +++ b/packages/ui/src/components/PasswordField/types.ts @@ -0,0 +1,41 @@ +/* + * Copyright 2025 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import type { TextFieldProps as AriaTextFieldProps } from 'react-aria-components'; +import { ReactNode } from 'react'; +import type { Breakpoint } from '../../types'; +import type { FieldLabelProps } from '../FieldLabel/types'; + +/** @public */ +export interface PasswordFieldProps + extends AriaTextFieldProps, + Omit { + /** + * An icon to render before the input + */ + icon?: ReactNode; + + /** + * The size of the password field + * @defaultValue 'medium' + */ + size?: 'small' | 'medium' | Partial>; + + /** + * Text to display in the input when it has no value + */ + placeholder?: string; +} diff --git a/packages/ui/src/components/Popover/Popover.module.css b/packages/ui/src/components/Popover/Popover.module.css new file mode 100644 index 0000000000..910c2b8c76 --- /dev/null +++ b/packages/ui/src/components/Popover/Popover.module.css @@ -0,0 +1,30 @@ +/* + * Copyright 2025 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@layer tokens, base, components, utilities; + +@layer components { + .bui-Popover { + margin-right: 12px; + overflow-x: hidden; + overflow-y: auto; + background-color: var(--bui-bg-surface-1); + border: 1px solid var(--bui-border); + border-radius: var(--bui-radius-3); + padding-block: var(--bui-space-1); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); + } +} diff --git a/packages/ui/src/components/Popover/Popover.styles.css b/packages/ui/src/components/Popover/Popover.styles.css deleted file mode 100644 index 23ae5c1019..0000000000 --- a/packages/ui/src/components/Popover/Popover.styles.css +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2024 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -.bui-Popover { - margin-right: 12px; - overflow: scroll; - background-color: var(--bui-bg-surface-1); - border: 1px solid var(--bui-border); - border-radius: var(--bui-radius-3); - padding-block: var(--bui-space-1); - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); -} diff --git a/packages/ui/src/components/RadioGroup/RadioGroup.module.css b/packages/ui/src/components/RadioGroup/RadioGroup.module.css new file mode 100644 index 0000000000..a18f2d385d --- /dev/null +++ b/packages/ui/src/components/RadioGroup/RadioGroup.module.css @@ -0,0 +1,117 @@ +/* + * Copyright 2025 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@layer tokens, base, components, utilities; + +@layer components { + .bui-RadioGroup { + display: flex; + flex-direction: column; + color: var(--bui-fg-primary); + } + + .bui-RadioGroup[data-orientation='horizontal'] .bui-RadioGroupContent { + flex-direction: row; + gap: var(--bui-space-4); + } + + .bui-RadioGroupContent { + display: flex; + flex-direction: column; + gap: var(--bui-space-2); + } + + .bui-Radio { + display: flex; + /* This is needed so the HiddenInput is positioned correctly */ + position: relative; + align-items: center; + gap: var(--bui-space-2); + font-size: var(--bui-font-size-2); + color: var(--bui-fg-primary); + forced-color-adjust: none; + + &:before { + content: ''; + display: block; + width: 1rem; + height: 1rem; + box-sizing: border-box; + border: 0.125rem solid var(--bui-border); + background: var(--bui-gray-1); + border-radius: var(--bui-radius-full); + transition: all 200ms; + flex-shrink: 0; + flex-grow: 0; + } + + &[data-pressed]:before { + border-color: var(--bui-border); + } + + &[data-selected] { + &:before { + border-color: var(--bui-bg-solid); + border-width: 0.25rem; + } + + &[data-pressed]:before { + border-color: var(--bui-bg-solid); + } + } + + &[data-focus-visible]:before { + outline: 2px solid var(--bui-ring); + outline-offset: 2px; + } + + &[data-disabled] { + cursor: not-allowed; + color: var(--bui-fg-disabled); + + &:before { + border-color: var(--bui-border-disabled); + background: var(--bui-bg-disabled); + } + + &[data-selected]:before { + border-color: var(--bui-border-disabled); + } + } + + &[data-invalid]:before { + border-color: var(--bui-border-danger); + } + + &[data-invalid][data-selected]:before { + border-color: var(--bui-border-danger); + } + + /* Ensure disabled state prevails over invalid state */ + &[data-disabled][data-invalid] { + color: var(--bui-fg-disabled); + + &:before { + border-color: var(--bui-border-disabled); + background: var(--bui-bg-disabled); + } + + &[data-selected]:before { + border-color: var(--bui-border-disabled); + } + } + } +} diff --git a/packages/ui/src/components/RadioGroup/RadioGroup.styles.css b/packages/ui/src/components/RadioGroup/RadioGroup.styles.css deleted file mode 100644 index f12d5571e0..0000000000 --- a/packages/ui/src/components/RadioGroup/RadioGroup.styles.css +++ /dev/null @@ -1,95 +0,0 @@ -.bui-RadioGroup { - display: flex; - flex-direction: column; - color: var(--bui-fg-primary); -} - -.bui-RadioGroup[data-orientation='horizontal'] .bui-RadioGroupContent { - flex-direction: row; - gap: var(--bui-space-4); -} - -.bui-RadioGroupContent { - display: flex; - flex-direction: column; - gap: var(--bui-space-2); -} - -.bui-Radio { - display: flex; - /* This is needed so the HiddenInput is positioned correctly */ - position: relative; - align-items: center; - gap: var(--bui-space-2); - font-size: var(--bui-font-size-2); - color: var(--bui-fg-primary); - forced-color-adjust: none; - - &:before { - content: ''; - display: block; - width: 1rem; - height: 1rem; - box-sizing: border-box; - border: 0.125rem solid var(--bui-border); - background: var(--bui-gray-1); - border-radius: var(--bui-radius-full); - transition: all 200ms; - } - - &[data-pressed]:before { - border-color: var(--bui-border); - } - - &[data-selected] { - &:before { - border-color: var(--bui-bg-solid); - border-width: 0.25rem; - } - - &[data-pressed]:before { - border-color: var(--bui-bg-solid); - } - } - - &[data-focus-visible]:before { - outline: 2px solid var(--bui-ring); - outline-offset: 2px; - } - - &[data-disabled] { - cursor: not-allowed; - color: var(--bui-fg-disabled); - - &:before { - border-color: var(--bui-border-disabled); - background: var(--bui-bg-disabled); - } - - &[data-selected]:before { - border-color: var(--bui-border-disabled); - } - } - - &[data-invalid]:before { - border-color: var(--bui-border-danger); - } - - &[data-invalid][data-selected]:before { - border-color: var(--bui-border-danger); - } - - /* Ensure disabled state prevails over invalid state */ - &[data-disabled][data-invalid] { - color: var(--bui-fg-disabled); - - &:before { - border-color: var(--bui-border-disabled); - background: var(--bui-bg-disabled); - } - - &[data-selected]:before { - border-color: var(--bui-border-disabled); - } - } -} diff --git a/packages/ui/src/components/RadioGroup/RadioGroup.tsx b/packages/ui/src/components/RadioGroup/RadioGroup.tsx index 4363477c1a..355e181f37 100644 --- a/packages/ui/src/components/RadioGroup/RadioGroup.tsx +++ b/packages/ui/src/components/RadioGroup/RadioGroup.tsx @@ -23,12 +23,14 @@ import clsx from 'clsx'; import { FieldLabel } from '../FieldLabel'; import { FieldError } from '../FieldError'; import { useStyles } from '../../hooks/useStyles'; +import styles from './RadioGroup.module.css'; import type { RadioGroupProps, RadioProps } from './types'; /** @public */ export const RadioGroup = forwardRef( (props, ref) => { + const { classNames, cleanedProps } = useStyles('RadioGroup', props); const { className, label, @@ -39,9 +41,7 @@ export const RadioGroup = forwardRef( 'aria-labelledby': ariaLabelledBy, children, ...rest - } = props; - - const { classNames } = useStyles('RadioGroup'); + } = cleanedProps; useEffect(() => { if (!label && !ariaLabel && !ariaLabelledBy) { @@ -57,7 +57,7 @@ export const RadioGroup = forwardRef( return ( ( secondaryLabel={secondaryLabelText} description={description} /> -
    {children}
    +
    + {children} +
    ); @@ -85,7 +87,7 @@ export const Radio = forwardRef((props, ref) => { return ( diff --git a/packages/ui/src/components/RadioGroup/types.ts b/packages/ui/src/components/RadioGroup/types.ts index 029ef4baf9..8e2148401f 100644 --- a/packages/ui/src/components/RadioGroup/types.ts +++ b/packages/ui/src/components/RadioGroup/types.ts @@ -24,7 +24,7 @@ import { ReactNode } from 'react'; /** @public */ export interface RadioGroupProps extends Omit, - Omit { + Omit { children?: ReactNode; } diff --git a/packages/ui/src/components/ScrollArea/ScrollArea.stories.tsx b/packages/ui/src/components/ScrollArea/ScrollArea.stories.tsx deleted file mode 100644 index 48a329eea4..0000000000 --- a/packages/ui/src/components/ScrollArea/ScrollArea.stories.tsx +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2024 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import type { Meta, StoryObj } from '@storybook/react-vite'; -import { ScrollArea } from './ScrollArea'; -import { Text } from '../Text/Text'; - -const meta = { - title: 'Backstage UI/ScrollArea', - component: ScrollArea.Root, -} satisfies Meta; - -export default meta; -type Story = StoryObj; - -export const Default: Story = { - render: () => ( - - -
    - - Vernacular architecture is building done outside any academic - tradition, and without professional guidance. It is not a particular - architectural movement or style, but rather a broad category, - encompassing a wide range and variety of building types, with - differing methods of construction, from around the world, both - historical and extant and classical and modern. Vernacular - architecture constitutes 95% of the world's built environment, as - estimated in 1995 by Amos Rapoport, as measured against the small - percentage of new buildings every year designed by architects and - built by engineers. - - - This type of architecture usually serves immediate, local needs, is - constrained by the materials available in its particular region and - reflects local traditions and cultural practices. The study of - vernacular architecture does not examine formally schooled - architects, but instead that of the design skills and tradition of - local builders, who were rarely given any attribution for the work. - More recently, vernacular architecture has been examined by - designers and the building industry in an effort to be more energy - conscious with contemporary design and construction—part of a - broader interest in sustainable design. - -
    -
    - - - -
    - ), -}; diff --git a/packages/ui/src/components/ScrollArea/ScrollArea.styles.css b/packages/ui/src/components/ScrollArea/ScrollArea.styles.css deleted file mode 100644 index 209168c329..0000000000 --- a/packages/ui/src/components/ScrollArea/ScrollArea.styles.css +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2024 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -.bui-ScrollAreaRoot { - box-sizing: border-box; - width: 100%; -} - -.bui-ScrollAreaViewport { - height: 100%; - overscroll-behavior: contain; -} - -.bui-ScrollAreaContent { - display: flex; - flex-direction: column; - gap: 1rem; - padding-block: 0.75rem; - padding-left: 1rem; - padding-right: 1.5rem; -} - -.bui-ScrollAreaScrollbar { - display: flex; - justify-content: center; - background-color: var(--bui-scrollbar); - width: 0.25rem; - border-radius: 0.375rem; - margin: 0.5rem; - opacity: 0; - transition: opacity 150ms 300ms; - - &[data-hovering], - &[data-scrolling] { - opacity: 1; - transition-duration: 75ms; - transition-delay: 0ms; - } - - &::before { - content: ''; - position: absolute; - width: 1.25rem; - height: 100%; - } -} - -.bui-ScrollAreaThumb { - width: 100%; - border-radius: inherit; - background-color: var(--bui-scrollbar-thumb); -} diff --git a/packages/ui/src/components/ScrollArea/ScrollArea.tsx b/packages/ui/src/components/ScrollArea/ScrollArea.tsx deleted file mode 100644 index 44722fbd12..0000000000 --- a/packages/ui/src/components/ScrollArea/ScrollArea.tsx +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright 2024 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { forwardRef } from 'react'; -import { ScrollArea as ScrollAreaPrimitive } from '@base-ui-components/react/scroll-area'; -import clsx from 'clsx'; -import { useStyles } from '../../hooks/useStyles'; - -const ScrollAreaRoot = forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => { - const { classNames } = useStyles('ScrollArea'); - - return ( - - ); -}); -ScrollAreaRoot.displayName = ScrollAreaPrimitive.Root.displayName; - -const ScrollAreaViewport = forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => { - const { classNames } = useStyles('ScrollArea'); - - return ( - - ); -}); -ScrollAreaViewport.displayName = ScrollAreaPrimitive.Viewport.displayName; - -const ScrollAreaScrollbar = forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => { - const { classNames } = useStyles('ScrollArea'); - - return ( - - ); -}); -ScrollAreaScrollbar.displayName = ScrollAreaPrimitive.Scrollbar.displayName; - -const ScrollAreaThumb = forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => { - const { classNames } = useStyles('ScrollArea'); - - return ( - - ); -}); -ScrollAreaThumb.displayName = ScrollAreaPrimitive.Thumb.displayName; - -/** @public */ -export const ScrollArea = { - Root: ScrollAreaRoot, - Viewport: ScrollAreaViewport, - Scrollbar: ScrollAreaScrollbar, - Thumb: ScrollAreaThumb, -}; diff --git a/packages/ui/src/components/SearchField/SearchField.module.css b/packages/ui/src/components/SearchField/SearchField.module.css new file mode 100644 index 0000000000..e99c212b29 --- /dev/null +++ b/packages/ui/src/components/SearchField/SearchField.module.css @@ -0,0 +1,209 @@ +/* + * Copyright 2025 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@layer tokens, base, components, utilities; + +@layer components { + .bui-SearchField { + display: flex; + flex-direction: column; + font-family: var(--bui-font-regular); + width: 100%; + flex: 1; + flex-shrink: 0; + + &[data-size='small'] { + --search-field-item-height: 2rem; + } + + &[data-size='medium'] { + --search-field-item-height: 2.5rem; + } + + &[data-empty] { + .bui-SearchFieldClear { + display: none; + } + } + + &[data-startCollapsed='true'] { + transition: flex-basis 0.3s ease-in-out; + padding: 0; + flex: 0 1 auto; + + &[data-collapsed='true'] { + flex-basis: 200px; + } + + &[data-collapsed='false'] { + cursor: pointer; + + &[data-size='medium'] { + flex-basis: 2.5rem; + height: 2.5rem; + } + + &[data-size='small'] { + flex-basis: 2rem; + height: 2rem; + } + + &[data-size='medium'] .bui-SearchFieldInput { + &::placeholder { + opacity: 0; + } + } + + &[data-size='small'] .bui-SearchFieldInput { + &::placeholder { + opacity: 0; + } + } + + .bui-SearchFieldInputWrapper { + .bui-SearchFieldInput[data-icon] { + padding-right: 0px; + } + } + } + } + } + + .bui-SearchFieldInputWrapper { + display: flex; + align-items: center; + border-radius: var(--bui-radius-2); + border: 1px solid var(--bui-border); + background-color: var(--bui-bg-surface-1); + transition: border-color 0.2s ease-in-out, outline-color 0.2s ease-in-out; + + &[data-size='small'] { + height: 2rem; + } + + &[data-size='medium'] { + height: 2.5rem; + } + + &:focus-within { + border-color: var(--bui-border-pressed); + outline-width: 0px; + } + + &:hover { + border-color: var(--bui-border-hover); + } + + &[data-invalid] { + border-color: var(--bui-fg-danger); + } + + &[data-disabled] { + opacity: 0.5; + cursor: not-allowed; + border: 1px solid var(--bui-border-disabled); + } + } + + .bui-SearchFieldInputIcon { + flex: 0 0 auto; + display: grid; + place-content: center; + color: var(--bui-fg-primary); + pointer-events: none; + width: var(--search-field-item-height); + height: var(--search-field-item-height); + /* To animate the icon when the input is collapsed */ + transition: opacity 0.2s ease-in-out; + + & svg { + .bui-SearchField[data-size='small'] & { + width: 1rem; + height: 1rem; + } + + .bui-SearchField[data-size='medium'] & { + width: 1.25rem; + height: 1.25rem; + } + } + } + + .bui-SearchFieldInput { + flex: 1; + display: flex; + align-items: center; + padding: 0; + border: none; + background-color: transparent; + font-size: var(--bui-font-size-3); + font-family: var(--bui-font-regular); + font-weight: var(--bui-font-weight-regular); + color: var(--bui-fg-primary); + transition: padding 0.3s ease-in-out; + width: 100%; + height: 100%; + outline: none; + cursor: inherit; + + &::-webkit-search-cancel-button, + &::-webkit-search-decoration { + -webkit-appearance: none; + } + + &::placeholder { + color: var(--bui-fg-secondary); + } + + &[data-disabled] { + cursor: not-allowed; + } + + &:first-child { + .bui-SearchField[data-size='small'] & { + padding-inline: var(--bui-space-3) 0; + } + + .bui-SearchField[data-size='medium'] & { + padding-inline: var(--bui-space-3) 0; + } + } + } + + .bui-SearchFieldClear { + flex: 0 0 auto; + display: grid; + place-content: center; + background-color: transparent; + border: none; + padding: 0; + margin: 0; + cursor: pointer; + color: var(--bui-fg-secondary); + transition: color 0.2s ease-in-out; + width: var(--search-field-item-height); + height: var(--search-field-item-height); + + &:hover { + color: var(--bui-fg-primary); + } + + & svg { + width: 1rem; + height: 1rem; + } + } +} diff --git a/packages/ui/src/components/SearchField/SearchField.stories.tsx b/packages/ui/src/components/SearchField/SearchField.stories.tsx index a811d2a451..81bc090835 100644 --- a/packages/ui/src/components/SearchField/SearchField.stories.tsx +++ b/packages/ui/src/components/SearchField/SearchField.stories.tsx @@ -17,11 +17,10 @@ import type { Meta, StoryObj } from '@storybook/react-vite'; import { SearchField } from './SearchField'; import { Form } from 'react-aria-components'; -import { Icon } from '../Icon'; import { Flex } from '../Flex'; import { FieldLabel } from '../FieldLabel'; import { ButtonIcon } from '../ButtonIcon'; -import { RiCactusLine } from '@remixicon/react'; +import { RiCactusLine, RiEBike2Line } from '@remixicon/react'; import { Button } from '../Button'; import { Header } from '../Header'; import { MemoryRouter } from 'react-router-dom'; @@ -51,6 +50,7 @@ export const Default: Story = { style: { maxWidth: '300px', }, + 'aria-label': 'Search', }, }; @@ -110,7 +110,7 @@ export const WithIcon: Story = { {...args} placeholder="Enter a URL" size="small" - icon={} + icon={} /> ), }; @@ -192,7 +192,7 @@ export const InHeader: Story = { size="small" variant="secondary" /> - + } @@ -272,3 +272,24 @@ export const StartCollapsedWithButtons: Story = {
    ), }; + +export const StartCollapsedWithOnChange: Story = { + args: { + ...StartCollapsed.args, + }, + render: args => { + const handleChange = (value: string) => { + console.log('Search value:', value); + }; + + return ( + + + + ); + }, +}; diff --git a/packages/ui/src/components/SearchField/SearchField.styles.css b/packages/ui/src/components/SearchField/SearchField.styles.css deleted file mode 100644 index a13c455b44..0000000000 --- a/packages/ui/src/components/SearchField/SearchField.styles.css +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright 2024 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -.bui-SearchField { - flex: 1; - flex-shrink: 0; - - &[data-empty] { - .bui-InputClear { - display: none; - } - } - - &[data-start-collapsed='true'] { - transition: flex-basis 0.3s ease-in-out; - padding: 0; - flex: 0 1 auto; - - &[data-collapsed='true'] { - flex-basis: 200px; - } - - &[data-collapsed='false'] { - cursor: pointer; - - &[data-size='medium'] { - flex-basis: 2.5rem; - height: 2.5rem; - } - - &[data-size='small'] { - flex-basis: 2rem; - height: 2rem; - } - - &[data-size='medium'] .bui-Input { - &::placeholder { - opacity: 0; - } - } - - &[data-size='small'] .bui-Input { - &::placeholder { - opacity: 0; - } - } - - .bui-InputWrapper { - .bui-Input[data-icon] { - padding-right: 0px; - } - } - } - } -} - -.bui-SearchField .bui-Input { - transition: padding 0.3s ease-in-out, border-color 0.2s ease-in-out, - outline-color 0.2s ease-in-out; - - &[data-hovered] { - border-color: var(--bui-border-hover); - } - - &[data-focused] { - border-color: var(--bui-border-pressed); - outline-width: 0px; - } -} - -.bui-SearchField .bui-InputWrapper { - .bui-Input[data-icon] { - padding-right: var(--bui-space-6); - } -} - -.bui-SearchField .bui-InputIcon { - left: 0; - display: flex; - justify-content: center; - - &[data-size='small'] { - width: var(--bui-space-8); - } - - &[data-size='medium'] { - width: var(--bui-space-10); - } -} - -.bui-InputClear { - position: absolute; - right: 0; - top: 0; - bottom: 0; - display: flex; - align-items: center; - justify-content: center; - background-color: transparent; - border: none; - padding: 0; - margin: 0; - cursor: pointer; - color: var(--bui-fg-secondary); - transition: color 0.2s ease-in-out; -} - -.bui-InputClear:hover { - color: var(--bui-fg-primary); -} - -.bui-InputClear[data-size='small'] { - width: 2rem; - height: 2rem; -} - -.bui-InputClear[data-size='medium'] { - width: 2.5rem; - height: 2.5rem; -} - -.bui-InputClear svg { - width: 1rem; - height: 1rem; -} diff --git a/packages/ui/src/components/SearchField/SearchField.tsx b/packages/ui/src/components/SearchField/SearchField.tsx index e497cecd49..34fb8c0cee 100644 --- a/packages/ui/src/components/SearchField/SearchField.tsx +++ b/packages/ui/src/components/SearchField/SearchField.tsx @@ -25,6 +25,7 @@ import { FieldLabel } from '../FieldLabel'; import { FieldError } from '../FieldError'; import { RiSearch2Line, RiCloseCircleLine } from '@remixicon/react'; import { useStyles } from '../../hooks/useStyles'; +import styles from './SearchField.module.css'; import type { SearchFieldProps } from './types'; @@ -32,18 +33,9 @@ import type { SearchFieldProps } from './types'; export const SearchField = forwardRef( (props, ref) => { const { - className, - icon, - size = 'small', label, - secondaryLabel, - description, - isRequired, - placeholder = 'Search', - startCollapsed = false, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledBy, - ...rest } = props; const [isCollapsed, setIsCollapsed] = useState(false); @@ -57,14 +49,26 @@ export const SearchField = forwardRef( } }, [label, ariaLabel, ariaLabelledBy]); - const { classNames: textFieldClassNames, dataAttributes } = useStyles( - 'TextField', + const { classNames, dataAttributes, style, cleanedProps } = useStyles( + 'SearchField', { - size, + size: 'small', + placeholder: 'Search', + startCollapsed: false, + ...props, }, ); - const { classNames: searchFieldClassNames } = useStyles('SearchField', {}); + const { + className, + description, + icon, + isRequired, + secondaryLabel, + placeholder, + startCollapsed, + ...rest + } = cleanedProps; // If a secondary label is provided, use it. Otherwise, use 'Required' if the field is required. const secondaryLabelText = @@ -92,18 +96,14 @@ export const SearchField = forwardRef( return ( @@ -113,12 +113,18 @@ export const SearchField = forwardRef( description={description} />
    {icon !== false && ( )} + - - - {options?.map(option => ( - -
    - -
    - - {option.label} - -
    - ))} -
    + + ); diff --git a/packages/ui/src/components/Select/SelectContent.tsx b/packages/ui/src/components/Select/SelectContent.tsx new file mode 100644 index 0000000000..42070321a3 --- /dev/null +++ b/packages/ui/src/components/Select/SelectContent.tsx @@ -0,0 +1,74 @@ +/* + * Copyright 2025 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + Input, + SearchField, + Autocomplete, + Button, +} from 'react-aria-components'; +import { useFilter } from 'react-aria'; +import { RiCloseCircleLine } from '@remixicon/react'; +import clsx from 'clsx'; +import { useStyles } from '../../hooks/useStyles'; +import { SelectListBox } from './SelectListBox'; +import styles from './Select.module.css'; +import type { Option } from './types'; + +interface SelectContentProps { + searchable?: boolean; + searchPlaceholder?: string; + options?: Array

  • + +
    +

    Backstage UI (New)

    +
      +
    • Theming: CSS variables and tokens
    • +
    • Coverage: Growing, focused on new work
    • +
    • Documentation: ui.backstage.io
    • +
    +
    +