Repository: directus/directus
File: app/src/modules/content/comparison-utils.ts (TypeScript)

Code before:
	incoming: Record<string, any>;
	base: Record<string, any>;
};

export function getFieldsWithDifferences(
	comparedData: NormalizedComparison,
	fieldMetadata?: Record<string, any>,
	comparisonType?: 'version' | 'revision',
): string[] {
	return Object.keys(comparedData.incoming).filter((fieldKey) => {
		// Skip fields that don't exist in the collection's field metadata
		if (fieldMetadata && !fieldMetadata[fieldKey]) {
			return false;
		}

		// Skip read-only fields. Even if they are different, they cannot be edited, so there is no point in showing them.
		if (fieldMetadata && fieldMetadata[fieldKey]?.meta?.readonly === true) {
			return false;
		}

		// Skip related item fields when comparing revisions
		if (comparisonType === 'revision' && fieldMetadata && fieldMetadata[fieldKey]) {
			const field = fieldMetadata[fieldKey];
			const localType = field.meta?.special?.[0];

			// Exclude relational field types from revision comparison
			if (isRelationalField(localType)) {
				return false;
			}
		}

		const incomingValue = comparedData.incoming[fieldKey];
		const baseValue = comparedData.base[fieldKey];

		return !isEqual(incomingValue, baseValue);
	});
}

export function getVersionDisplayName(version: ContentVersion): string {
	return isNil(version.name) ? version.key : version.name;
}

export function isRelationalField(localType: string | undefined): boolean {
	return localType ? ['m2o', 'o2m', 'm2m', 'm2a', 'files', 'translations'].includes(localType) : false;
}

export function addFieldToSelection(selectedFields: string[], field: string): string[] {
	return [...selectedFields, field];
}

export function removeFieldFromSelection(selectedFields: string[], field: string): string[] {

Review comment (suggestion):
Instead of excluding relational fields from the comparison, let’s only prevent the colored indicators from appearing.

Code after:
	incoming: Record<string, any>;
	base: Record<string, any>;
};

export function getFieldsWithDifferences(
	comparedData: NormalizedComparison,
	fieldMetadata?: Record<string, any>,
	type?: 'version' | 'revision',
): string[] {
	return Object.keys(comparedData.incoming).filter((fieldKey) => {
		const field = fieldMetadata?.[fieldKey];
		if (!field) return false;

		if (field.meta?.readonly) return false;

		if (type === 'revision' && isRelationalField(field)) return false;

		const incomingValue = comparedData.incoming[fieldKey];
		const baseValue = comparedData.base[fieldKey];

		return !isEqual(incomingValue, baseValue);
	});
}

export function getVersionDisplayName(version: ContentVersion): string {
	return isNil(version.name) ? version.key : version.name;
}

export function isRelationalField(field: Field): boolean {
	const RELATIONAL_TYPES = ['file', 'files', 'm2o', 'o2m', 'm2m', 'm2a', 'translations'];
	return field.meta?.special?.find((type) => RELATIONAL_TYPES.includes(type)) !== undefined;
}

export function addFieldToSelection(selectedFields: string[], field: string): string[] {
	return [...selectedFields, field];
}

export function removeFieldFromSelection(selectedFields: string[], field: string): string[] {
	return selectedFields.filter((f: string) => f !== field);
}

export function toggleFieldInSelection(selectedFields: string[], field: string): string[] {
	if (selectedFields.includes(field)) {
		return removeFieldFromSelection(selectedFields, field);
	} else {
		return addFieldToSelection(selectedFields, field);
	}
}

export function toggleAllFields(selectedFields: string[], availableFields: string[], allSelected: boolean): string[] {
	if (allSelected) {