@backstage/plugin-app-visualizer 0.1.28-next.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +30 -0
- package/dist/components/AppVisualizerPage/DetailedVisualizer.esm.js +11 -9
- package/dist/components/AppVisualizerPage/DetailedVisualizer.esm.js.map +1 -1
- package/dist/components/AppVisualizerPage/TextVisualizer.esm.js +6 -3
- package/dist/components/AppVisualizerPage/TextVisualizer.esm.js.map +1 -1
- package/dist/components/AppVisualizerPage/TreeVisualizer.esm.js +16 -24
- package/dist/components/AppVisualizerPage/TreeVisualizer.esm.js.map +1 -1
- package/dist/components/CopyTreeButton.esm.js +43 -0
- package/dist/components/CopyTreeButton.esm.js.map +1 -0
- package/dist/index.d.ts +111 -2
- package/dist/package.json.esm.js +3 -3
- package/dist/plugin.esm.js +47 -3
- package/dist/plugin.esm.js.map +1 -1
- package/package.json +9 -9
- package/dist/components/AppVisualizerPage/AppVisualizerPage.esm.js +0 -72
- package/dist/components/AppVisualizerPage/AppVisualizerPage.esm.js.map +0 -1
- package/dist/components/AppVisualizerPage/index.esm.js +0 -2
- package/dist/components/AppVisualizerPage/index.esm.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,35 @@
|
|
|
1
1
|
# @backstage/plugin-app-visualizer
|
|
2
2
|
|
|
3
|
+
## 0.2.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- ef6916e: Migrated to use `SubPageBlueprint` for tabbed navigation and added a copy-tree-as-JSON plugin header action using `PluginHeaderActionBlueprint`. The plugin now specifies a `title` and `icon`.
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- cb090b4: Bump react-aria-components to v1.14.0
|
|
12
|
+
- c38b74d: Internal updates for blueprint moves to `@backstage/plugin-app-react`.
|
|
13
|
+
- 4137a43: Updated CSS token references to use renamed `--bui-border-2` token.
|
|
14
|
+
- 4d50e1f: Improved rendering performance of the details page.
|
|
15
|
+
- a7e0d50: Updated `react-router-dom` peer dependency to `^6.30.2` and explicitly disabled v7 future flags to suppress deprecation warnings.
|
|
16
|
+
- Updated dependencies
|
|
17
|
+
- @backstage/ui@0.12.0
|
|
18
|
+
- @backstage/core-components@0.18.7
|
|
19
|
+
- @backstage/frontend-plugin-api@0.14.0
|
|
20
|
+
- @backstage/core-plugin-api@1.12.3
|
|
21
|
+
|
|
22
|
+
## 0.1.28-next.1
|
|
23
|
+
|
|
24
|
+
### Patch Changes
|
|
25
|
+
|
|
26
|
+
- a7e0d50: Prepare for React Router v7 migration by updating to v6.30.2 across all NFS packages and enabling v7 future flags. Convert routes from splat paths to parent/child structure with Outlet components.
|
|
27
|
+
- Updated dependencies
|
|
28
|
+
- @backstage/ui@0.12.0-next.2
|
|
29
|
+
- @backstage/frontend-plugin-api@0.14.0-next.2
|
|
30
|
+
- @backstage/core-components@0.18.7-next.2
|
|
31
|
+
- @backstage/core-plugin-api@1.12.3-next.1
|
|
32
|
+
|
|
3
33
|
## 0.1.28-next.0
|
|
4
34
|
|
|
5
35
|
### Patch Changes
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { NavItemBlueprint, coreExtensionData, ApiBlueprint,
|
|
3
|
-
import { Flex, Box, TooltipTrigger, Text, Tooltip, Link } from '@backstage/ui';
|
|
1
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
2
|
+
import { useApi, appTreeApiRef, NavItemBlueprint, coreExtensionData, ApiBlueprint, routeResolutionApiRef } from '@backstage/frontend-plugin-api';
|
|
3
|
+
import { FullPage, Flex, Box, TooltipTrigger, Text, Tooltip, Link } from '@backstage/ui';
|
|
4
4
|
import { RiCloseCircleLine, RiInputField } from '@remixicon/react';
|
|
5
5
|
import { Focusable } from 'react-aria-components';
|
|
6
6
|
import { useMemo, memo, useState, useRef, useEffect, Fragment as Fragment$1 } from 'react';
|
|
@@ -184,7 +184,7 @@ function Extension({ node, depth }) {
|
|
|
184
184
|
style: {
|
|
185
185
|
width: "fit-content",
|
|
186
186
|
color: enabled ? "var(--bui-fg-primary)" : "var(--bui-fg-disabled)",
|
|
187
|
-
background: "var(--bui-bg-
|
|
187
|
+
background: "var(--bui-bg-neutral-1)",
|
|
188
188
|
borderTopRightRadius: "var(--bui-radius-2)",
|
|
189
189
|
borderBottomRightRadius: "var(--bui-radius-2)"
|
|
190
190
|
},
|
|
@@ -263,8 +263,10 @@ function Legend() {
|
|
|
263
263
|
}
|
|
264
264
|
);
|
|
265
265
|
}
|
|
266
|
-
function DetailedVisualizer(
|
|
267
|
-
|
|
266
|
+
function DetailedVisualizer() {
|
|
267
|
+
const appTreeApi = useApi(appTreeApiRef);
|
|
268
|
+
const { tree } = appTreeApi.getTree();
|
|
269
|
+
return /* @__PURE__ */ jsx(FullPage, { children: /* @__PURE__ */ jsxs(Flex, { direction: "column", style: { height: "100%", flex: "1 1 100%" }, children: [
|
|
268
270
|
/* @__PURE__ */ jsx(Box, { ml: "4", mt: "4", style: { flex: "1 1 0", overflow: "auto" }, children: /* @__PURE__ */ jsx(Extension, { node: tree.root, depth: 0 }) }),
|
|
269
271
|
/* @__PURE__ */ jsx(
|
|
270
272
|
Box,
|
|
@@ -272,14 +274,14 @@ function DetailedVisualizer({ tree }) {
|
|
|
272
274
|
m: "2",
|
|
273
275
|
style: {
|
|
274
276
|
flex: "0 0 auto",
|
|
275
|
-
background: "var(--bui-bg-
|
|
276
|
-
border: "1px solid var(--bui-border)",
|
|
277
|
+
background: "var(--bui-bg-neutral-1)",
|
|
278
|
+
border: "1px solid var(--bui-border-2)",
|
|
277
279
|
borderRadius: "var(--bui-radius-2)"
|
|
278
280
|
},
|
|
279
281
|
children: /* @__PURE__ */ jsx(Legend, {})
|
|
280
282
|
}
|
|
281
283
|
)
|
|
282
|
-
] });
|
|
284
|
+
] }) });
|
|
283
285
|
}
|
|
284
286
|
|
|
285
287
|
export { DetailedVisualizer };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DetailedVisualizer.esm.js","sources":["../../../src/components/AppVisualizerPage/DetailedVisualizer.tsx"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n AppNode,\n AppTree,\n ExtensionDataRef,\n coreExtensionData,\n ApiBlueprint,\n NavItemBlueprint,\n useApi,\n routeResolutionApiRef,\n} from '@backstage/frontend-plugin-api';\nimport { Box, Flex, Link, Text, Tooltip, TooltipTrigger } from '@backstage/ui';\nimport {\n RiInputField as InputIcon,\n RiCloseCircleLine as DisabledIcon,\n} from '@remixicon/react';\nimport { Focusable } from 'react-aria-components';\nimport { memo, useMemo, useState, useEffect, useRef, Fragment } from 'react';\n\nfunction getContrastColor(bgColor: string): string {\n const hex = bgColor.replace('#', '');\n const r = parseInt(hex.substr(0, 2), 16);\n const g = parseInt(hex.substr(2, 2), 16);\n const b = parseInt(hex.substr(4, 2), 16);\n const brightness = (r * 299 + g * 587 + b * 114) / 1000;\n return brightness > 128 ? '#000000' : '#ffffff';\n}\n\nfunction createOutputColorGenerator(\n colorMap: { [extDataId: string]: string },\n availableColors: string[],\n) {\n const map = new Map<string, { backgroundColor: string; color: string }>();\n let i = 0;\n\n return function getOutputColor(id: string) {\n let backgroundColor: string;\n if (id in colorMap) {\n backgroundColor = colorMap[id];\n } else {\n const cached = map.get(id);\n if (cached) {\n return cached;\n }\n backgroundColor = availableColors[i];\n i += 1;\n if (i >= availableColors.length) {\n i = 0;\n }\n }\n const result = {\n backgroundColor,\n color: getContrastColor(backgroundColor),\n };\n map.set(id, result);\n return result;\n };\n}\n\nconst getOutputColor = createOutputColorGenerator(\n {\n [coreExtensionData.reactElement.id]: '#4caf50',\n [coreExtensionData.routePath.id]: '#ffeb3b',\n [coreExtensionData.routeRef.id]: '#9c27b0',\n [ApiBlueprint.dataRefs.factory.id]: '#2196f3',\n [NavItemBlueprint.dataRefs.target.id]: '#ff9800',\n },\n\n ['#90caf9', '#ffcc80', '#a5d6a7', '#ef9a9a', '#fff59d', '#ce93d8', '#e6ee9c'],\n);\n\n// Helper function to get border color based on depth\nfunction getBorderColor(depth: number): string {\n const greyLevels = [8, 7, 6, 5]; // darker levels that contrast well with background\n const index = depth % greyLevels.length;\n const level = greyLevels[index];\n return `var(--bui-gray-${level})`;\n}\n\nfunction getFullPath(node?: AppNode): string {\n if (!node) {\n return '';\n }\n const parent = node.edges.attachedTo?.node;\n const part = node.instance?.getData(coreExtensionData.routePath);\n if (!part) {\n return getFullPath(parent);\n }\n return getFullPath(parent) + part;\n}\n\nfunction ProgressiveCollection({\n items,\n batchSize = 10,\n}: {\n items: Array<() => React.ReactElement>;\n batchSize?: number;\n}) {\n const [maxIndex, setMaxIndex] = useState(0);\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n useEffect(() => {\n if (maxIndex >= items.length) {\n return undefined;\n }\n\n const processBatch = () => {\n const nextIndex = Math.min(maxIndex + batchSize, items.length);\n setMaxIndex(nextIndex);\n\n if (nextIndex < items.length) {\n timeoutRef.current = setTimeout(processBatch, 0);\n }\n };\n\n timeoutRef.current = setTimeout(processBatch, 0);\n\n // eslint-disable-next-line consistent-return\n return () => {\n if (timeoutRef.current !== null) {\n clearTimeout(timeoutRef.current);\n timeoutRef.current = null;\n }\n };\n }, [maxIndex, items.length, batchSize]);\n\n return (\n <>\n {items.slice(0, maxIndex).map((item, index) => (\n <Fragment key={index}>{item()}</Fragment>\n ))}\n </>\n );\n}\n\nconst Output = memo(\n function Output(props: {\n dataRef: ExtensionDataRef<unknown>;\n node?: AppNode;\n }) {\n const { dataRef, node } = props;\n const { id } = dataRef;\n const instance = node?.instance;\n\n const routeResolutionApi = useApi(routeResolutionApiRef);\n\n const { backgroundColor, color } = getOutputColor(id);\n\n const chipStyle: React.CSSProperties = {\n height: 20,\n padding: '0 10px',\n borderRadius: '10px',\n color,\n backgroundColor,\n display: 'flex',\n alignItems: 'center',\n fontWeight:\n 'var(--bui-font-weight-regular)' as React.CSSProperties['fontWeight'],\n };\n\n if (id === coreExtensionData.routeRef.id && node) {\n try {\n const routeRef = props.node?.instance?.getData(\n coreExtensionData.routeRef,\n );\n const link = routeRef && routeResolutionApi.resolve(routeRef)?.();\n if (link) {\n return (\n <TooltipTrigger>\n <Link href={link} style={chipStyle}>\n link\n </Link>\n <Tooltip>{id}</Tooltip>\n </TooltipTrigger>\n );\n }\n } catch {\n /* ignore */\n }\n }\n\n let tooltip = id;\n let text: string | undefined = undefined;\n if (id === coreExtensionData.routePath.id) {\n text = String(instance?.getData(dataRef) ?? '');\n tooltip = getFullPath(node);\n }\n\n return (\n <TooltipTrigger>\n <Focusable>\n <Text style={{ ...chipStyle, cursor: 'help' }}>{text}</Text>\n </Focusable>\n <Tooltip style={{ maxWidth: 'unset' }}>{tooltip}</Tooltip>\n </TooltipTrigger>\n );\n },\n (prevProps, nextProps) => {\n return (\n prevProps.dataRef.id === nextProps.dataRef.id &&\n prevProps.node?.spec.id === nextProps.node?.spec.id\n );\n },\n);\n\nfunction Extension({ node, depth }: { node: AppNode; depth: number }) {\n const enabled = Boolean(node.instance);\n\n const tooltipText = useMemo(() => {\n const tooltipParts = [];\n let currentNode = node;\n tooltipParts.push(currentNode.spec.id);\n while (currentNode.edges.attachedTo) {\n const input = currentNode.edges.attachedTo.input;\n currentNode = currentNode.edges.attachedTo.node;\n tooltipParts.push(`${currentNode.spec.id} [${input}]`);\n }\n tooltipParts.reverse();\n return tooltipParts.join('\\n');\n }, [node]);\n\n const sortedDataRefs = useMemo(() => {\n if (!node.instance) {\n return [];\n }\n const dataRefs = [...node.instance.getDataRefs()];\n return dataRefs.sort((a, b) => a.id.localeCompare(b.id));\n }, [node.instance]);\n\n const sortedAttachments = useMemo(() => {\n return [...node.edges.attachments.entries()].sort(([a], [b]) =>\n a.localeCompare(b),\n );\n }, [node.edges.attachments]);\n\n return (\n <Box\n key={node.spec.id}\n style={{\n borderLeftWidth: 'var(--bui-space-1_5)',\n borderLeftStyle: 'solid',\n borderLeftColor: getBorderColor(depth),\n }}\n >\n <Flex\n py=\"1\"\n px=\"2\"\n align=\"center\"\n style={{\n width: 'fit-content',\n color: enabled ? 'var(--bui-fg-primary)' : 'var(--bui-fg-disabled)',\n background: 'var(--bui-bg-surface-1)',\n borderTopRightRadius: 'var(--bui-radius-2)',\n borderBottomRightRadius: 'var(--bui-radius-2)',\n }}\n >\n <TooltipTrigger>\n <Focusable>\n <Text style={{ userSelect: 'all' }}>{node.spec.id}</Text>\n </Focusable>\n <Tooltip style={{ maxWidth: 'unset' }}>\n <Text style={{ whiteSpace: 'pre-wrap' }}>{tooltipText}</Text>\n </Tooltip>\n </TooltipTrigger>\n <Flex ml=\"2\" align=\"center\" gap=\"2\">\n {sortedDataRefs.length > 0 &&\n sortedDataRefs.map(ref => (\n <Output key={ref.id} dataRef={ref} node={node} />\n ))}\n {!enabled && <DisabledIcon size={16} />}\n </Flex>\n </Flex>\n {sortedAttachments.length > 0 && (\n <Flex direction=\"column\" gap=\"4\">\n {sortedAttachments.map(([key, children], idx) => (\n <Box key={key}>\n <Flex\n p=\"2\"\n align=\"center\"\n style={{\n borderTopWidth: 'var(--bui-space-1_5)',\n borderTopStyle: 'solid',\n borderTopColor: getBorderColor(depth),\n borderTop: idx === 0 ? 'none' : undefined,\n width: 'fit-content',\n }}\n >\n <InputIcon size={16} />\n <div style={{ marginLeft: 'var(--bui-space-2)' }}>{key}</div>\n </Flex>\n <Flex ml=\"2\" mb=\"2\" direction=\"column\" align=\"start\" gap=\"1\">\n <ProgressiveCollection\n items={children.map(childNode => () => (\n <Extension\n key={childNode.spec.id}\n node={childNode}\n depth={depth + 1}\n />\n ))}\n />\n </Flex>\n </Box>\n ))}\n </Flex>\n )}\n </Box>\n );\n}\n\nconst legendMap = {\n 'React Element': coreExtensionData.reactElement,\n 'Utility API': ApiBlueprint.dataRefs.factory,\n 'Route Path': coreExtensionData.routePath,\n 'Route Ref': coreExtensionData.routeRef,\n 'Nav Target': NavItemBlueprint.dataRefs.target,\n};\n\nfunction Legend() {\n return (\n <Box\n p=\"2\"\n style={{\n display: 'grid',\n maxWidth: 600,\n grid: 'auto-flow / repeat(3, 1fr)',\n gap: 'var(--bui-space-4)',\n }}\n >\n {Object.entries(legendMap).map(([label, dataRef]) => (\n <Flex key={dataRef.id} gap=\"2\" align=\"center\">\n <Output dataRef={dataRef} />\n <div>{label}</div>\n </Flex>\n ))}\n </Box>\n );\n}\n\nexport function DetailedVisualizer({ tree }: { tree: AppTree }) {\n return (\n <Flex direction=\"column\" style={{ height: '100%', flex: '1 1 100%' }}>\n <Box ml=\"4\" mt=\"4\" style={{ flex: '1 1 0', overflow: 'auto' }}>\n <Extension node={tree.root} depth={0} />\n </Box>\n\n <Box\n m=\"2\"\n style={{\n flex: '0 0 auto',\n background: 'var(--bui-bg-surface-1)',\n border: '1px solid var(--bui-border)',\n borderRadius: 'var(--bui-radius-2)',\n }}\n >\n <Legend />\n </Box>\n </Flex>\n );\n}\n"],"names":["getOutputColor","Fragment","Output","DisabledIcon","InputIcon"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"DetailedVisualizer.esm.js","sources":["../../../src/components/AppVisualizerPage/DetailedVisualizer.tsx"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n AppNode,\n ExtensionDataRef,\n coreExtensionData,\n ApiBlueprint,\n NavItemBlueprint,\n useApi,\n routeResolutionApiRef,\n appTreeApiRef,\n} from '@backstage/frontend-plugin-api';\nimport {\n Box,\n Flex,\n FullPage,\n Link,\n Text,\n Tooltip,\n TooltipTrigger,\n} from '@backstage/ui';\nimport {\n RiInputField as InputIcon,\n RiCloseCircleLine as DisabledIcon,\n} from '@remixicon/react';\nimport { Focusable } from 'react-aria-components';\nimport { memo, useMemo, useState, useEffect, useRef, Fragment } from 'react';\n\nfunction getContrastColor(bgColor: string): string {\n const hex = bgColor.replace('#', '');\n const r = parseInt(hex.substr(0, 2), 16);\n const g = parseInt(hex.substr(2, 2), 16);\n const b = parseInt(hex.substr(4, 2), 16);\n const brightness = (r * 299 + g * 587 + b * 114) / 1000;\n return brightness > 128 ? '#000000' : '#ffffff';\n}\n\nfunction createOutputColorGenerator(\n colorMap: { [extDataId: string]: string },\n availableColors: string[],\n) {\n const map = new Map<string, { backgroundColor: string; color: string }>();\n let i = 0;\n\n return function getOutputColor(id: string) {\n let backgroundColor: string;\n if (id in colorMap) {\n backgroundColor = colorMap[id];\n } else {\n const cached = map.get(id);\n if (cached) {\n return cached;\n }\n backgroundColor = availableColors[i];\n i += 1;\n if (i >= availableColors.length) {\n i = 0;\n }\n }\n const result = {\n backgroundColor,\n color: getContrastColor(backgroundColor),\n };\n map.set(id, result);\n return result;\n };\n}\n\nconst getOutputColor = createOutputColorGenerator(\n {\n [coreExtensionData.reactElement.id]: '#4caf50',\n [coreExtensionData.routePath.id]: '#ffeb3b',\n [coreExtensionData.routeRef.id]: '#9c27b0',\n [ApiBlueprint.dataRefs.factory.id]: '#2196f3',\n [NavItemBlueprint.dataRefs.target.id]: '#ff9800',\n },\n\n ['#90caf9', '#ffcc80', '#a5d6a7', '#ef9a9a', '#fff59d', '#ce93d8', '#e6ee9c'],\n);\n\n// Helper function to get border color based on depth\nfunction getBorderColor(depth: number): string {\n const greyLevels = [8, 7, 6, 5]; // darker levels that contrast well with background\n const index = depth % greyLevels.length;\n const level = greyLevels[index];\n return `var(--bui-gray-${level})`;\n}\n\nfunction getFullPath(node?: AppNode): string {\n if (!node) {\n return '';\n }\n const parent = node.edges.attachedTo?.node;\n const part = node.instance?.getData(coreExtensionData.routePath);\n if (!part) {\n return getFullPath(parent);\n }\n return getFullPath(parent) + part;\n}\n\nfunction ProgressiveCollection({\n items,\n batchSize = 10,\n}: {\n items: Array<() => React.ReactElement>;\n batchSize?: number;\n}) {\n const [maxIndex, setMaxIndex] = useState(0);\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n useEffect(() => {\n if (maxIndex >= items.length) {\n return undefined;\n }\n\n const processBatch = () => {\n const nextIndex = Math.min(maxIndex + batchSize, items.length);\n setMaxIndex(nextIndex);\n\n if (nextIndex < items.length) {\n timeoutRef.current = setTimeout(processBatch, 0);\n }\n };\n\n timeoutRef.current = setTimeout(processBatch, 0);\n\n // eslint-disable-next-line consistent-return\n return () => {\n if (timeoutRef.current !== null) {\n clearTimeout(timeoutRef.current);\n timeoutRef.current = null;\n }\n };\n }, [maxIndex, items.length, batchSize]);\n\n return (\n <>\n {items.slice(0, maxIndex).map((item, index) => (\n <Fragment key={index}>{item()}</Fragment>\n ))}\n </>\n );\n}\n\nconst Output = memo(\n function Output(props: {\n dataRef: ExtensionDataRef<unknown>;\n node?: AppNode;\n }) {\n const { dataRef, node } = props;\n const { id } = dataRef;\n const instance = node?.instance;\n\n const routeResolutionApi = useApi(routeResolutionApiRef);\n\n const { backgroundColor, color } = getOutputColor(id);\n\n const chipStyle: React.CSSProperties = {\n height: 20,\n padding: '0 10px',\n borderRadius: '10px',\n color,\n backgroundColor,\n display: 'flex',\n alignItems: 'center',\n fontWeight:\n 'var(--bui-font-weight-regular)' as React.CSSProperties['fontWeight'],\n };\n\n if (id === coreExtensionData.routeRef.id && node) {\n try {\n const routeRef = props.node?.instance?.getData(\n coreExtensionData.routeRef,\n );\n const link = routeRef && routeResolutionApi.resolve(routeRef)?.();\n if (link) {\n return (\n <TooltipTrigger>\n <Link href={link} style={chipStyle}>\n link\n </Link>\n <Tooltip>{id}</Tooltip>\n </TooltipTrigger>\n );\n }\n } catch {\n /* ignore */\n }\n }\n\n let tooltip = id;\n let text: string | undefined = undefined;\n if (id === coreExtensionData.routePath.id) {\n text = String(instance?.getData(dataRef) ?? '');\n tooltip = getFullPath(node);\n }\n\n return (\n <TooltipTrigger>\n <Focusable>\n <Text style={{ ...chipStyle, cursor: 'help' }}>{text}</Text>\n </Focusable>\n <Tooltip style={{ maxWidth: 'unset' }}>{tooltip}</Tooltip>\n </TooltipTrigger>\n );\n },\n (prevProps, nextProps) => {\n return (\n prevProps.dataRef.id === nextProps.dataRef.id &&\n prevProps.node?.spec.id === nextProps.node?.spec.id\n );\n },\n);\n\nfunction Extension({ node, depth }: { node: AppNode; depth: number }) {\n const enabled = Boolean(node.instance);\n\n const tooltipText = useMemo(() => {\n const tooltipParts = [];\n let currentNode = node;\n tooltipParts.push(currentNode.spec.id);\n while (currentNode.edges.attachedTo) {\n const input = currentNode.edges.attachedTo.input;\n currentNode = currentNode.edges.attachedTo.node;\n tooltipParts.push(`${currentNode.spec.id} [${input}]`);\n }\n tooltipParts.reverse();\n return tooltipParts.join('\\n');\n }, [node]);\n\n const sortedDataRefs = useMemo(() => {\n if (!node.instance) {\n return [];\n }\n const dataRefs = [...node.instance.getDataRefs()];\n return dataRefs.sort((a, b) => a.id.localeCompare(b.id));\n }, [node.instance]);\n\n const sortedAttachments = useMemo(() => {\n return [...node.edges.attachments.entries()].sort(([a], [b]) =>\n a.localeCompare(b),\n );\n }, [node.edges.attachments]);\n\n return (\n <Box\n key={node.spec.id}\n style={{\n borderLeftWidth: 'var(--bui-space-1_5)',\n borderLeftStyle: 'solid',\n borderLeftColor: getBorderColor(depth),\n }}\n >\n <Flex\n py=\"1\"\n px=\"2\"\n align=\"center\"\n style={{\n width: 'fit-content',\n color: enabled ? 'var(--bui-fg-primary)' : 'var(--bui-fg-disabled)',\n background: 'var(--bui-bg-neutral-1)',\n borderTopRightRadius: 'var(--bui-radius-2)',\n borderBottomRightRadius: 'var(--bui-radius-2)',\n }}\n >\n <TooltipTrigger>\n <Focusable>\n <Text style={{ userSelect: 'all' }}>{node.spec.id}</Text>\n </Focusable>\n <Tooltip style={{ maxWidth: 'unset' }}>\n <Text style={{ whiteSpace: 'pre-wrap' }}>{tooltipText}</Text>\n </Tooltip>\n </TooltipTrigger>\n <Flex ml=\"2\" align=\"center\" gap=\"2\">\n {sortedDataRefs.length > 0 &&\n sortedDataRefs.map(ref => (\n <Output key={ref.id} dataRef={ref} node={node} />\n ))}\n {!enabled && <DisabledIcon size={16} />}\n </Flex>\n </Flex>\n {sortedAttachments.length > 0 && (\n <Flex direction=\"column\" gap=\"4\">\n {sortedAttachments.map(([key, children], idx) => (\n <Box key={key}>\n <Flex\n p=\"2\"\n align=\"center\"\n style={{\n borderTopWidth: 'var(--bui-space-1_5)',\n borderTopStyle: 'solid',\n borderTopColor: getBorderColor(depth),\n borderTop: idx === 0 ? 'none' : undefined,\n width: 'fit-content',\n }}\n >\n <InputIcon size={16} />\n <div style={{ marginLeft: 'var(--bui-space-2)' }}>{key}</div>\n </Flex>\n <Flex ml=\"2\" mb=\"2\" direction=\"column\" align=\"start\" gap=\"1\">\n <ProgressiveCollection\n items={children.map(childNode => () => (\n <Extension\n key={childNode.spec.id}\n node={childNode}\n depth={depth + 1}\n />\n ))}\n />\n </Flex>\n </Box>\n ))}\n </Flex>\n )}\n </Box>\n );\n}\n\nconst legendMap = {\n 'React Element': coreExtensionData.reactElement,\n 'Utility API': ApiBlueprint.dataRefs.factory,\n 'Route Path': coreExtensionData.routePath,\n 'Route Ref': coreExtensionData.routeRef,\n 'Nav Target': NavItemBlueprint.dataRefs.target,\n};\n\nfunction Legend() {\n return (\n <Box\n p=\"2\"\n style={{\n display: 'grid',\n maxWidth: 600,\n grid: 'auto-flow / repeat(3, 1fr)',\n gap: 'var(--bui-space-4)',\n }}\n >\n {Object.entries(legendMap).map(([label, dataRef]) => (\n <Flex key={dataRef.id} gap=\"2\" align=\"center\">\n <Output dataRef={dataRef} />\n <div>{label}</div>\n </Flex>\n ))}\n </Box>\n );\n}\n\nexport function DetailedVisualizer() {\n const appTreeApi = useApi(appTreeApiRef);\n const { tree } = appTreeApi.getTree();\n\n return (\n <FullPage>\n <Flex direction=\"column\" style={{ height: '100%', flex: '1 1 100%' }}>\n <Box ml=\"4\" mt=\"4\" style={{ flex: '1 1 0', overflow: 'auto' }}>\n <Extension node={tree.root} depth={0} />\n </Box>\n\n <Box\n m=\"2\"\n style={{\n flex: '0 0 auto',\n background: 'var(--bui-bg-neutral-1)',\n border: '1px solid var(--bui-border-2)',\n borderRadius: 'var(--bui-radius-2)',\n }}\n >\n <Legend />\n </Box>\n </Flex>\n </FullPage>\n );\n}\n"],"names":["getOutputColor","Fragment","Output","DisabledIcon","InputIcon"],"mappings":"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"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { useApi, appTreeApiRef } from '@backstage/frontend-plugin-api';
|
|
2
3
|
import { Box, Checkbox } from '@backstage/ui';
|
|
3
4
|
import { useState } from 'react';
|
|
4
5
|
|
|
@@ -43,7 +44,9 @@ function nodeToText(node, options) {
|
|
|
43
44
|
mkDiv(`</${node.spec.id}>`, { key: "end", color })
|
|
44
45
|
]);
|
|
45
46
|
}
|
|
46
|
-
function TextVisualizer(
|
|
47
|
+
function TextVisualizer() {
|
|
48
|
+
const appTreeApi = useApi(appTreeApiRef);
|
|
49
|
+
const { tree } = appTreeApi.getTree();
|
|
47
50
|
const [showOutputs, setShowOutputs] = useState(false);
|
|
48
51
|
const [showDisabled, setShowDisabled] = useState(false);
|
|
49
52
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
@@ -54,8 +57,8 @@ function TextVisualizer({ tree }) {
|
|
|
54
57
|
py: "2",
|
|
55
58
|
px: "4",
|
|
56
59
|
style: {
|
|
57
|
-
background: "var(--bui-bg-
|
|
58
|
-
borderTop: "1px solid var(--bui-border)"
|
|
60
|
+
background: "var(--bui-bg-neutral-1)",
|
|
61
|
+
borderTop: "1px solid var(--bui-border-2)"
|
|
59
62
|
},
|
|
60
63
|
children: [
|
|
61
64
|
/* @__PURE__ */ jsx(Checkbox, { isSelected: showOutputs, onChange: setShowOutputs, children: "Show Outputs" }),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TextVisualizer.esm.js","sources":["../../../src/components/AppVisualizerPage/TextVisualizer.tsx"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { AppNode,
|
|
1
|
+
{"version":3,"file":"TextVisualizer.esm.js","sources":["../../../src/components/AppVisualizerPage/TextVisualizer.tsx"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { AppNode, useApi, appTreeApiRef } from '@backstage/frontend-plugin-api';\nimport { Box, Checkbox } from '@backstage/ui';\nimport { ReactNode, useState } from 'react';\n\nfunction mkDiv(\n children: ReactNode,\n options?: { indent?: boolean; key?: string | number; color?: string },\n) {\n return (\n <div\n key={options?.key}\n style={{\n color: options?.color,\n marginLeft: options?.indent ? 'var(--bui-space-4)' : undefined,\n }}\n >\n {children}\n </div>\n );\n}\n\nfunction nodeToText(\n node: AppNode,\n options?: { showOutputs?: boolean; showDisabled?: boolean },\n): ReactNode {\n const dataRefIds =\n node.instance && [...node.instance.getDataRefs()].map(r => r.id);\n const out =\n options?.showOutputs && dataRefIds && dataRefIds.length > 0\n ? ` out=\"${[...dataRefIds].sort().join(', ')}\"`\n : '';\n const color = node.instance ? undefined : 'gray';\n\n if (node.edges.attachments.size === 0) {\n return mkDiv(`<${node.spec.id}${out}/>`, { color });\n }\n\n return mkDiv([\n mkDiv(`<${node.spec.id}${out}>`, { key: 'start', color }),\n ...[...node.edges.attachments.entries()]\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([key, v]) => {\n const children = v\n .filter(e => options?.showDisabled || e.instance)\n .sort((a, b) => a.spec.id.localeCompare(b.spec.id));\n if (children.length === 0) {\n return mkDiv(`${key} []`, { key, indent: true });\n }\n return mkDiv(\n [\n mkDiv(`${key} [`, { key: 'start' }),\n ...children.map(e =>\n mkDiv(nodeToText(e, options), { indent: true, key: e.spec.id }),\n ),\n mkDiv(']', { key: 'end' }),\n ],\n { key, indent: true },\n );\n }),\n mkDiv(`</${node.spec.id}>`, { key: 'end', color }),\n ]);\n}\n\nexport function TextVisualizer() {\n const appTreeApi = useApi(appTreeApiRef);\n const { tree } = appTreeApi.getTree();\n const [showOutputs, setShowOutputs] = useState(false);\n const [showDisabled, setShowDisabled] = useState(false);\n\n return (\n <>\n <Box style={{ overflow: 'auto', flex: '1 0 0' }}>\n <Box m=\"4\" style={{ width: 'max-content' }}>\n {nodeToText(tree.root, { showOutputs, showDisabled })}\n </Box>\n </Box>\n <Box\n py=\"2\"\n px=\"4\"\n style={{\n background: 'var(--bui-bg-neutral-1)',\n borderTop: '1px solid var(--bui-border-2)',\n }}\n >\n <Checkbox isSelected={showOutputs} onChange={setShowOutputs}>\n Show Outputs\n </Checkbox>\n <Checkbox isSelected={showDisabled} onChange={setShowDisabled}>\n Show Disabled\n </Checkbox>\n </Box>\n </>\n );\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
2
2
|
import { DependencyGraph, DependencyGraphTypes } from '@backstage/core-components';
|
|
3
|
-
import {
|
|
3
|
+
import { useApi, appTreeApiRef } from '@backstage/frontend-plugin-api';
|
|
4
|
+
import { FullPage, Flex } from '@backstage/ui';
|
|
4
5
|
import { useMemo, useState, useRef, useLayoutEffect } from 'react';
|
|
5
6
|
|
|
6
7
|
function inputId({ node, input }) {
|
|
@@ -96,32 +97,23 @@ function Node(props) {
|
|
|
96
97
|
)
|
|
97
98
|
] });
|
|
98
99
|
}
|
|
99
|
-
function TreeVisualizer(
|
|
100
|
+
function TreeVisualizer() {
|
|
101
|
+
const appTreeApi = useApi(appTreeApiRef);
|
|
102
|
+
const { tree } = appTreeApi.getTree();
|
|
100
103
|
const graphData = useMemo(() => resolveGraphData(tree), [tree]);
|
|
101
|
-
return /* @__PURE__ */ jsx(
|
|
102
|
-
|
|
104
|
+
return /* @__PURE__ */ jsx(FullPage, { children: /* @__PURE__ */ jsx(Flex, { style: { height: "100%" }, children: /* @__PURE__ */ jsx(
|
|
105
|
+
DependencyGraph,
|
|
103
106
|
{
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
{
|
|
113
|
-
fit: "contain",
|
|
114
|
-
...graphData,
|
|
115
|
-
nodeMargin: 10,
|
|
116
|
-
rankMargin: 50,
|
|
117
|
-
paddingX: 50,
|
|
118
|
-
renderNode: Node,
|
|
119
|
-
ranker: DependencyGraphTypes.Ranker.TIGHT_TREE,
|
|
120
|
-
direction: DependencyGraphTypes.Direction.LEFT_RIGHT
|
|
121
|
-
}
|
|
122
|
-
)
|
|
107
|
+
fit: "contain",
|
|
108
|
+
...graphData,
|
|
109
|
+
nodeMargin: 10,
|
|
110
|
+
rankMargin: 50,
|
|
111
|
+
paddingX: 50,
|
|
112
|
+
renderNode: Node,
|
|
113
|
+
ranker: DependencyGraphTypes.Ranker.TIGHT_TREE,
|
|
114
|
+
direction: DependencyGraphTypes.Direction.LEFT_RIGHT
|
|
123
115
|
}
|
|
124
|
-
);
|
|
116
|
+
) }) });
|
|
125
117
|
}
|
|
126
118
|
|
|
127
119
|
export { Node, TreeVisualizer };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TreeVisualizer.esm.js","sources":["../../../src/components/AppVisualizerPage/TreeVisualizer.tsx"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n DependencyGraph,\n DependencyGraphTypes,\n} from '@backstage/core-components';\nimport {
|
|
1
|
+
{"version":3,"file":"TreeVisualizer.esm.js","sources":["../../../src/components/AppVisualizerPage/TreeVisualizer.tsx"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n DependencyGraph,\n DependencyGraphTypes,\n} from '@backstage/core-components';\nimport {\n AppNode,\n AppTree,\n useApi,\n appTreeApiRef,\n} from '@backstage/frontend-plugin-api';\nimport { Flex, FullPage } from '@backstage/ui';\nimport { useLayoutEffect, useMemo, useRef, useState } from 'react';\n\ntype NodeType =\n | ({ type: 'node'; id: string } & AppNode)\n | { type: 'input'; id: string; name: string };\n\nfunction inputId({ node, input }: { node: AppNode; input: string }) {\n return `${node.spec.id}$$${input}`;\n}\n\nfunction trimNodeId(id: string) {\n let newId = id;\n if (newId.startsWith('apis.')) {\n newId = newId.slice('apis.'.length);\n }\n if (newId.startsWith('plugin.')) {\n newId = newId.slice('plugin.'.length);\n }\n if (newId.startsWith('catalog.filter.entity.')) {\n newId = newId.slice('catalog.filter.entity.'.length);\n }\n if (newId.endsWith('.nav.index')) {\n newId = newId.slice(0, -'.nav.index'.length);\n }\n return newId;\n}\n\nfunction resolveGraphData(tree: AppTree): {\n nodes: NodeType[];\n edges: { from: string; to: string }[];\n} {\n const nodes = [...tree.nodes.values()]\n .filter(node => node.instance)\n .map(node => ({ ...node, id: node.spec.id, type: 'node' as const }));\n\n return {\n nodes: [\n ...nodes,\n ...nodes.flatMap(node =>\n [...node.edges.attachments.keys()].map(input => ({\n id: inputId({ node, input }),\n type: 'input' as const,\n name: input,\n })),\n ),\n ],\n edges: [\n ...nodes\n .filter(node => node.edges.attachedTo)\n .map(node => ({\n from: inputId(node.edges.attachedTo!),\n to: node.spec.id,\n })),\n ...nodes.flatMap(node =>\n [...node.edges.attachments.keys()].map(input => ({\n from: node.spec.id,\n to: inputId({ node, input }),\n })),\n ),\n ],\n };\n}\n\n/** @public */\nexport function Node(props: { node: NodeType }) {\n const { node } = props;\n const [width, setWidth] = useState(0);\n const [height, setHeight] = useState(0);\n const idRef = useRef<SVGTextElement | null>(null);\n\n useLayoutEffect(() => {\n // set the width to the length of the ID\n if (idRef.current) {\n let { height: renderedHeight, width: renderedWidth } =\n idRef.current.getBBox();\n renderedHeight = Math.round(renderedHeight);\n renderedWidth = Math.round(renderedWidth);\n\n if (renderedHeight !== height || renderedWidth !== width) {\n setWidth(renderedWidth);\n setHeight(renderedHeight);\n }\n }\n }, [width, height]);\n\n const padding = 10;\n const paddedWidth = width + padding * 2;\n const paddedHeight = height + padding * 2;\n\n // Simple inline styles for SVG elements\n const nodeFill = node.type === 'node' ? '#90caf9' : '#9e9e9e';\n const nodeStroke = node.type === 'node' ? '#2196f3' : '#757575';\n const textFill = '#000000';\n\n return (\n <g>\n <rect\n fill={nodeFill}\n stroke={nodeStroke}\n width={paddedWidth}\n height={paddedHeight}\n rx={node.type === 'node' ? 0 : 20}\n />\n <text\n ref={idRef}\n fill={textFill}\n y={paddedHeight / 2}\n x={paddedWidth / 2}\n textAnchor=\"middle\"\n alignmentBaseline=\"middle\"\n >\n {node.type === 'node' ? trimNodeId(node.id) : node.name}\n </text>\n </g>\n );\n}\n\nexport function TreeVisualizer() {\n const appTreeApi = useApi(appTreeApiRef);\n const { tree } = appTreeApi.getTree();\n const graphData = useMemo(() => resolveGraphData(tree), [tree]);\n\n return (\n <FullPage>\n <Flex style={{ height: '100%' }}>\n <DependencyGraph\n fit=\"contain\"\n {...graphData}\n nodeMargin={10}\n rankMargin={50}\n paddingX={50}\n renderNode={Node}\n ranker={DependencyGraphTypes.Ranker.TIGHT_TREE}\n direction={DependencyGraphTypes.Direction.LEFT_RIGHT}\n />\n </Flex>\n </FullPage>\n );\n}\n"],"names":[],"mappings":";;;;;;AAiCA,SAAS,OAAA,CAAQ,EAAE,IAAA,EAAM,KAAA,EAAM,EAAqC;AAClE,EAAA,OAAO,CAAA,EAAG,IAAA,CAAK,IAAA,CAAK,EAAE,KAAK,KAAK,CAAA,CAAA;AAClC;AAEA,SAAS,WAAW,EAAA,EAAY;AAC9B,EAAA,IAAI,KAAA,GAAQ,EAAA;AACZ,EAAA,IAAI,KAAA,CAAM,UAAA,CAAW,OAAO,CAAA,EAAG;AAC7B,IAAA,KAAA,GAAQ,KAAA,CAAM,KAAA,CAAM,OAAA,CAAQ,MAAM,CAAA;AAAA,EACpC;AACA,EAAA,IAAI,KAAA,CAAM,UAAA,CAAW,SAAS,CAAA,EAAG;AAC/B,IAAA,KAAA,GAAQ,KAAA,CAAM,KAAA,CAAM,SAAA,CAAU,MAAM,CAAA;AAAA,EACtC;AACA,EAAA,IAAI,KAAA,CAAM,UAAA,CAAW,wBAAwB,CAAA,EAAG;AAC9C,IAAA,KAAA,GAAQ,KAAA,CAAM,KAAA,CAAM,wBAAA,CAAyB,MAAM,CAAA;AAAA,EACrD;AACA,EAAA,IAAI,KAAA,CAAM,QAAA,CAAS,YAAY,CAAA,EAAG;AAChC,IAAA,KAAA,GAAQ,KAAA,CAAM,KAAA,CAAM,CAAA,EAAG,CAAC,aAAa,MAAM,CAAA;AAAA,EAC7C;AACA,EAAA,OAAO,KAAA;AACT;AAEA,SAAS,iBAAiB,IAAA,EAGxB;AACA,EAAA,MAAM,KAAA,GAAQ,CAAC,GAAG,IAAA,CAAK,KAAA,CAAM,QAAQ,CAAA,CAClC,MAAA,CAAO,CAAA,IAAA,KAAQ,IAAA,CAAK,QAAQ,EAC5B,GAAA,CAAI,CAAA,IAAA,MAAS,EAAE,GAAG,IAAA,EAAM,EAAA,EAAI,KAAK,IAAA,CAAK,EAAA,EAAI,IAAA,EAAM,MAAA,EAAgB,CAAE,CAAA;AAErE,EAAA,OAAO;AAAA,IACL,KAAA,EAAO;AAAA,MACL,GAAG,KAAA;AAAA,MACH,GAAG,KAAA,CAAM,OAAA;AAAA,QAAQ,CAAA,IAAA,KACf,CAAC,GAAG,IAAA,CAAK,KAAA,CAAM,YAAY,IAAA,EAAM,CAAA,CAAE,GAAA,CAAI,CAAA,KAAA,MAAU;AAAA,UAC/C,EAAA,EAAI,OAAA,CAAQ,EAAE,IAAA,EAAM,OAAO,CAAA;AAAA,UAC3B,IAAA,EAAM,OAAA;AAAA,UACN,IAAA,EAAM;AAAA,SACR,CAAE;AAAA;AACJ,KACF;AAAA,IACA,KAAA,EAAO;AAAA,MACL,GAAG,MACA,MAAA,CAAO,CAAA,IAAA,KAAQ,KAAK,KAAA,CAAM,UAAU,CAAA,CACpC,GAAA,CAAI,CAAA,IAAA,MAAS;AAAA,QACZ,IAAA,EAAM,OAAA,CAAQ,IAAA,CAAK,KAAA,CAAM,UAAW,CAAA;AAAA,QACpC,EAAA,EAAI,KAAK,IAAA,CAAK;AAAA,OAChB,CAAE,CAAA;AAAA,MACJ,GAAG,KAAA,CAAM,OAAA;AAAA,QAAQ,CAAA,IAAA,KACf,CAAC,GAAG,IAAA,CAAK,KAAA,CAAM,YAAY,IAAA,EAAM,CAAA,CAAE,GAAA,CAAI,CAAA,KAAA,MAAU;AAAA,UAC/C,IAAA,EAAM,KAAK,IAAA,CAAK,EAAA;AAAA,UAChB,EAAA,EAAI,OAAA,CAAQ,EAAE,IAAA,EAAM,OAAO;AAAA,SAC7B,CAAE;AAAA;AACJ;AACF,GACF;AACF;AAGO,SAAS,KAAK,KAAA,EAA2B;AAC9C,EAAA,MAAM,EAAE,MAAK,GAAI,KAAA;AACjB,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAI,SAAS,CAAC,CAAA;AACpC,EAAA,MAAM,CAAC,MAAA,EAAQ,SAAS,CAAA,GAAI,SAAS,CAAC,CAAA;AACtC,EAAA,MAAM,KAAA,GAAQ,OAA8B,IAAI,CAAA;AAEhD,EAAA,eAAA,CAAgB,MAAM;AAEpB,IAAA,IAAI,MAAM,OAAA,EAAS;AACjB,MAAA,IAAI,EAAE,QAAQ,cAAA,EAAgB,KAAA,EAAO,eAAc,GACjD,KAAA,CAAM,QAAQ,OAAA,EAAQ;AACxB,MAAA,cAAA,GAAiB,IAAA,CAAK,MAAM,cAAc,CAAA;AAC1C,MAAA,aAAA,GAAgB,IAAA,CAAK,MAAM,aAAa,CAAA;AAExC,MAAA,IAAI,cAAA,KAAmB,MAAA,IAAU,aAAA,KAAkB,KAAA,EAAO;AACxD,QAAA,QAAA,CAAS,aAAa,CAAA;AACtB,QAAA,SAAA,CAAU,cAAc,CAAA;AAAA,MAC1B;AAAA,IACF;AAAA,EACF,CAAA,EAAG,CAAC,KAAA,EAAO,MAAM,CAAC,CAAA;AAElB,EAAA,MAAM,OAAA,GAAU,EAAA;AAChB,EAAA,MAAM,WAAA,GAAc,QAAQ,OAAA,GAAU,CAAA;AACtC,EAAA,MAAM,YAAA,GAAe,SAAS,OAAA,GAAU,CAAA;AAGxC,EAAA,MAAM,QAAA,GAAW,IAAA,CAAK,IAAA,KAAS,MAAA,GAAS,SAAA,GAAY,SAAA;AACpD,EAAA,MAAM,UAAA,GAAa,IAAA,CAAK,IAAA,KAAS,MAAA,GAAS,SAAA,GAAY,SAAA;AACtD,EAAA,MAAM,QAAA,GAAW,SAAA;AAEjB,EAAA,4BACG,GAAA,EAAA,EACC,QAAA,EAAA;AAAA,oBAAA,GAAA;AAAA,MAAC,MAAA;AAAA,MAAA;AAAA,QACC,IAAA,EAAM,QAAA;AAAA,QACN,MAAA,EAAQ,UAAA;AAAA,QACR,KAAA,EAAO,WAAA;AAAA,QACP,MAAA,EAAQ,YAAA;AAAA,QACR,EAAA,EAAI,IAAA,CAAK,IAAA,KAAS,MAAA,GAAS,CAAA,GAAI;AAAA;AAAA,KACjC;AAAA,oBACA,GAAA;AAAA,MAAC,MAAA;AAAA,MAAA;AAAA,QACC,GAAA,EAAK,KAAA;AAAA,QACL,IAAA,EAAM,QAAA;AAAA,QACN,GAAG,YAAA,GAAe,CAAA;AAAA,QAClB,GAAG,WAAA,GAAc,CAAA;AAAA,QACjB,UAAA,EAAW,QAAA;AAAA,QACX,iBAAA,EAAkB,QAAA;AAAA,QAEjB,eAAK,IAAA,KAAS,MAAA,GAAS,WAAW,IAAA,CAAK,EAAE,IAAI,IAAA,CAAK;AAAA;AAAA;AACrD,GAAA,EACF,CAAA;AAEJ;AAEO,SAAS,cAAA,GAAiB;AAC/B,EAAA,MAAM,UAAA,GAAa,OAAO,aAAa,CAAA;AACvC,EAAA,MAAM,EAAE,IAAA,EAAK,GAAI,UAAA,CAAW,OAAA,EAAQ;AACpC,EAAA,MAAM,SAAA,GAAY,QAAQ,MAAM,gBAAA,CAAiB,IAAI,CAAA,EAAG,CAAC,IAAI,CAAC,CAAA;AAE9D,EAAA,uBACE,GAAA,CAAC,YACC,QAAA,kBAAA,GAAA,CAAC,IAAA,EAAA,EAAK,OAAO,EAAE,MAAA,EAAQ,QAAO,EAC5B,QAAA,kBAAA,GAAA;AAAA,IAAC,eAAA;AAAA,IAAA;AAAA,MACC,GAAA,EAAI,SAAA;AAAA,MACH,GAAG,SAAA;AAAA,MACJ,UAAA,EAAY,EAAA;AAAA,MACZ,UAAA,EAAY,EAAA;AAAA,MACZ,QAAA,EAAU,EAAA;AAAA,MACV,UAAA,EAAY,IAAA;AAAA,MACZ,MAAA,EAAQ,qBAAqB,MAAA,CAAO,UAAA;AAAA,MACpC,SAAA,EAAW,qBAAqB,SAAA,CAAU;AAAA;AAAA,KAE9C,CAAA,EACF,CAAA;AAEJ;;;;"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { useApi, appTreeApiRef } from '@backstage/frontend-plugin-api';
|
|
4
|
+
import { Button } from '@backstage/ui';
|
|
5
|
+
import { RiCheckLine, RiFileCopyLine } from '@remixicon/react';
|
|
6
|
+
|
|
7
|
+
function nodeToJson(node) {
|
|
8
|
+
const attachments = {};
|
|
9
|
+
for (const [input, children] of node.edges.attachments) {
|
|
10
|
+
attachments[input] = children.map(nodeToJson);
|
|
11
|
+
}
|
|
12
|
+
return {
|
|
13
|
+
id: node.spec.id,
|
|
14
|
+
plugin: node.spec.plugin.pluginId,
|
|
15
|
+
disabled: node.spec.disabled || void 0,
|
|
16
|
+
...Object.keys(attachments).length > 0 ? { attachments } : {}
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
function CopyTreeButton() {
|
|
20
|
+
const appTreeApi = useApi(appTreeApiRef);
|
|
21
|
+
const [copied, setCopied] = useState(false);
|
|
22
|
+
const handlePress = () => {
|
|
23
|
+
const { tree } = appTreeApi.getTree();
|
|
24
|
+
const json = JSON.stringify(nodeToJson(tree.root), null, 2);
|
|
25
|
+
window.navigator.clipboard.writeText(json).then(() => {
|
|
26
|
+
setCopied(true);
|
|
27
|
+
setTimeout(() => setCopied(false), 2e3);
|
|
28
|
+
});
|
|
29
|
+
};
|
|
30
|
+
return /* @__PURE__ */ jsx(
|
|
31
|
+
Button,
|
|
32
|
+
{
|
|
33
|
+
variant: "secondary",
|
|
34
|
+
size: "small",
|
|
35
|
+
iconStart: copied ? /* @__PURE__ */ jsx(RiCheckLine, {}) : /* @__PURE__ */ jsx(RiFileCopyLine, {}),
|
|
36
|
+
onPress: handlePress,
|
|
37
|
+
children: copied ? "Copied!" : "Copy as JSON"
|
|
38
|
+
}
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export { CopyTreeButton };
|
|
43
|
+
//# sourceMappingURL=CopyTreeButton.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CopyTreeButton.esm.js","sources":["../../src/components/CopyTreeButton.tsx"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { useState } from 'react';\nimport {\n useApi,\n appTreeApiRef,\n type AppNode,\n} from '@backstage/frontend-plugin-api';\nimport { Button } from '@backstage/ui';\nimport { RiFileCopyLine, RiCheckLine } from '@remixicon/react';\n\nfunction nodeToJson(node: AppNode): object {\n const attachments: Record<string, object[]> = {};\n for (const [input, children] of node.edges.attachments) {\n attachments[input] = children.map(nodeToJson);\n }\n\n return {\n id: node.spec.id,\n plugin: node.spec.plugin.pluginId,\n disabled: node.spec.disabled || undefined,\n ...(Object.keys(attachments).length > 0 ? { attachments } : {}),\n };\n}\n\nexport function CopyTreeButton() {\n const appTreeApi = useApi(appTreeApiRef);\n const [copied, setCopied] = useState(false);\n\n const handlePress = () => {\n const { tree } = appTreeApi.getTree();\n const json = JSON.stringify(nodeToJson(tree.root), null, 2);\n window.navigator.clipboard.writeText(json).then(() => {\n setCopied(true);\n setTimeout(() => setCopied(false), 2000);\n });\n };\n\n return (\n <Button\n variant=\"secondary\"\n size=\"small\"\n iconStart={copied ? <RiCheckLine /> : <RiFileCopyLine />}\n onPress={handlePress}\n >\n {copied ? 'Copied!' : 'Copy as JSON'}\n </Button>\n );\n}\n"],"names":[],"mappings":";;;;;;AAyBA,SAAS,WAAW,IAAA,EAAuB;AACzC,EAAA,MAAM,cAAwC,EAAC;AAC/C,EAAA,KAAA,MAAW,CAAC,KAAA,EAAO,QAAQ,CAAA,IAAK,IAAA,CAAK,MAAM,WAAA,EAAa;AACtD,IAAA,WAAA,CAAY,KAAK,CAAA,GAAI,QAAA,CAAS,GAAA,CAAI,UAAU,CAAA;AAAA,EAC9C;AAEA,EAAA,OAAO;AAAA,IACL,EAAA,EAAI,KAAK,IAAA,CAAK,EAAA;AAAA,IACd,MAAA,EAAQ,IAAA,CAAK,IAAA,CAAK,MAAA,CAAO,QAAA;AAAA,IACzB,QAAA,EAAU,IAAA,CAAK,IAAA,CAAK,QAAA,IAAY,MAAA;AAAA,IAChC,GAAI,MAAA,CAAO,IAAA,CAAK,WAAW,CAAA,CAAE,SAAS,CAAA,GAAI,EAAE,WAAA,EAAY,GAAI;AAAC,GAC/D;AACF;AAEO,SAAS,cAAA,GAAiB;AAC/B,EAAA,MAAM,UAAA,GAAa,OAAO,aAAa,CAAA;AACvC,EAAA,MAAM,CAAC,MAAA,EAAQ,SAAS,CAAA,GAAI,SAAS,KAAK,CAAA;AAE1C,EAAA,MAAM,cAAc,MAAM;AACxB,IAAA,MAAM,EAAE,IAAA,EAAK,GAAI,UAAA,CAAW,OAAA,EAAQ;AACpC,IAAA,MAAM,IAAA,GAAO,KAAK,SAAA,CAAU,UAAA,CAAW,KAAK,IAAI,CAAA,EAAG,MAAM,CAAC,CAAA;AAC1D,IAAA,MAAA,CAAO,UAAU,SAAA,CAAU,SAAA,CAAU,IAAI,CAAA,CAAE,KAAK,MAAM;AACpD,MAAA,SAAA,CAAU,IAAI,CAAA;AACd,MAAA,UAAA,CAAW,MAAM,SAAA,CAAU,KAAK,CAAA,EAAG,GAAI,CAAA;AAAA,IACzC,CAAC,CAAA;AAAA,EACH,CAAA;AAEA,EAAA,uBACE,GAAA;AAAA,IAAC,MAAA;AAAA,IAAA;AAAA,MACC,OAAA,EAAQ,WAAA;AAAA,MACR,IAAA,EAAK,OAAA;AAAA,MACL,WAAW,MAAA,mBAAS,GAAA,CAAC,WAAA,EAAA,EAAY,CAAA,uBAAM,cAAA,EAAA,EAAe,CAAA;AAAA,MACtD,OAAA,EAAS,WAAA;AAAA,MAER,mBAAS,SAAA,GAAY;AAAA;AAAA,GACxB;AAEJ;;;;"}
|
package/dist/index.d.ts
CHANGED
|
@@ -25,17 +25,126 @@ declare const visualizerPlugin: _backstage_frontend_plugin_api.OverridableFronte
|
|
|
25
25
|
name: undefined;
|
|
26
26
|
config: {
|
|
27
27
|
path: string | undefined;
|
|
28
|
+
title: string | undefined;
|
|
28
29
|
};
|
|
29
30
|
configInput: {
|
|
31
|
+
title?: string | undefined;
|
|
30
32
|
path?: string | undefined;
|
|
31
33
|
};
|
|
32
|
-
output: _backstage_frontend_plugin_api.ExtensionDataRef<string, "core.routing.path", {}> | _backstage_frontend_plugin_api.ExtensionDataRef<
|
|
34
|
+
output: _backstage_frontend_plugin_api.ExtensionDataRef<string, "core.routing.path", {}> | _backstage_frontend_plugin_api.ExtensionDataRef<_backstage_frontend_plugin_api.RouteRef<_backstage_frontend_plugin_api.AnyRouteRefParams>, "core.routing.ref", {
|
|
35
|
+
optional: true;
|
|
36
|
+
}> | _backstage_frontend_plugin_api.ExtensionDataRef<react.JSX.Element, "core.reactElement", {}> | _backstage_frontend_plugin_api.ExtensionDataRef<string, "core.title", {
|
|
37
|
+
optional: true;
|
|
38
|
+
}> | _backstage_frontend_plugin_api.ExtensionDataRef<_backstage_frontend_plugin_api.IconElement, "core.icon", {
|
|
33
39
|
optional: true;
|
|
34
40
|
}>;
|
|
35
|
-
inputs: {
|
|
41
|
+
inputs: {
|
|
42
|
+
pages: _backstage_frontend_plugin_api.ExtensionInput<_backstage_frontend_plugin_api.ConfigurableExtensionDataRef<react.JSX.Element, "core.reactElement", {}> | _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<string, "core.routing.path", {}> | _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<_backstage_frontend_plugin_api.RouteRef<_backstage_frontend_plugin_api.AnyRouteRefParams>, "core.routing.ref", {
|
|
43
|
+
optional: true;
|
|
44
|
+
}> | _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<string, "core.title", {
|
|
45
|
+
optional: true;
|
|
46
|
+
}> | _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<_backstage_frontend_plugin_api.IconElement, "core.icon", {
|
|
47
|
+
optional: true;
|
|
48
|
+
}>, {
|
|
49
|
+
singleton: false;
|
|
50
|
+
optional: false;
|
|
51
|
+
internal: false;
|
|
52
|
+
}>;
|
|
53
|
+
};
|
|
36
54
|
params: {
|
|
37
55
|
defaultPath?: [Error: `Use the 'path' param instead`];
|
|
38
56
|
path: string;
|
|
57
|
+
title?: string;
|
|
58
|
+
icon?: _backstage_frontend_plugin_api.IconElement;
|
|
59
|
+
loader?: () => Promise<react.JSX.Element>;
|
|
60
|
+
routeRef?: _backstage_frontend_plugin_api.RouteRef;
|
|
61
|
+
noHeader?: boolean;
|
|
62
|
+
};
|
|
63
|
+
}>;
|
|
64
|
+
"plugin-header-action:app-visualizer": _backstage_frontend_plugin_api.OverridableExtensionDefinition<{
|
|
65
|
+
kind: "plugin-header-action";
|
|
66
|
+
name: undefined;
|
|
67
|
+
config: {};
|
|
68
|
+
configInput: {};
|
|
69
|
+
output: _backstage_frontend_plugin_api.ExtensionDataRef<react.JSX.Element, "core.reactElement", {}>;
|
|
70
|
+
inputs: {};
|
|
71
|
+
params: (params: {
|
|
72
|
+
loader: () => Promise<JSX.Element>;
|
|
73
|
+
}) => _backstage_frontend_plugin_api.ExtensionBlueprintParams<{
|
|
74
|
+
loader: () => Promise<JSX.Element>;
|
|
75
|
+
}>;
|
|
76
|
+
}>;
|
|
77
|
+
"sub-page:app-visualizer/details": _backstage_frontend_plugin_api.OverridableExtensionDefinition<{
|
|
78
|
+
kind: "sub-page";
|
|
79
|
+
name: "details";
|
|
80
|
+
config: {
|
|
81
|
+
path: string | undefined;
|
|
82
|
+
title: string | undefined;
|
|
83
|
+
};
|
|
84
|
+
configInput: {
|
|
85
|
+
title?: string | undefined;
|
|
86
|
+
path?: string | undefined;
|
|
87
|
+
};
|
|
88
|
+
output: _backstage_frontend_plugin_api.ExtensionDataRef<string, "core.routing.path", {}> | _backstage_frontend_plugin_api.ExtensionDataRef<_backstage_frontend_plugin_api.RouteRef<_backstage_frontend_plugin_api.AnyRouteRefParams>, "core.routing.ref", {
|
|
89
|
+
optional: true;
|
|
90
|
+
}> | _backstage_frontend_plugin_api.ExtensionDataRef<react.JSX.Element, "core.reactElement", {}> | _backstage_frontend_plugin_api.ExtensionDataRef<string, "core.title", {}> | _backstage_frontend_plugin_api.ExtensionDataRef<_backstage_frontend_plugin_api.IconElement, "core.icon", {
|
|
91
|
+
optional: true;
|
|
92
|
+
}>;
|
|
93
|
+
inputs: {};
|
|
94
|
+
params: {
|
|
95
|
+
path: string;
|
|
96
|
+
title: string;
|
|
97
|
+
icon?: _backstage_frontend_plugin_api.IconElement;
|
|
98
|
+
loader: () => Promise<JSX.Element>;
|
|
99
|
+
routeRef?: _backstage_frontend_plugin_api.RouteRef;
|
|
100
|
+
};
|
|
101
|
+
}>;
|
|
102
|
+
"sub-page:app-visualizer/text": _backstage_frontend_plugin_api.OverridableExtensionDefinition<{
|
|
103
|
+
kind: "sub-page";
|
|
104
|
+
name: "text";
|
|
105
|
+
config: {
|
|
106
|
+
path: string | undefined;
|
|
107
|
+
title: string | undefined;
|
|
108
|
+
};
|
|
109
|
+
configInput: {
|
|
110
|
+
title?: string | undefined;
|
|
111
|
+
path?: string | undefined;
|
|
112
|
+
};
|
|
113
|
+
output: _backstage_frontend_plugin_api.ExtensionDataRef<string, "core.routing.path", {}> | _backstage_frontend_plugin_api.ExtensionDataRef<_backstage_frontend_plugin_api.RouteRef<_backstage_frontend_plugin_api.AnyRouteRefParams>, "core.routing.ref", {
|
|
114
|
+
optional: true;
|
|
115
|
+
}> | _backstage_frontend_plugin_api.ExtensionDataRef<react.JSX.Element, "core.reactElement", {}> | _backstage_frontend_plugin_api.ExtensionDataRef<string, "core.title", {}> | _backstage_frontend_plugin_api.ExtensionDataRef<_backstage_frontend_plugin_api.IconElement, "core.icon", {
|
|
116
|
+
optional: true;
|
|
117
|
+
}>;
|
|
118
|
+
inputs: {};
|
|
119
|
+
params: {
|
|
120
|
+
path: string;
|
|
121
|
+
title: string;
|
|
122
|
+
icon?: _backstage_frontend_plugin_api.IconElement;
|
|
123
|
+
loader: () => Promise<JSX.Element>;
|
|
124
|
+
routeRef?: _backstage_frontend_plugin_api.RouteRef;
|
|
125
|
+
};
|
|
126
|
+
}>;
|
|
127
|
+
"sub-page:app-visualizer/tree": _backstage_frontend_plugin_api.OverridableExtensionDefinition<{
|
|
128
|
+
kind: "sub-page";
|
|
129
|
+
name: "tree";
|
|
130
|
+
config: {
|
|
131
|
+
path: string | undefined;
|
|
132
|
+
title: string | undefined;
|
|
133
|
+
};
|
|
134
|
+
configInput: {
|
|
135
|
+
title?: string | undefined;
|
|
136
|
+
path?: string | undefined;
|
|
137
|
+
};
|
|
138
|
+
output: _backstage_frontend_plugin_api.ExtensionDataRef<string, "core.routing.path", {}> | _backstage_frontend_plugin_api.ExtensionDataRef<_backstage_frontend_plugin_api.RouteRef<_backstage_frontend_plugin_api.AnyRouteRefParams>, "core.routing.ref", {
|
|
139
|
+
optional: true;
|
|
140
|
+
}> | _backstage_frontend_plugin_api.ExtensionDataRef<react.JSX.Element, "core.reactElement", {}> | _backstage_frontend_plugin_api.ExtensionDataRef<string, "core.title", {}> | _backstage_frontend_plugin_api.ExtensionDataRef<_backstage_frontend_plugin_api.IconElement, "core.icon", {
|
|
141
|
+
optional: true;
|
|
142
|
+
}>;
|
|
143
|
+
inputs: {};
|
|
144
|
+
params: {
|
|
145
|
+
path: string;
|
|
146
|
+
title: string;
|
|
147
|
+
icon?: _backstage_frontend_plugin_api.IconElement;
|
|
39
148
|
loader: () => Promise<JSX.Element>;
|
|
40
149
|
routeRef?: _backstage_frontend_plugin_api.RouteRef;
|
|
41
150
|
};
|
package/dist/package.json.esm.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
var name = "@backstage/plugin-app-visualizer";
|
|
2
|
-
var version = "0.
|
|
2
|
+
var version = "0.2.0";
|
|
3
3
|
var description = "Visualizes the Backstage app structure";
|
|
4
4
|
var backstage = {
|
|
5
5
|
role: "frontend-plugin",
|
|
@@ -46,13 +46,13 @@ var devDependencies = {
|
|
|
46
46
|
"@types/react": "^18.0.0",
|
|
47
47
|
react: "^18.0.2",
|
|
48
48
|
"react-dom": "^18.0.2",
|
|
49
|
-
"react-router-dom": "^6.
|
|
49
|
+
"react-router-dom": "^6.30.2"
|
|
50
50
|
};
|
|
51
51
|
var peerDependencies = {
|
|
52
52
|
"@types/react": "^17.0.0 || ^18.0.0",
|
|
53
53
|
react: "^17.0.0 || ^18.0.0",
|
|
54
54
|
"react-dom": "^17.0.0 || ^18.0.0",
|
|
55
|
-
"react-router-dom": "^6.
|
|
55
|
+
"react-router-dom": "^6.30.2"
|
|
56
56
|
};
|
|
57
57
|
var peerDependenciesMeta = {
|
|
58
58
|
"@types/react": {
|
package/dist/plugin.esm.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx } from 'react/jsx-runtime';
|
|
2
|
-
import { createRouteRef, PageBlueprint, NavItemBlueprint, createFrontendPlugin } from '@backstage/frontend-plugin-api';
|
|
2
|
+
import { createRouteRef, PageBlueprint, SubPageBlueprint, PluginHeaderActionBlueprint, NavItemBlueprint, createFrontendPlugin } from '@backstage/frontend-plugin-api';
|
|
3
3
|
import { RiEyeLine } from '@remixicon/react';
|
|
4
4
|
|
|
5
5
|
const rootRouteRef = createRouteRef();
|
|
@@ -7,9 +7,44 @@ const appVisualizerPage = PageBlueprint.make({
|
|
|
7
7
|
params: {
|
|
8
8
|
path: "/visualizer",
|
|
9
9
|
routeRef: rootRouteRef,
|
|
10
|
-
|
|
10
|
+
title: "Visualizer"
|
|
11
11
|
}
|
|
12
12
|
});
|
|
13
|
+
const treeRouteRef = createRouteRef();
|
|
14
|
+
const detailedRouteRef = createRouteRef();
|
|
15
|
+
const textRouteRef = createRouteRef();
|
|
16
|
+
const appVisualizerTreePage = SubPageBlueprint.make({
|
|
17
|
+
name: "tree",
|
|
18
|
+
params: {
|
|
19
|
+
path: "tree",
|
|
20
|
+
routeRef: treeRouteRef,
|
|
21
|
+
title: "Tree",
|
|
22
|
+
loader: () => import('./components/AppVisualizerPage/TreeVisualizer.esm.js').then((m) => /* @__PURE__ */ jsx(m.TreeVisualizer, {}))
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
const appVisualizerDetailedPage = SubPageBlueprint.make({
|
|
26
|
+
name: "details",
|
|
27
|
+
params: {
|
|
28
|
+
path: "details",
|
|
29
|
+
routeRef: detailedRouteRef,
|
|
30
|
+
title: "Detailed",
|
|
31
|
+
loader: () => import('./components/AppVisualizerPage/DetailedVisualizer.esm.js').then((m) => /* @__PURE__ */ jsx(m.DetailedVisualizer, {}))
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
const appVisualizerTextPage = SubPageBlueprint.make({
|
|
35
|
+
name: "text",
|
|
36
|
+
params: {
|
|
37
|
+
path: "text",
|
|
38
|
+
routeRef: textRouteRef,
|
|
39
|
+
title: "Text",
|
|
40
|
+
loader: () => import('./components/AppVisualizerPage/TextVisualizer.esm.js').then((m) => /* @__PURE__ */ jsx(m.TextVisualizer, {}))
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
const copyTreeAsJson = PluginHeaderActionBlueprint.make({
|
|
44
|
+
params: (defineParams) => defineParams({
|
|
45
|
+
loader: () => import('./components/CopyTreeButton.esm.js').then((m) => /* @__PURE__ */ jsx(m.CopyTreeButton, {}))
|
|
46
|
+
})
|
|
47
|
+
});
|
|
13
48
|
const appVisualizerNavItem = NavItemBlueprint.make({
|
|
14
49
|
params: {
|
|
15
50
|
title: "Visualizer",
|
|
@@ -19,8 +54,17 @@ const appVisualizerNavItem = NavItemBlueprint.make({
|
|
|
19
54
|
});
|
|
20
55
|
const visualizerPlugin = createFrontendPlugin({
|
|
21
56
|
pluginId: "app-visualizer",
|
|
57
|
+
title: "App Visualizer",
|
|
58
|
+
icon: /* @__PURE__ */ jsx(RiEyeLine, {}),
|
|
22
59
|
info: { packageJson: () => import('./package.json.esm.js') },
|
|
23
|
-
extensions: [
|
|
60
|
+
extensions: [
|
|
61
|
+
appVisualizerPage,
|
|
62
|
+
appVisualizerTreePage,
|
|
63
|
+
appVisualizerDetailedPage,
|
|
64
|
+
appVisualizerTextPage,
|
|
65
|
+
appVisualizerNavItem,
|
|
66
|
+
copyTreeAsJson
|
|
67
|
+
]
|
|
24
68
|
});
|
|
25
69
|
|
|
26
70
|
export { appVisualizerNavItem, visualizerPlugin };
|
package/dist/plugin.esm.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.esm.js","sources":["../src/plugin.tsx"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n createFrontendPlugin,\n createRouteRef,\n NavItemBlueprint,\n PageBlueprint,\n} from '@backstage/frontend-plugin-api';\nimport { RiEyeLine
|
|
1
|
+
{"version":3,"file":"plugin.esm.js","sources":["../src/plugin.tsx"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n createFrontendPlugin,\n createRouteRef,\n NavItemBlueprint,\n PageBlueprint,\n PluginHeaderActionBlueprint,\n SubPageBlueprint,\n} from '@backstage/frontend-plugin-api';\nimport { RiEyeLine } from '@remixicon/react';\n\nconst rootRouteRef = createRouteRef();\n\nconst appVisualizerPage = PageBlueprint.make({\n params: {\n path: '/visualizer',\n routeRef: rootRouteRef,\n title: 'Visualizer',\n },\n});\n\nconst treeRouteRef = createRouteRef();\nconst detailedRouteRef = createRouteRef();\nconst textRouteRef = createRouteRef();\n\nconst appVisualizerTreePage = SubPageBlueprint.make({\n name: 'tree',\n params: {\n path: 'tree',\n routeRef: treeRouteRef,\n title: 'Tree',\n loader: () =>\n import('./components/AppVisualizerPage/TreeVisualizer').then(m => (\n <m.TreeVisualizer />\n )),\n },\n});\nconst appVisualizerDetailedPage = SubPageBlueprint.make({\n name: 'details',\n params: {\n path: 'details',\n routeRef: detailedRouteRef,\n title: 'Detailed',\n loader: () =>\n import('./components/AppVisualizerPage/DetailedVisualizer').then(m => (\n <m.DetailedVisualizer />\n )),\n },\n});\nconst appVisualizerTextPage = SubPageBlueprint.make({\n name: 'text',\n params: {\n path: 'text',\n routeRef: textRouteRef,\n title: 'Text',\n loader: () =>\n import('./components/AppVisualizerPage/TextVisualizer').then(m => (\n <m.TextVisualizer />\n )),\n },\n});\n\nconst copyTreeAsJson = PluginHeaderActionBlueprint.make({\n params: defineParams =>\n defineParams({\n loader: () =>\n import('./components/CopyTreeButton').then(m => <m.CopyTreeButton />),\n }),\n});\n\nexport const appVisualizerNavItem = NavItemBlueprint.make({\n params: {\n title: 'Visualizer',\n icon: () => <RiEyeLine />,\n routeRef: rootRouteRef,\n },\n});\n\n/** @public */\nexport const visualizerPlugin = createFrontendPlugin({\n pluginId: 'app-visualizer',\n title: 'App Visualizer',\n icon: <RiEyeLine />,\n info: { packageJson: () => import('../package.json') },\n extensions: [\n appVisualizerPage,\n appVisualizerTreePage,\n appVisualizerDetailedPage,\n appVisualizerTextPage,\n appVisualizerNavItem,\n copyTreeAsJson,\n ],\n});\n"],"names":[],"mappings":";;;;AA0BA,MAAM,eAAe,cAAA,EAAe;AAEpC,MAAM,iBAAA,GAAoB,cAAc,IAAA,CAAK;AAAA,EAC3C,MAAA,EAAQ;AAAA,IACN,IAAA,EAAM,aAAA;AAAA,IACN,QAAA,EAAU,YAAA;AAAA,IACV,KAAA,EAAO;AAAA;AAEX,CAAC,CAAA;AAED,MAAM,eAAe,cAAA,EAAe;AACpC,MAAM,mBAAmB,cAAA,EAAe;AACxC,MAAM,eAAe,cAAA,EAAe;AAEpC,MAAM,qBAAA,GAAwB,iBAAiB,IAAA,CAAK;AAAA,EAClD,IAAA,EAAM,MAAA;AAAA,EACN,MAAA,EAAQ;AAAA,IACN,IAAA,EAAM,MAAA;AAAA,IACN,QAAA,EAAU,YAAA;AAAA,IACV,KAAA,EAAO,MAAA;AAAA,IACP,MAAA,EAAQ,MACN,OAAO,sDAA+C,CAAA,CAAE,IAAA,CAAK,CAAA,CAAA,qBAC3D,GAAA,CAAC,CAAA,CAAE,cAAA,EAAF,EAAiB,CACnB;AAAA;AAEP,CAAC,CAAA;AACD,MAAM,yBAAA,GAA4B,iBAAiB,IAAA,CAAK;AAAA,EACtD,IAAA,EAAM,SAAA;AAAA,EACN,MAAA,EAAQ;AAAA,IACN,IAAA,EAAM,SAAA;AAAA,IACN,QAAA,EAAU,gBAAA;AAAA,IACV,KAAA,EAAO,UAAA;AAAA,IACP,MAAA,EAAQ,MACN,OAAO,0DAAmD,CAAA,CAAE,IAAA,CAAK,CAAA,CAAA,qBAC/D,GAAA,CAAC,CAAA,CAAE,kBAAA,EAAF,EAAqB,CACvB;AAAA;AAEP,CAAC,CAAA;AACD,MAAM,qBAAA,GAAwB,iBAAiB,IAAA,CAAK;AAAA,EAClD,IAAA,EAAM,MAAA;AAAA,EACN,MAAA,EAAQ;AAAA,IACN,IAAA,EAAM,MAAA;AAAA,IACN,QAAA,EAAU,YAAA;AAAA,IACV,KAAA,EAAO,MAAA;AAAA,IACP,MAAA,EAAQ,MACN,OAAO,sDAA+C,CAAA,CAAE,IAAA,CAAK,CAAA,CAAA,qBAC3D,GAAA,CAAC,CAAA,CAAE,cAAA,EAAF,EAAiB,CACnB;AAAA;AAEP,CAAC,CAAA;AAED,MAAM,cAAA,GAAiB,4BAA4B,IAAA,CAAK;AAAA,EACtD,MAAA,EAAQ,kBACN,YAAA,CAAa;AAAA,IACX,MAAA,EAAQ,MACN,OAAO,oCAA6B,CAAA,CAAE,IAAA,CAAK,CAAA,CAAA,qBAAK,GAAA,CAAC,CAAA,CAAE,cAAA,EAAF,EAAiB,CAAE;AAAA,GACvE;AACL,CAAC,CAAA;AAEM,MAAM,oBAAA,GAAuB,iBAAiB,IAAA,CAAK;AAAA,EACxD,MAAA,EAAQ;AAAA,IACN,KAAA,EAAO,YAAA;AAAA,IACP,IAAA,EAAM,sBAAM,GAAA,CAAC,SAAA,EAAA,EAAU,CAAA;AAAA,IACvB,QAAA,EAAU;AAAA;AAEd,CAAC;AAGM,MAAM,mBAAmB,oBAAA,CAAqB;AAAA,EACnD,QAAA,EAAU,gBAAA;AAAA,EACV,KAAA,EAAO,gBAAA;AAAA,EACP,IAAA,sBAAO,SAAA,EAAA,EAAU,CAAA;AAAA,EACjB,MAAM,EAAE,WAAA,EAAa,MAAM,OAAO,uBAAiB,CAAA,EAAE;AAAA,EACrD,UAAA,EAAY;AAAA,IACV,iBAAA;AAAA,IACA,qBAAA;AAAA,IACA,yBAAA;AAAA,IACA,qBAAA;AAAA,IACA,oBAAA;AAAA,IACA;AAAA;AAEJ,CAAC;;;;"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@backstage/plugin-app-visualizer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Visualizes the Backstage app structure",
|
|
5
5
|
"backstage": {
|
|
6
6
|
"role": "frontend-plugin",
|
|
@@ -37,26 +37,26 @@
|
|
|
37
37
|
"test": "backstage-cli package test"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@backstage/core-components": "0.18.
|
|
41
|
-
"@backstage/core-plugin-api": "1.12.
|
|
42
|
-
"@backstage/frontend-plugin-api": "0.14.0
|
|
43
|
-
"@backstage/ui": "0.12.0
|
|
40
|
+
"@backstage/core-components": "^0.18.7",
|
|
41
|
+
"@backstage/core-plugin-api": "^1.12.3",
|
|
42
|
+
"@backstage/frontend-plugin-api": "^0.14.0",
|
|
43
|
+
"@backstage/ui": "^0.12.0",
|
|
44
44
|
"@remixicon/react": "^4.6.0",
|
|
45
45
|
"react-aria-components": "^1.14.0"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
|
-
"@backstage/cli": "0.35.
|
|
49
|
-
"@backstage/frontend-defaults": "0.
|
|
48
|
+
"@backstage/cli": "^0.35.4",
|
|
49
|
+
"@backstage/frontend-defaults": "^0.4.0",
|
|
50
50
|
"@types/react": "^18.0.0",
|
|
51
51
|
"react": "^18.0.2",
|
|
52
52
|
"react-dom": "^18.0.2",
|
|
53
|
-
"react-router-dom": "^6.
|
|
53
|
+
"react-router-dom": "^6.30.2"
|
|
54
54
|
},
|
|
55
55
|
"peerDependencies": {
|
|
56
56
|
"@types/react": "^17.0.0 || ^18.0.0",
|
|
57
57
|
"react": "^17.0.0 || ^18.0.0",
|
|
58
58
|
"react-dom": "^17.0.0 || ^18.0.0",
|
|
59
|
-
"react-router-dom": "^6.
|
|
59
|
+
"react-router-dom": "^6.30.2"
|
|
60
60
|
},
|
|
61
61
|
"peerDependenciesMeta": {
|
|
62
62
|
"@types/react": {
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
2
|
-
import { Page, Header, Content, HeaderTabs } from '@backstage/core-components';
|
|
3
|
-
import { useApi } from '@backstage/core-plugin-api';
|
|
4
|
-
import { appTreeApiRef } from '@backstage/frontend-plugin-api';
|
|
5
|
-
import { Flex } from '@backstage/ui';
|
|
6
|
-
import { useMemo, useCallback, useEffect } from 'react';
|
|
7
|
-
import { DetailedVisualizer } from './DetailedVisualizer.esm.js';
|
|
8
|
-
import { TextVisualizer } from './TextVisualizer.esm.js';
|
|
9
|
-
import { TreeVisualizer } from './TreeVisualizer.esm.js';
|
|
10
|
-
import { useLocation, useRoutes, useParams, matchRoutes, useNavigate } from 'react-router-dom';
|
|
11
|
-
|
|
12
|
-
function AppVisualizerPage() {
|
|
13
|
-
const appTreeApi = useApi(appTreeApiRef);
|
|
14
|
-
const { tree } = appTreeApi.getTree();
|
|
15
|
-
const tabs = useMemo(
|
|
16
|
-
() => [
|
|
17
|
-
{
|
|
18
|
-
id: "tree",
|
|
19
|
-
path: "tree",
|
|
20
|
-
label: "Tree",
|
|
21
|
-
element: /* @__PURE__ */ jsx(TreeVisualizer, { tree })
|
|
22
|
-
},
|
|
23
|
-
{
|
|
24
|
-
id: "detailed",
|
|
25
|
-
path: "detailed",
|
|
26
|
-
label: "Detailed",
|
|
27
|
-
element: /* @__PURE__ */ jsx(DetailedVisualizer, { tree })
|
|
28
|
-
},
|
|
29
|
-
{
|
|
30
|
-
id: "text",
|
|
31
|
-
path: "text",
|
|
32
|
-
label: "Text",
|
|
33
|
-
element: /* @__PURE__ */ jsx(TextVisualizer, { tree })
|
|
34
|
-
}
|
|
35
|
-
],
|
|
36
|
-
[tree]
|
|
37
|
-
);
|
|
38
|
-
const location = useLocation();
|
|
39
|
-
const element = useRoutes(tabs, location);
|
|
40
|
-
const currentPath = `/${useParams()["*"]}`;
|
|
41
|
-
const [matchedRoute] = matchRoutes(tabs, currentPath) ?? [];
|
|
42
|
-
const currentTabIndex = matchedRoute ? tabs.findIndex((t) => t.path === matchedRoute.route.path) : 0;
|
|
43
|
-
const navigate = useNavigate();
|
|
44
|
-
const handleTabChange = useCallback(
|
|
45
|
-
(index) => {
|
|
46
|
-
navigate(tabs[index].id);
|
|
47
|
-
},
|
|
48
|
-
[navigate, tabs]
|
|
49
|
-
);
|
|
50
|
-
useEffect(() => {
|
|
51
|
-
if (!element) {
|
|
52
|
-
navigate(tabs[0].path);
|
|
53
|
-
}
|
|
54
|
-
}, [element, navigate, tabs]);
|
|
55
|
-
return /* @__PURE__ */ jsxs(Page, { themeId: "tool", children: [
|
|
56
|
-
/* @__PURE__ */ jsx(Header, { title: "App Visualizer" }),
|
|
57
|
-
/* @__PURE__ */ jsx(Content, { noPadding: true, stretch: true, children: /* @__PURE__ */ jsxs(Flex, { direction: "column", style: { height: "100%" }, children: [
|
|
58
|
-
/* @__PURE__ */ jsx(
|
|
59
|
-
HeaderTabs,
|
|
60
|
-
{
|
|
61
|
-
tabs,
|
|
62
|
-
selectedIndex: currentTabIndex,
|
|
63
|
-
onChange: handleTabChange
|
|
64
|
-
}
|
|
65
|
-
),
|
|
66
|
-
element
|
|
67
|
-
] }) })
|
|
68
|
-
] });
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
export { AppVisualizerPage };
|
|
72
|
-
//# sourceMappingURL=AppVisualizerPage.esm.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"AppVisualizerPage.esm.js","sources":["../../../src/components/AppVisualizerPage/AppVisualizerPage.tsx"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Content, Header, HeaderTabs, Page } from '@backstage/core-components';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { appTreeApiRef } from '@backstage/frontend-plugin-api';\nimport { Flex } from '@backstage/ui';\nimport { useCallback, useEffect, useMemo } from 'react';\nimport { DetailedVisualizer } from './DetailedVisualizer';\nimport { TextVisualizer } from './TextVisualizer';\nimport { TreeVisualizer } from './TreeVisualizer';\nimport {\n matchRoutes,\n useLocation,\n useNavigate,\n useParams,\n useRoutes,\n} from 'react-router-dom';\n\nexport function AppVisualizerPage() {\n const appTreeApi = useApi(appTreeApiRef);\n const { tree } = appTreeApi.getTree();\n\n const tabs = useMemo(\n () => [\n {\n id: 'tree',\n path: 'tree',\n label: 'Tree',\n element: <TreeVisualizer tree={tree} />,\n },\n {\n id: 'detailed',\n path: 'detailed',\n label: 'Detailed',\n element: <DetailedVisualizer tree={tree} />,\n },\n {\n id: 'text',\n path: 'text',\n label: 'Text',\n element: <TextVisualizer tree={tree} />,\n },\n ],\n [tree],\n );\n\n const location = useLocation();\n const element = useRoutes(tabs, location);\n\n const currentPath = `/${useParams()['*']}`;\n const [matchedRoute] = matchRoutes(tabs, currentPath) ?? [];\n\n const currentTabIndex = matchedRoute\n ? tabs.findIndex(t => t.path === matchedRoute.route.path)\n : 0;\n\n const navigate = useNavigate();\n const handleTabChange = useCallback(\n (index: number) => {\n navigate(tabs[index].id);\n },\n [navigate, tabs],\n );\n\n useEffect(() => {\n if (!element) {\n navigate(tabs[0].path);\n }\n }, [element, navigate, tabs]);\n\n return (\n <Page themeId=\"tool\">\n <Header title=\"App Visualizer\" />\n <Content noPadding stretch>\n <Flex direction=\"column\" style={{ height: '100%' }}>\n <HeaderTabs\n tabs={tabs}\n selectedIndex={currentTabIndex}\n onChange={handleTabChange}\n />\n {element}\n </Flex>\n </Content>\n </Page>\n );\n}\n"],"names":[],"mappings":";;;;;;;;;;;AAgCO,SAAS,iBAAA,GAAoB;AAClC,EAAA,MAAM,UAAA,GAAa,OAAO,aAAa,CAAA;AACvC,EAAA,MAAM,EAAE,IAAA,EAAK,GAAI,UAAA,CAAW,OAAA,EAAQ;AAEpC,EAAA,MAAM,IAAA,GAAO,OAAA;AAAA,IACX,MAAM;AAAA,MACJ;AAAA,QACE,EAAA,EAAI,MAAA;AAAA,QACJ,IAAA,EAAM,MAAA;AAAA,QACN,KAAA,EAAO,MAAA;AAAA,QACP,OAAA,kBAAS,GAAA,CAAC,cAAA,EAAA,EAAe,IAAA,EAAY;AAAA,OACvC;AAAA,MACA;AAAA,QACE,EAAA,EAAI,UAAA;AAAA,QACJ,IAAA,EAAM,UAAA;AAAA,QACN,KAAA,EAAO,UAAA;AAAA,QACP,OAAA,kBAAS,GAAA,CAAC,kBAAA,EAAA,EAAmB,IAAA,EAAY;AAAA,OAC3C;AAAA,MACA;AAAA,QACE,EAAA,EAAI,MAAA;AAAA,QACJ,IAAA,EAAM,MAAA;AAAA,QACN,KAAA,EAAO,MAAA;AAAA,QACP,OAAA,kBAAS,GAAA,CAAC,cAAA,EAAA,EAAe,IAAA,EAAY;AAAA;AACvC,KACF;AAAA,IACA,CAAC,IAAI;AAAA,GACP;AAEA,EAAA,MAAM,WAAW,WAAA,EAAY;AAC7B,EAAA,MAAM,OAAA,GAAU,SAAA,CAAU,IAAA,EAAM,QAAQ,CAAA;AAExC,EAAA,MAAM,WAAA,GAAc,CAAA,CAAA,EAAI,SAAA,EAAU,CAAE,GAAG,CAAC,CAAA,CAAA;AACxC,EAAA,MAAM,CAAC,YAAY,CAAA,GAAI,YAAY,IAAA,EAAM,WAAW,KAAK,EAAC;AAE1D,EAAA,MAAM,eAAA,GAAkB,YAAA,GACpB,IAAA,CAAK,SAAA,CAAU,CAAA,CAAA,KAAK,EAAE,IAAA,KAAS,YAAA,CAAa,KAAA,CAAM,IAAI,CAAA,GACtD,CAAA;AAEJ,EAAA,MAAM,WAAW,WAAA,EAAY;AAC7B,EAAA,MAAM,eAAA,GAAkB,WAAA;AAAA,IACtB,CAAC,KAAA,KAAkB;AACjB,MAAA,QAAA,CAAS,IAAA,CAAK,KAAK,CAAA,CAAE,EAAE,CAAA;AAAA,IACzB,CAAA;AAAA,IACA,CAAC,UAAU,IAAI;AAAA,GACjB;AAEA,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,OAAA,EAAS;AACZ,MAAA,QAAA,CAAS,IAAA,CAAK,CAAC,CAAA,CAAE,IAAI,CAAA;AAAA,IACvB;AAAA,EACF,CAAA,EAAG,CAAC,OAAA,EAAS,QAAA,EAAU,IAAI,CAAC,CAAA;AAE5B,EAAA,uBACE,IAAA,CAAC,IAAA,EAAA,EAAK,OAAA,EAAQ,MAAA,EACZ,QAAA,EAAA;AAAA,oBAAA,GAAA,CAAC,MAAA,EAAA,EAAO,OAAM,gBAAA,EAAiB,CAAA;AAAA,oBAC/B,GAAA,CAAC,OAAA,EAAA,EAAQ,SAAA,EAAS,IAAA,EAAC,SAAO,IAAA,EACxB,QAAA,kBAAA,IAAA,CAAC,IAAA,EAAA,EAAK,SAAA,EAAU,QAAA,EAAS,KAAA,EAAO,EAAE,MAAA,EAAQ,QAAO,EAC/C,QAAA,EAAA;AAAA,sBAAA,GAAA;AAAA,QAAC,UAAA;AAAA,QAAA;AAAA,UACC,IAAA;AAAA,UACA,aAAA,EAAe,eAAA;AAAA,UACf,QAAA,EAAU;AAAA;AAAA,OACZ;AAAA,MACC;AAAA,KAAA,EACH,CAAA,EACF;AAAA,GAAA,EACF,CAAA;AAEJ;;;;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
|