+
+
+
+
+
+
+
+
+
+
+
+
+ <%= config.getOptionalString('app.title') ?? 'Backstage' %>
+
+
+
+
+
+
+
diff --git a/workspaces/theme/packages/app-legacy/public/manifest.json b/workspaces/theme/packages/app-legacy/public/manifest.json
new file mode 100644
index 0000000000..4a7c1b4ec4
--- /dev/null
+++ b/workspaces/theme/packages/app-legacy/public/manifest.json
@@ -0,0 +1,15 @@
+{
+ "short_name": "Backstage",
+ "name": "Backstage",
+ "icons": [
+ {
+ "src": "favicon.ico",
+ "sizes": "48x48",
+ "type": "image/png"
+ }
+ ],
+ "start_url": "./index.html",
+ "display": "standalone",
+ "theme_color": "#000000",
+ "background_color": "#ffffff"
+}
diff --git a/workspaces/theme/packages/app/public/robots.txt b/workspaces/theme/packages/app-legacy/public/robots.txt
similarity index 100%
rename from workspaces/theme/packages/app/public/robots.txt
rename to workspaces/theme/packages/app-legacy/public/robots.txt
diff --git a/workspaces/theme/packages/app/public/safari-pinned-tab.svg b/workspaces/theme/packages/app-legacy/public/safari-pinned-tab.svg
similarity index 100%
rename from workspaces/theme/packages/app/public/safari-pinned-tab.svg
rename to workspaces/theme/packages/app-legacy/public/safari-pinned-tab.svg
diff --git a/workspaces/theme/packages/app/src/App.test.tsx b/workspaces/theme/packages/app-legacy/src/App.test.tsx
similarity index 100%
rename from workspaces/theme/packages/app/src/App.test.tsx
rename to workspaces/theme/packages/app-legacy/src/App.test.tsx
diff --git a/workspaces/theme/packages/app-legacy/src/App.tsx b/workspaces/theme/packages/app-legacy/src/App.tsx
new file mode 100644
index 0000000000..7ac722bb68
--- /dev/null
+++ b/workspaces/theme/packages/app-legacy/src/App.tsx
@@ -0,0 +1,135 @@
+/*
+ * Copyright Red Hat, Inc.
+ *
+ * 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 { Navigate, Route } from 'react-router-dom';
+import { apiDocsPlugin, ApiExplorerPage } from '@backstage/plugin-api-docs';
+import {
+ CatalogEntityPage,
+ CatalogIndexPage,
+ catalogPlugin,
+} from '@backstage/plugin-catalog';
+import {
+ CatalogImportPage,
+ catalogImportPlugin,
+} from '@backstage/plugin-catalog-import';
+import { ScaffolderPage, scaffolderPlugin } from '@backstage/plugin-scaffolder';
+import { orgPlugin } from '@backstage/plugin-org';
+import { SearchPage } from '@backstage/plugin-search';
+import {
+ TechDocsIndexPage,
+ techdocsPlugin,
+ TechDocsReaderPage,
+} from '@backstage/plugin-techdocs';
+import { TechDocsAddons } from '@backstage/plugin-techdocs-react';
+import { ReportIssue } from '@backstage/plugin-techdocs-module-addons-contrib';
+import { UserSettingsPage } from '@backstage/plugin-user-settings';
+import { apis } from './apis';
+import { entityPage } from './components/catalog/EntityPage';
+import { searchPage } from './components/search/SearchPage';
+import { Root } from './components/Root';
+
+import {
+ AlertDisplay,
+ OAuthRequestDialog,
+ SignInPage,
+} from '@backstage/core-components';
+import { createApp } from '@backstage/app-defaults';
+import { AppRouter, FlatRoutes } from '@backstage/core-app-api';
+import { CatalogGraphPage } from '@backstage/plugin-catalog-graph';
+import { RequirePermission } from '@backstage/plugin-permission-react';
+import { catalogEntityCreatePermission } from '@backstage/plugin-catalog-common/alpha';
+
+import { BCCTestPage } from '@red-hat-developer-hub/backstage-plugin-bcc-test';
+import { BUITestPage } from '@red-hat-developer-hub/backstage-plugin-bui-test';
+import { MUI4TestPage } from '@red-hat-developer-hub/backstage-plugin-mui4-test';
+import { MUI5TestPage } from '@red-hat-developer-hub/backstage-plugin-mui5-test';
+import { getAllThemes } from '@red-hat-developer-hub/backstage-plugin-theme';
+
+const app = createApp({
+ apis,
+ bindRoutes({ bind }) {
+ bind(catalogPlugin.externalRoutes, {
+ createComponent: scaffolderPlugin.routes.root,
+ viewTechDoc: techdocsPlugin.routes.docRoot,
+ createFromTemplate: scaffolderPlugin.routes.selectedTemplate,
+ });
+ bind(apiDocsPlugin.externalRoutes, {
+ registerApi: catalogImportPlugin.routes.importPage,
+ });
+ bind(scaffolderPlugin.externalRoutes, {
+ registerComponent: catalogImportPlugin.routes.importPage,
+ viewTechDoc: techdocsPlugin.routes.docRoot,
+ });
+ bind(orgPlugin.externalRoutes, {
+ catalogIndex: catalogPlugin.routes.catalogIndex,
+ });
+ },
+ components: {
+ SignInPage: props => ,
+ },
+ themes: getAllThemes(),
+});
+
+const routes = (
+
+ } />
+ } />
+ }
+ >
+ {entityPage}
+
+ } />
+ }
+ >
+
+
+
+
+ } />
+ } />
+
+
+
+ }
+ />
+ }>
+ {searchPage}
+
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+
+);
+
+export default app.createRoot(
+ <>
+
+
+
+ {routes}
+
+ >,
+);
diff --git a/workspaces/theme/packages/app-legacy/src/apis.ts b/workspaces/theme/packages/app-legacy/src/apis.ts
new file mode 100644
index 0000000000..8bbbfc05aa
--- /dev/null
+++ b/workspaces/theme/packages/app-legacy/src/apis.ts
@@ -0,0 +1,35 @@
+/*
+ * Copyright Red Hat, Inc.
+ *
+ * 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 {
+ ScmIntegrationsApi,
+ scmIntegrationsApiRef,
+ ScmAuth,
+} from '@backstage/integration-react';
+import {
+ AnyApiFactory,
+ configApiRef,
+ createApiFactory,
+} from '@backstage/core-plugin-api';
+
+export const apis: AnyApiFactory[] = [
+ createApiFactory({
+ api: scmIntegrationsApiRef,
+ deps: { configApi: configApiRef },
+ factory: ({ configApi }) => ScmIntegrationsApi.fromConfig(configApi),
+ }),
+ ScmAuth.createDefaultApiFactory(),
+];
diff --git a/workspaces/theme/packages/app-legacy/src/components/Root/LogoFull.tsx b/workspaces/theme/packages/app-legacy/src/components/Root/LogoFull.tsx
new file mode 100644
index 0000000000..c7536520e7
--- /dev/null
+++ b/workspaces/theme/packages/app-legacy/src/components/Root/LogoFull.tsx
@@ -0,0 +1,45 @@
+/*
+ * Copyright Red Hat, Inc.
+ *
+ * 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 { makeStyles } from '@material-ui/core';
+
+const useStyles = makeStyles({
+ svg: {
+ width: 'auto',
+ height: 30,
+ },
+ path: {
+ fill: '#7df3e1',
+ },
+});
+const LogoFull = () => {
+ const classes = useStyles();
+
+ return (
+
+ );
+};
+
+export default LogoFull;
diff --git a/workspaces/theme/packages/app-legacy/src/components/Root/LogoIcon.tsx b/workspaces/theme/packages/app-legacy/src/components/Root/LogoIcon.tsx
new file mode 100644
index 0000000000..5d0ff212c8
--- /dev/null
+++ b/workspaces/theme/packages/app-legacy/src/components/Root/LogoIcon.tsx
@@ -0,0 +1,46 @@
+/*
+ * Copyright Red Hat, Inc.
+ *
+ * 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 { makeStyles } from '@material-ui/core';
+
+const useStyles = makeStyles({
+ svg: {
+ width: 'auto',
+ height: 28,
+ },
+ path: {
+ fill: '#7df3e1',
+ },
+});
+
+const LogoIcon = () => {
+ const classes = useStyles();
+
+ return (
+
+ );
+};
+
+export default LogoIcon;
diff --git a/workspaces/theme/packages/app-legacy/src/components/Root/Root.tsx b/workspaces/theme/packages/app-legacy/src/components/Root/Root.tsx
new file mode 100644
index 0000000000..18198b3a66
--- /dev/null
+++ b/workspaces/theme/packages/app-legacy/src/components/Root/Root.tsx
@@ -0,0 +1,117 @@
+/*
+ * Copyright Red Hat, Inc.
+ *
+ * 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 { PropsWithChildren } from 'react';
+import { makeStyles } from '@material-ui/core';
+import HomeIcon from '@material-ui/icons/Home';
+import ExtensionIcon from '@material-ui/icons/Extension';
+import LibraryBooks from '@material-ui/icons/LibraryBooks';
+import CreateComponentIcon from '@material-ui/icons/AddCircleOutline';
+import LogoFull from './LogoFull';
+import LogoIcon from './LogoIcon';
+import {
+ Settings as SidebarSettings,
+ UserSettingsSignInAvatar,
+} from '@backstage/plugin-user-settings';
+import { SidebarSearchModal } from '@backstage/plugin-search';
+import {
+ Sidebar,
+ sidebarConfig,
+ SidebarDivider,
+ SidebarGroup,
+ SidebarItem,
+ SidebarPage,
+ SidebarScrollWrapper,
+ SidebarSpace,
+ useSidebarOpenState,
+ Link,
+} from '@backstage/core-components';
+import MenuIcon from '@material-ui/icons/Menu';
+import SearchIcon from '@material-ui/icons/Search';
+import { MyGroupsSidebarItem } from '@backstage/plugin-org';
+import GroupIcon from '@material-ui/icons/People';
+
+const useSidebarLogoStyles = makeStyles({
+ root: {
+ width: sidebarConfig.drawerWidthClosed,
+ height: 3 * sidebarConfig.logoHeight,
+ display: 'flex',
+ flexFlow: 'row nowrap',
+ alignItems: 'center',
+ marginBottom: -14,
+ },
+ link: {
+ width: sidebarConfig.drawerWidthClosed,
+ marginLeft: 24,
+ },
+});
+
+const SidebarLogo = () => {
+ const classes = useSidebarLogoStyles();
+ const { isOpen } = useSidebarOpenState();
+
+ return (
+
+
+ {isOpen ? : }
+
+
+ );
+};
+
+export const Root = ({ children }: PropsWithChildren<{}>) => (
+
+
+
+ } to="/search">
+
+
+
+ }>
+ {/* Global nav, not org-specific */}
+
+
+
+
+
+ {/* End global nav */}
+
+
+
+
+
+
+
+ {/* Items in this group will be scrollable if they run out of space */}
+
+
+
+
+ }
+ to="/settings"
+ >
+
+
+
+ {children}
+
+);
diff --git a/workspaces/theme/packages/app-legacy/src/components/Root/index.ts b/workspaces/theme/packages/app-legacy/src/components/Root/index.ts
new file mode 100644
index 0000000000..98b6dde118
--- /dev/null
+++ b/workspaces/theme/packages/app-legacy/src/components/Root/index.ts
@@ -0,0 +1,17 @@
+/*
+ * Copyright Red Hat, Inc.
+ *
+ * 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 { Root } from './Root';
diff --git a/workspaces/theme/packages/app-legacy/src/components/catalog/EntityPage.tsx b/workspaces/theme/packages/app-legacy/src/components/catalog/EntityPage.tsx
new file mode 100644
index 0000000000..f82362c31c
--- /dev/null
+++ b/workspaces/theme/packages/app-legacy/src/components/catalog/EntityPage.tsx
@@ -0,0 +1,426 @@
+/*
+ * Copyright Red Hat, Inc.
+ *
+ * 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 { Button, Grid } from '@material-ui/core';
+import {
+ EntityApiDefinitionCard,
+ EntityConsumedApisCard,
+ EntityConsumingComponentsCard,
+ EntityHasApisCard,
+ EntityProvidedApisCard,
+ EntityProvidingComponentsCard,
+} from '@backstage/plugin-api-docs';
+import {
+ EntityAboutCard,
+ EntityDependsOnComponentsCard,
+ EntityDependsOnResourcesCard,
+ EntityHasComponentsCard,
+ EntityHasResourcesCard,
+ EntityHasSubcomponentsCard,
+ EntityHasSystemsCard,
+ EntityLayout,
+ EntityLinksCard,
+ EntitySwitch,
+ EntityOrphanWarning,
+ EntityProcessingErrorsPanel,
+ isComponentType,
+ isKind,
+ hasCatalogProcessingErrors,
+ isOrphan,
+ hasRelationWarnings,
+ EntityRelationWarning,
+} from '@backstage/plugin-catalog';
+import {
+ EntityUserProfileCard,
+ EntityGroupProfileCard,
+ EntityMembersListCard,
+ EntityOwnershipCard,
+} from '@backstage/plugin-org';
+import { EntityTechdocsContent } from '@backstage/plugin-techdocs';
+import { EmptyState } from '@backstage/core-components';
+import {
+ Direction,
+ EntityCatalogGraphCard,
+} from '@backstage/plugin-catalog-graph';
+import {
+ RELATION_API_CONSUMED_BY,
+ RELATION_API_PROVIDED_BY,
+ RELATION_CONSUMES_API,
+ RELATION_DEPENDENCY_OF,
+ RELATION_DEPENDS_ON,
+ RELATION_HAS_PART,
+ RELATION_PART_OF,
+ RELATION_PROVIDES_API,
+} from '@backstage/catalog-model';
+
+import { TechDocsAddons } from '@backstage/plugin-techdocs-react';
+import { ReportIssue } from '@backstage/plugin-techdocs-module-addons-contrib';
+
+import {
+ EntityKubernetesContent,
+ isKubernetesAvailable,
+} from '@backstage/plugin-kubernetes';
+
+const techdocsContent = (
+
+
+
+
+
+);
+
+const cicdContent = (
+ // This is an example of how you can implement your company's logic in entity page.
+ // You can for example enforce that all components of type 'service' should use GitHubActions
+
+ {/*
+ Here you can add support for different CI/CD services, for example
+ using @backstage-community/plugin-github-actions as follows:
+
+
+
+ */}
+
+
+ Read more
+
+ }
+ />
+
+
+);
+
+const entityWarningContent = (
+ <>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ >
+);
+
+const overviewContent = (
+
+ {entityWarningContent}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+);
+
+const serviceEntityPage = (
+
+
+ {overviewContent}
+
+
+
+ {cicdContent}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {techdocsContent}
+
+
+);
+
+const websiteEntityPage = (
+
+
+ {overviewContent}
+
+
+
+ {cicdContent}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {techdocsContent}
+
+
+);
+
+/**
+ * NOTE: This page is designed to work on small screens such as mobile devices.
+ * This is based on Material UI Grid. If breakpoints are used, each grid item must set the `xs` prop to a column size or to `true`,
+ * since this does not default. If no breakpoints are used, the items will equitably share the available space.
+ * https://material-ui.com/components/grid/#basic-grid.
+ */
+
+const defaultEntityPage = (
+
+
+ {overviewContent}
+
+
+
+ {techdocsContent}
+
+
+);
+
+const componentPage = (
+
+
+ {serviceEntityPage}
+
+
+
+ {websiteEntityPage}
+
+
+ {defaultEntityPage}
+
+);
+
+const apiPage = (
+
+
+
+ {entityWarningContent}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+);
+
+const userPage = (
+
+
+
+ {entityWarningContent}
+
+
+
+
+
+
+
+
+
+);
+
+const groupPage = (
+
+
+
+ {entityWarningContent}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+);
+
+const systemPage = (
+
+
+
+ {entityWarningContent}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+);
+
+const domainPage = (
+
+
+
+ {entityWarningContent}
+
+
+
+
+
+
+
+
+
+
+
+
+);
+
+export const entityPage = (
+
+
+
+
+
+
+
+
+ {defaultEntityPage}
+
+);
diff --git a/workspaces/theme/packages/app-legacy/src/components/search/SearchPage.tsx b/workspaces/theme/packages/app-legacy/src/components/search/SearchPage.tsx
new file mode 100644
index 0000000000..008fc1619a
--- /dev/null
+++ b/workspaces/theme/packages/app-legacy/src/components/search/SearchPage.tsx
@@ -0,0 +1,139 @@
+/*
+ * Copyright Red Hat, Inc.
+ *
+ * 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 { makeStyles, Theme, Grid, Paper } from '@material-ui/core';
+
+import { CatalogSearchResultListItem } from '@backstage/plugin-catalog';
+import {
+ catalogApiRef,
+ CATALOG_FILTER_EXISTS,
+} from '@backstage/plugin-catalog-react';
+import { TechDocsSearchResultListItem } from '@backstage/plugin-techdocs';
+
+import { SearchType } from '@backstage/plugin-search';
+import {
+ SearchBar,
+ SearchFilter,
+ SearchResult,
+ SearchPagination,
+ useSearch,
+} from '@backstage/plugin-search-react';
+import {
+ CatalogIcon,
+ Content,
+ DocsIcon,
+ Header,
+ Page,
+} from '@backstage/core-components';
+import { useApi } from '@backstage/core-plugin-api';
+
+const useStyles = makeStyles((theme: Theme) => ({
+ bar: {
+ padding: theme.spacing(1, 0),
+ },
+ filters: {
+ padding: theme.spacing(2),
+ marginTop: theme.spacing(2),
+ },
+ filter: {
+ '& + &': {
+ marginTop: theme.spacing(2.5),
+ },
+ },
+}));
+
+const SearchPage = () => {
+ const classes = useStyles();
+ const { types } = useSearch();
+ const catalogApi = useApi(catalogApiRef);
+
+ return (
+
+
+
+
+
+
+
+
+
+
+ ,
+ },
+ {
+ value: 'techdocs',
+ name: 'Documentation',
+ icon: ,
+ },
+ ]}
+ />
+
+ {types.includes('techdocs') && (
+ {
+ // Return a list of entities which are documented.
+ const { items } = await catalogApi.getEntities({
+ fields: ['metadata.name'],
+ filter: {
+ 'metadata.annotations.backstage.io/techdocs-ref':
+ CATALOG_FILTER_EXISTS,
+ },
+ });
+
+ const names = items.map(entity => entity.metadata.name);
+ names.sort();
+ return names;
+ }}
+ />
+ )}
+
+
+
+
+
+
+
+ } />
+ } />
+
+
+
+
+
+ );
+};
+
+export const searchPage = ;
diff --git a/workspaces/theme/packages/app-legacy/src/index.tsx b/workspaces/theme/packages/app-legacy/src/index.tsx
new file mode 100644
index 0000000000..631b76a44d
--- /dev/null
+++ b/workspaces/theme/packages/app-legacy/src/index.tsx
@@ -0,0 +1,22 @@
+/*
+ * Copyright Red Hat, Inc.
+ *
+ * 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 '@backstage/cli/asset-types';
+import ReactDOM from 'react-dom/client';
+import App from './App';
+import '@backstage/ui/css/styles.css';
+
+ReactDOM.createRoot(document.getElementById('root')!).render();
diff --git a/workspaces/theme/packages/app/src/setupTests.ts b/workspaces/theme/packages/app-legacy/src/setupTests.ts
similarity index 100%
rename from workspaces/theme/packages/app/src/setupTests.ts
rename to workspaces/theme/packages/app-legacy/src/setupTests.ts
diff --git a/workspaces/theme/packages/app/.eslintignore b/workspaces/theme/packages/app/.eslintignore
deleted file mode 100644
index a48cf0de7a..0000000000
--- a/workspaces/theme/packages/app/.eslintignore
+++ /dev/null
@@ -1 +0,0 @@
-public
diff --git a/workspaces/theme/packages/app/README.md b/workspaces/theme/packages/app/README.md
new file mode 100644
index 0000000000..09ccf4664e
--- /dev/null
+++ b/workspaces/theme/packages/app/README.md
@@ -0,0 +1,35 @@
+# app (NFS – main app)
+
+This package is the **main frontend app** for the theme workspace, built on the **New Frontend System (NFS)**. It uses:
+
+- `createApp` from `@backstage/frontend-defaults`
+- `convertLegacyApp` from `@backstage/core-compat-api` for hybrid migration
+- RHDH themes registered via **ThemeBlueprint** in a `createFrontendModule({ pluginId: 'app', extensions: [...] })`
+
+Existing theme behavior is unchanged: the same `getAllThemes()` from `@red-hat-developer-hub/backstage-plugin-theme` is used; the app registers them as NFS extensions instead of passing `themes` to legacy `createApp`.
+
+## Run
+
+From the theme workspace root:
+
+```bash
+# Start backend (in one terminal)
+yarn start-backend
+
+# Start app (NFS – default)
+yarn start
+```
+
+To run the legacy app instead:
+
+```bash
+yarn start:legacy
+```
+
+Or from this package:
+
+```bash
+yarn start
+```
+
+See [THEME_NFS_BRIDGE_DESIGN.md](../../docs/THEME_NFS_BRIDGE_DESIGN.md) for the design.
diff --git a/workspaces/theme/packages/app/package.json b/workspaces/theme/packages/app/package.json
index 05a83b68c8..9bf9aad282 100644
--- a/workspaces/theme/packages/app/package.json
+++ b/workspaces/theme/packages/app/package.json
@@ -19,14 +19,17 @@
"lint": "backstage-cli package lint"
},
"dependencies": {
- "@backstage/app-defaults": "^1.7.2",
"@backstage/catalog-model": "^1.7.6",
"@backstage/cli": "^0.34.5",
"@backstage/core-app-api": "^1.19.2",
+ "@backstage/core-compat-api": "^0.2.0",
"@backstage/core-components": "^0.18.3",
"@backstage/core-plugin-api": "^1.12.0",
+ "@backstage/frontend-defaults": "^0.3.0",
+ "@backstage/frontend-plugin-api": "^0.13.0",
"@backstage/integration-react": "^1.2.12",
"@backstage/plugin-api-docs": "^0.13.1",
+ "@backstage/plugin-app-react": "^0.1.0",
"@backstage/plugin-catalog": "^1.32.0",
"@backstage/plugin-catalog-common": "^1.1.7",
"@backstage/plugin-catalog-graph": "^0.5.3",
@@ -57,9 +60,7 @@
"react-router-dom": "^6.3.0"
},
"devDependencies": {
- "@axe-core/playwright": "^4.11.0",
"@backstage/test-utils": "^1.7.13",
- "@playwright/test": "1.58.2",
"@testing-library/dom": "^9.0.0",
"@testing-library/jest-dom": "^6.0.0",
"@testing-library/react": "^14.0.0",
diff --git a/workspaces/theme/packages/app/public/index.html b/workspaces/theme/packages/app/public/index.html
index 00859982f2..3d2491c2e0 100644
--- a/workspaces/theme/packages/app/public/index.html
+++ b/workspaces/theme/packages/app/public/index.html
@@ -8,10 +8,6 @@
name="description"
content="Backstage is an open source framework for building developer portals"
/>
-
-