feat(graph): enable composite graph functionality (#27789)
This PR enables composite graph functionality:
- Experimental feature to enable Composite Graph
- In Composite Graph mode:
- Nodes are shown by default.
- Show/Hide All Projects function similarly to regular mode
- Focus a Composite Node renders the inner nodes with up-to 3 additional
containers: Green area contains external nodes that depend on the inner
nodes; Orange area contains external nodes that the inner nodes depend
depend on; Purple area contains external nodes with circular
dependencies with the inner nodes.
- Focused node can be unfocus/reset.
- Only one node can be focused at one given time. - Show All projects
while having a focused node will unfocus the node.
- Expand a Composite Node renders the inner nodes of the composite node
in-place (i.e: still keep the context of the current graph). Expanded
node can be collapsed to go back.
This commit is contained in:
parent
7e1cf531ca
commit
3c95965e7c
@ -25,15 +25,65 @@ export const compositeGraphStateConfig: ProjectGraphStateNodeConfig = {
|
||||
),
|
||||
],
|
||||
exit: [
|
||||
send(() => ({ type: 'notifyGraphDisableCompositeGraph' }), {
|
||||
to: (ctx) => ctx.graphActor,
|
||||
}),
|
||||
assign((ctx) => {
|
||||
ctx.compositeGraph.enabled = false;
|
||||
ctx.compositeGraph.context = undefined;
|
||||
}),
|
||||
send(
|
||||
(ctx) => ({
|
||||
type: 'notifyGraphUpdateGraph',
|
||||
projects: ctx.projects,
|
||||
dependencies: ctx.dependencies,
|
||||
fileMap: ctx.fileMap,
|
||||
affectedProjects: ctx.affectedProjects,
|
||||
workspaceLayout: ctx.workspaceLayout,
|
||||
groupByFolder: ctx.groupByFolder,
|
||||
selectedProjects: ctx.selectedProjects,
|
||||
composite: ctx.compositeGraph,
|
||||
}),
|
||||
{
|
||||
to: (ctx) => ctx.graphActor,
|
||||
}
|
||||
),
|
||||
],
|
||||
on: {
|
||||
selectAll: {
|
||||
actions: [
|
||||
assign((ctx, event) => {
|
||||
if (event.type !== 'selectAll') return;
|
||||
ctx.compositeGraph.enabled = true;
|
||||
ctx.compositeGraph.context = null;
|
||||
}),
|
||||
send((ctx) => ({
|
||||
type: 'enableCompositeGraph',
|
||||
context: ctx.compositeGraph.context,
|
||||
})),
|
||||
],
|
||||
},
|
||||
deselectAll: {
|
||||
actions: [
|
||||
assign((ctx, event) => {
|
||||
if (event.type !== 'deselectAll') return;
|
||||
ctx.compositeGraph.enabled = true;
|
||||
}),
|
||||
send(
|
||||
() => ({
|
||||
type: 'notifyGraphHideAllProjects',
|
||||
}),
|
||||
{ to: (context) => context.graphActor }
|
||||
),
|
||||
],
|
||||
},
|
||||
selectAffected: {
|
||||
actions: [
|
||||
send(
|
||||
() => ({
|
||||
type: 'notifyGraphShowAffectedProjects',
|
||||
}),
|
||||
{ to: (context) => context.graphActor }
|
||||
),
|
||||
],
|
||||
},
|
||||
focusProject: {
|
||||
actions: [
|
||||
assign((ctx, event) => {
|
||||
@ -112,6 +162,7 @@ export const compositeGraphStateConfig: ProjectGraphStateNodeConfig = {
|
||||
if (event.type !== 'enableCompositeGraph') return;
|
||||
ctx.compositeGraph.enabled = true;
|
||||
ctx.compositeGraph.context = event.context || undefined;
|
||||
ctx.focusedProject = null;
|
||||
}),
|
||||
send(
|
||||
(ctx, event) => ({
|
||||
|
||||
@ -8,7 +8,7 @@ export interface CompositeGraphPanelProps {
|
||||
export const CompositeGraphPanel = memo(
|
||||
({ compositeEnabled, compositeEnabledChanged }: CompositeGraphPanelProps) => {
|
||||
return (
|
||||
<div className="px-4">
|
||||
<div className="mt-4 px-4">
|
||||
<div className="flex items-start">
|
||||
<div className="flex h-5 items-center">
|
||||
<input
|
||||
@ -16,7 +16,7 @@ export const CompositeGraphPanel = memo(
|
||||
name="composite"
|
||||
value="composite"
|
||||
type="checkbox"
|
||||
className="h-4 w-4 accent-purple-500"
|
||||
className="h-4 w-4 accent-blue-500 dark:accent-sky-500"
|
||||
onChange={(event) =>
|
||||
compositeEnabledChanged(event.target.checked)
|
||||
}
|
||||
|
||||
@ -3,11 +3,15 @@ import { CheckboxPanel } from '../../ui-components/checkbox-panel';
|
||||
export interface DisplayOptionsPanelProps {
|
||||
groupByFolder: boolean;
|
||||
groupByFolderChanged: (checked: boolean) => void;
|
||||
disabled?: boolean;
|
||||
disabledDescription?: string;
|
||||
}
|
||||
|
||||
export const GroupByFolderPanel = ({
|
||||
groupByFolder,
|
||||
groupByFolderChanged,
|
||||
disabled,
|
||||
disabledDescription,
|
||||
}: DisplayOptionsPanelProps) => {
|
||||
return (
|
||||
<CheckboxPanel
|
||||
@ -16,6 +20,8 @@ export const GroupByFolderPanel = ({
|
||||
name={'groupByFolder'}
|
||||
label={'Group by folder'}
|
||||
description={'Visually arrange libraries by folders.'}
|
||||
disabled={disabled}
|
||||
disabledDescription={disabledDescription}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@ -27,6 +27,7 @@ import { getProjectGraphService } from '../machines/get-services';
|
||||
import { Link, useNavigate, useNavigation } from 'react-router-dom';
|
||||
import { useRouteConstructor } from '@nx/graph/shared';
|
||||
import { CompositeNode } from '../interfaces';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
interface SidebarProject {
|
||||
projectGraphNode: ProjectGraphProjectNode;
|
||||
@ -249,6 +250,10 @@ function CompositeNodeListItem({
|
||||
const routeConstructor = useRouteConstructor();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const label = compositeNode.parent
|
||||
? `${compositeNode.parent}/${compositeNode.label}`
|
||||
: compositeNode.label;
|
||||
|
||||
function toggleProject() {
|
||||
if (compositeNode.state !== 'hidden') {
|
||||
projectGraphService.send({
|
||||
@ -283,13 +288,8 @@ function CompositeNodeListItem({
|
||||
<div className="flex items-center">
|
||||
<Link
|
||||
to={routeConstructor(
|
||||
{
|
||||
pathname: `/projects`,
|
||||
search: `?composite=true&compositeContext=${encodeURIComponent(
|
||||
compositeNode.id
|
||||
)}`,
|
||||
},
|
||||
false
|
||||
{ pathname: `/projects`, search: `?composite=${compositeNode.id}` },
|
||||
true
|
||||
)}
|
||||
className="mr-1 flex items-center rounded-md border-slate-300 bg-white p-1 font-medium text-slate-500 shadow-sm ring-1 ring-slate-200 transition hover:bg-slate-50 dark:border-slate-600 dark:bg-slate-800 dark:text-slate-400 dark:ring-slate-600 hover:dark:bg-slate-700"
|
||||
title="Focus on this node"
|
||||
@ -313,11 +313,11 @@ function CompositeNodeListItem({
|
||||
<label
|
||||
className="ml-2 block w-full cursor-pointer truncate rounded-md p-2 font-mono font-normal transition hover:bg-slate-50 hover:dark:bg-slate-700"
|
||||
data-project={compositeNode.id}
|
||||
title={compositeNode.label}
|
||||
title={label}
|
||||
data-active={compositeNode.state !== 'hidden'}
|
||||
onClick={toggleProject}
|
||||
>
|
||||
{compositeNode.label}
|
||||
{label}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
@ -339,8 +339,6 @@ function CompositeNodeList({
|
||||
}: {
|
||||
compositeNodes: CompositeNode[];
|
||||
}) {
|
||||
const projectGraphService = getProjectGraphService();
|
||||
|
||||
if (compositeNodes.length === 0) {
|
||||
return <p>No composite nodes</p>;
|
||||
}
|
||||
|
||||
@ -7,6 +7,8 @@ import { useProjectGraphSelector } from './hooks/use-project-graph-selector';
|
||||
import { TracingAlgorithmType } from './machines/interfaces';
|
||||
import {
|
||||
collapseEdgesSelector,
|
||||
compositeContextSelector,
|
||||
compositeGraphEnabledSelector,
|
||||
focusedProjectNameSelector,
|
||||
getTracingInfo,
|
||||
groupByFolderSelector,
|
||||
@ -40,9 +42,13 @@ import {
|
||||
} from 'react-router-dom';
|
||||
import { useCurrentPath } from '../hooks/use-current-path';
|
||||
import { ProjectDetailsModal } from '../ui-components/project-details-modal';
|
||||
import { CompositeGraphPanel } from './panels/composite-graph-panel';
|
||||
import { CompositeContextPanel } from '../ui-components/composite-context-panel';
|
||||
import { getGraphService } from '../machines/graph.service';
|
||||
|
||||
export function ProjectsSidebar(): JSX.Element {
|
||||
const environmentConfig = useEnvironmentConfig();
|
||||
const graphService = getGraphService();
|
||||
const projectGraphService = getProjectGraphService();
|
||||
const focusedProject = useProjectGraphSelector(focusedProjectNameSelector);
|
||||
const searchDepthInfo = useProjectGraphSelector(searchDepthSelector);
|
||||
@ -53,6 +59,11 @@ export function ProjectsSidebar(): JSX.Element {
|
||||
);
|
||||
const groupByFolder = useProjectGraphSelector(groupByFolderSelector);
|
||||
const collapseEdges = useProjectGraphSelector(collapseEdgesSelector);
|
||||
const compositeEnabled = useProjectGraphSelector(
|
||||
compositeGraphEnabledSelector
|
||||
);
|
||||
|
||||
const compositeContext = useProjectGraphSelector(compositeContextSelector);
|
||||
|
||||
const isTracing = projectGraphService.getSnapshot().matches('tracing');
|
||||
const tracingInfo = useProjectGraphSelector(getTracingInfo);
|
||||
@ -75,17 +86,48 @@ export function ProjectsSidebar(): JSX.Element {
|
||||
navigate(routeConstructor('/projects', true));
|
||||
}
|
||||
|
||||
function resetCompositeContext() {
|
||||
projectGraphService.send({ type: 'enableCompositeGraph', context: null });
|
||||
navigate(
|
||||
routeConstructor(
|
||||
{ pathname: '/projects', search: '?composite=true' },
|
||||
true
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function showAllProjects() {
|
||||
navigate(routeConstructor('/projects/all', true));
|
||||
navigate(
|
||||
routeConstructor('/projects/all', (searchParams) => {
|
||||
if (searchParams.has('composite')) {
|
||||
searchParams.set('composite', 'true');
|
||||
}
|
||||
return searchParams;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
function hideAllProjects() {
|
||||
projectGraphService.send({ type: 'deselectAll' });
|
||||
navigate(routeConstructor('/projects', true));
|
||||
navigate(
|
||||
routeConstructor('/projects', (searchParams) => {
|
||||
if (searchParams.has('composite')) {
|
||||
searchParams.set('composite', 'true');
|
||||
}
|
||||
return searchParams;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
function showAffectedProjects() {
|
||||
navigate(routeConstructor('/projects/affected', true));
|
||||
navigate(
|
||||
routeConstructor('/projects/affected', (searchParams) => {
|
||||
if (searchParams.has('composite')) {
|
||||
searchParams.set('composite', 'true');
|
||||
}
|
||||
return searchParams;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
function searchDepthFilterEnabledChange(checked: boolean) {
|
||||
@ -126,6 +168,19 @@ export function ProjectsSidebar(): JSX.Element {
|
||||
});
|
||||
}
|
||||
|
||||
function compositeEnabledChanged(checked: boolean) {
|
||||
navigate(
|
||||
routeConstructor('/projects', (searchParams) => {
|
||||
if (checked) {
|
||||
searchParams.set('composite', 'true');
|
||||
} else {
|
||||
searchParams.delete('composite');
|
||||
}
|
||||
return searchParams;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
function incrementDepthFilter() {
|
||||
const newSearchDepth = searchDepthInfo.searchDepth + 1;
|
||||
setSearchParams((currentSearchParams) => {
|
||||
@ -182,6 +237,19 @@ export function ProjectsSidebar(): JSX.Element {
|
||||
});
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
return graphService.listen((event) => {
|
||||
if (event.type === 'CompositeNodeDblClick') {
|
||||
projectGraphService.send({
|
||||
type: event.data.expanded
|
||||
? 'collapseCompositeNode'
|
||||
: 'expandCompositeNode',
|
||||
id: event.id,
|
||||
});
|
||||
}
|
||||
});
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
projectGraphService.send({
|
||||
type: 'setProjects',
|
||||
@ -224,7 +292,7 @@ export function ProjectsSidebar(): JSX.Element {
|
||||
projectName: routeParams.endTrace,
|
||||
});
|
||||
}
|
||||
}, [routeParams]);
|
||||
}, [routeParams, compositeEnabled]);
|
||||
|
||||
useEffect(() => {
|
||||
if (searchParams.has('groupByFolder') && groupByFolder === false) {
|
||||
@ -251,6 +319,17 @@ export function ProjectsSidebar(): JSX.Element {
|
||||
});
|
||||
}
|
||||
|
||||
if (searchParams.has('composite')) {
|
||||
const compositeParam = searchParams.get('composite');
|
||||
projectGraphService.send({
|
||||
type: 'enableCompositeGraph',
|
||||
context: compositeParam === 'true' ? null : compositeParam,
|
||||
});
|
||||
} else if (!searchParams.has('composite')) {
|
||||
projectGraphService.send({ type: 'disableCompositeGraph' });
|
||||
navigate(routeConstructor('/projects', true));
|
||||
}
|
||||
|
||||
if (searchParams.has('searchDepth')) {
|
||||
const parsedValue = parseInt(searchParams.get('searchDepth'), 10);
|
||||
|
||||
@ -329,6 +408,13 @@ export function ProjectsSidebar(): JSX.Element {
|
||||
<>
|
||||
<ProjectDetailsModal />
|
||||
|
||||
{compositeEnabled && compositeContext ? (
|
||||
<CompositeContextPanel
|
||||
compositeContext={compositeContext}
|
||||
reset={resetCompositeContext}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{focusedProject ? (
|
||||
<FocusedPanel
|
||||
focusedLabel={focusedProject}
|
||||
@ -367,6 +453,8 @@ export function ProjectsSidebar(): JSX.Element {
|
||||
<GroupByFolderPanel
|
||||
groupByFolder={groupByFolder}
|
||||
groupByFolderChanged={groupByFolderChanged}
|
||||
disabled={compositeEnabled}
|
||||
disabledDescription="Group by folder is not available when composite graph is enabled"
|
||||
></GroupByFolderPanel>
|
||||
|
||||
<SearchDepth
|
||||
@ -377,8 +465,13 @@ export function ProjectsSidebar(): JSX.Element {
|
||||
decrementDepthFilter={decrementDepthFilter}
|
||||
></SearchDepth>
|
||||
|
||||
<CompositeGraphPanel
|
||||
compositeEnabled={compositeEnabled}
|
||||
compositeEnabledChanged={compositeEnabledChanged}
|
||||
></CompositeGraphPanel>
|
||||
|
||||
<ExperimentalFeature>
|
||||
<div className="mx-4 mt-8 rounded-lg border-2 border-dashed border-purple-500 p-4 shadow-lg dark:border-purple-600 dark:bg-[#0B1221]">
|
||||
<div className="mx-4 mt-8 flex flex-col gap-4 rounded-lg border-2 border-dashed border-purple-500 p-4 shadow-lg dark:border-purple-600 dark:bg-[#0B1221]">
|
||||
<h3 className="cursor-text px-4 py-2 text-sm font-semibold uppercase tracking-wide text-slate-800 lg:text-xs dark:text-slate-200">
|
||||
Experimental Features
|
||||
</h3>
|
||||
|
||||
@ -36,4 +36,5 @@ export interface CompositeNode {
|
||||
id: string;
|
||||
label: string;
|
||||
state: 'expanded' | 'collapsed' | 'hidden';
|
||||
parent?: string;
|
||||
}
|
||||
|
||||
@ -23,7 +23,7 @@ import { Dropdown, Spinner } from '@nx/graph/ui-components';
|
||||
import { getSystemTheme, Theme, ThemePanel } from '@nx/graph-internal/ui-theme';
|
||||
import { Tooltip } from '@nx/graph/ui-tooltips';
|
||||
import classNames from 'classnames';
|
||||
import { useLayoutEffect, useState } from 'react';
|
||||
import { useEffect, useLayoutEffect, useState } from 'react';
|
||||
import {
|
||||
Outlet,
|
||||
useNavigate,
|
||||
@ -43,14 +43,18 @@ import { TooltipDisplay } from './ui-tooltips/graph-tooltip-display';
|
||||
export function Shell(): JSX.Element {
|
||||
const projectGraphService = getProjectGraphService();
|
||||
const projectGraphDataService = getProjectGraphDataService();
|
||||
|
||||
const graphService = getGraphService();
|
||||
|
||||
const lastPerfReport = useSyncExternalStore(
|
||||
(callback) => graphService.listen(callback),
|
||||
() => graphService.lastPerformanceReport
|
||||
const [lastPerfReport, setLastPerfReport] = useState(
|
||||
graphService.lastPerformanceReport
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
graphService.listen(() => {
|
||||
setLastPerfReport(graphService.lastPerformanceReport);
|
||||
});
|
||||
}, []);
|
||||
|
||||
const nodesVisible = lastPerfReport.numNodes !== 0;
|
||||
|
||||
const environment = useEnvironmentConfig();
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { memo } from 'react';
|
||||
import classNames from 'classnames';
|
||||
|
||||
export interface CheckboxPanelProps {
|
||||
checked: boolean;
|
||||
@ -6,12 +7,28 @@ export interface CheckboxPanelProps {
|
||||
name: string;
|
||||
label: string;
|
||||
description: string;
|
||||
disabled?: boolean;
|
||||
disabledDescription?: string;
|
||||
}
|
||||
|
||||
export const CheckboxPanel = memo(
|
||||
({ checked, checkChanged, label, description, name }: CheckboxPanelProps) => {
|
||||
({
|
||||
checked,
|
||||
checkChanged,
|
||||
label,
|
||||
description,
|
||||
name,
|
||||
disabled,
|
||||
disabledDescription,
|
||||
}: CheckboxPanelProps) => {
|
||||
return (
|
||||
<div className="mt-8 px-4">
|
||||
<div
|
||||
className={classNames(
|
||||
'mt-8 px-4',
|
||||
disabled ? 'cursor-not-allowed opacity-50' : ''
|
||||
)}
|
||||
title={disabled ? disabledDescription : description}
|
||||
>
|
||||
<div className="flex items-start">
|
||||
<div className="flex h-5 items-center">
|
||||
<input
|
||||
@ -22,12 +39,16 @@ export const CheckboxPanel = memo(
|
||||
className="h-4 w-4 accent-blue-500 dark:accent-sky-500"
|
||||
onChange={(event) => checkChanged(event.target.checked)}
|
||||
checked={checked}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</div>
|
||||
<div className="ml-3 text-sm">
|
||||
<label
|
||||
htmlFor={name}
|
||||
className="cursor-pointer font-medium text-slate-600 dark:text-slate-400"
|
||||
className={classNames(
|
||||
' font-medium text-slate-600 dark:text-slate-400',
|
||||
disabled ? 'cursor-not-allowed' : 'cursor-pointer'
|
||||
)}
|
||||
>
|
||||
{label}
|
||||
</label>
|
||||
|
||||
@ -41,17 +41,16 @@ export function TooltipDisplay() {
|
||||
});
|
||||
break;
|
||||
case 'focus-node': {
|
||||
const to =
|
||||
action.tooltipNodeType === 'compositeNode'
|
||||
? routeConstructor(
|
||||
{
|
||||
pathname: `/projects`,
|
||||
search: `?composite=true&compositeContext=${action.id}`,
|
||||
},
|
||||
false
|
||||
)
|
||||
: routeConstructor(`/projects/${action.id}`, true);
|
||||
navigate(to);
|
||||
if (action.tooltipNodeType === 'compositeNode') {
|
||||
navigate(
|
||||
routeConstructor(
|
||||
{ pathname: `/projects`, search: `?composite=${action.id}` },
|
||||
true
|
||||
)
|
||||
);
|
||||
} else {
|
||||
navigate(routeConstructor(`/projects/${action.id}`, true));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'collapse-node':
|
||||
@ -81,7 +80,12 @@ export function TooltipDisplay() {
|
||||
navigate(
|
||||
routeConstructor(
|
||||
`/projects/trace/${encodeURIComponent(start)}/${action.id}`,
|
||||
true
|
||||
(searchParams) => {
|
||||
if (searchParams.has('composite')) {
|
||||
searchParams.delete('composite');
|
||||
}
|
||||
return searchParams;
|
||||
}
|
||||
)
|
||||
);
|
||||
break;
|
||||
|
||||
@ -327,7 +327,7 @@
|
||||
"@markdoc/markdoc": "0.2.2",
|
||||
"@monaco-editor/react": "^4.4.6",
|
||||
"@napi-rs/canvas": "^0.1.52",
|
||||
"@nx/graph": "0.0.1-alpha.16",
|
||||
"@nx/graph": "0.0.1-alpha.23",
|
||||
"@react-spring/three": "^9.7.3",
|
||||
"@react-three/drei": "^9.108.3",
|
||||
"@react-three/fiber": "^8.16.8",
|
||||
|
||||
10
pnpm-lock.yaml
generated
10
pnpm-lock.yaml
generated
@ -31,8 +31,8 @@ importers:
|
||||
specifier: ^0.1.52
|
||||
version: 0.1.55
|
||||
'@nx/graph':
|
||||
specifier: 0.0.1-alpha.16
|
||||
version: 0.0.1-alpha.16(@nx/devkit@19.8.0-beta.2(nx@19.8.0-beta.2(@swc-node/register@1.9.1(@swc/core@1.5.7(@swc/helpers@0.5.11))(@swc/types@0.1.12)(typescript@5.5.4))(@swc/core@1.5.7(@swc/helpers@0.5.11))))(nx@19.8.0-beta.2(@swc-node/register@1.9.1(@swc/core@1.5.7(@swc/helpers@0.5.11))(@swc/types@0.1.12)(typescript@5.5.4))(@swc/core@1.5.7(@swc/helpers@0.5.11)))(react-dom@18.3.1(react@18.3.1))(react-router-dom@6.26.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)
|
||||
specifier: 0.0.1-alpha.23
|
||||
version: 0.0.1-alpha.23(@nx/devkit@19.8.0-beta.2(nx@19.8.0-beta.2(@swc-node/register@1.9.1(@swc/core@1.5.7(@swc/helpers@0.5.11))(@swc/types@0.1.12)(typescript@5.5.4))(@swc/core@1.5.7(@swc/helpers@0.5.11))))(nx@19.8.0-beta.2(@swc-node/register@1.9.1(@swc/core@1.5.7(@swc/helpers@0.5.11))(@swc/types@0.1.12)(typescript@5.5.4))(@swc/core@1.5.7(@swc/helpers@0.5.11)))(react-dom@18.3.1(react@18.3.1))(react-router-dom@6.26.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)
|
||||
'@react-spring/three':
|
||||
specifier: ^9.7.3
|
||||
version: 9.7.4(@react-three/fiber@8.17.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(three@0.166.1))(react@18.3.1)(three@0.166.1)
|
||||
@ -4575,8 +4575,8 @@ packages:
|
||||
'@zkochan/js-yaml':
|
||||
optional: true
|
||||
|
||||
'@nx/graph@0.0.1-alpha.16':
|
||||
resolution: {integrity: sha512-pJVFuTunlRfa8BuO2Vy6wxRtfC2kDT3TKDR6y1KXmMl90xTVT30zU/K9ITXPZzLfo8nLpewIfbv41gGSCY9+Dg==}
|
||||
'@nx/graph@0.0.1-alpha.23':
|
||||
resolution: {integrity: sha512-vgP9CxcCLa551AWZkwfN6qkwLGdZazU+GL7KZusIH8L3enRo2GHqyPQaF93fnTenokYM0SphR85/X4QibF3mlA==}
|
||||
peerDependencies:
|
||||
'@nx/devkit': '>= 19 < 20'
|
||||
nx: '>= 19 < 20'
|
||||
@ -21629,7 +21629,7 @@ snapshots:
|
||||
- supports-color
|
||||
- verdaccio
|
||||
|
||||
'@nx/graph@0.0.1-alpha.16(@nx/devkit@19.8.0-beta.2(nx@19.8.0-beta.2(@swc-node/register@1.9.1(@swc/core@1.5.7(@swc/helpers@0.5.11))(@swc/types@0.1.12)(typescript@5.5.4))(@swc/core@1.5.7(@swc/helpers@0.5.11))))(nx@19.8.0-beta.2(@swc-node/register@1.9.1(@swc/core@1.5.7(@swc/helpers@0.5.11))(@swc/types@0.1.12)(typescript@5.5.4))(@swc/core@1.5.7(@swc/helpers@0.5.11)))(react-dom@18.3.1(react@18.3.1))(react-router-dom@6.26.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)':
|
||||
'@nx/graph@0.0.1-alpha.23(@nx/devkit@19.8.0-beta.2(nx@19.8.0-beta.2(@swc-node/register@1.9.1(@swc/core@1.5.7(@swc/helpers@0.5.11))(@swc/types@0.1.12)(typescript@5.5.4))(@swc/core@1.5.7(@swc/helpers@0.5.11))))(nx@19.8.0-beta.2(@swc-node/register@1.9.1(@swc/core@1.5.7(@swc/helpers@0.5.11))(@swc/types@0.1.12)(typescript@5.5.4))(@swc/core@1.5.7(@swc/helpers@0.5.11)))(react-dom@18.3.1(react@18.3.1))(react-router-dom@6.26.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)':
|
||||
dependencies:
|
||||
'@floating-ui/react': 0.26.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
'@headlessui/react': 1.7.19(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user