From a3371f761c86ffa2022ffaf16e8d978862de9bda Mon Sep 17 00:00:00 2001 From: Vladislav Panin Date: Tue, 8 Sep 2026 09:52:15 +0300 Subject: [PATCH] fix(editor): normalize documentation and harden editor utilities Restrict XML documentation to public and protected API and remove redundant comments. Fix generic constraints, reflection member validation, settings array ownership, hidden navigation actions, dot rendering, and hover reset. Validation: 398 EditMode tests passed; XML documentation and diff checks passed. --- .../UI/Aspid-FastTools-Default-Dark.uss | 178 ++++++-------- ...-FastTools-AspidAnimatedDotsBackground.uss | 7 +- .../Components/Aspid-FastTools-AspidBox.uss | 2 +- .../Aspid-FastTools-AspidHelpBox.uss | 10 +- .../Components/Aspid-FastTools-AspidLabel.uss | 3 - .../Aspid-FastTools-InspectorNotice.uss | 34 +-- .../Aspid-FastTools-WindowFooter.uss | 9 - .../UI/Enums/Aspid-FastTools-EnumValues.uss | 13 +- .../Aspid-FastTools-ReferenceGraph.uss | 186 +------------- ...ols-SerializeReference-ExcludedFolders.uss | 33 +-- ...id-FastTools-SerializeReference-Window.uss | 58 +---- .../Aspid-FastTools-SerializeReference.uss | 226 ++---------------- .../Aspid-FastTools-TypeSelector-View.uxml | 4 +- .../UI/Types/Aspid-FastTools-TypeSelector.uss | 104 +------- .../UI/Windows/Aspid-FastTools-Settings.uss | 162 ++----------- .../Welcome/Aspid-FastTools-Welcome.uss | 38 +-- .../Enums/EnumValueIMGUIPropertyDrawer.cs | 2 - .../Enums/EnumValueUIToolkitPropertyDrawer.cs | 5 +- .../Enums/EnumValuesIMGUIPropertyDrawer.cs | 2 +- .../Scripts/Enums/EnumValuesPropertyDrawer.cs | 2 +- .../Enums/EnumValuesPropertyDrawerHelper.cs | 6 - .../EnumValuesUIToolkitPropertyDrawer.cs | 3 +- .../Scripts/Extensions/EditorExtensions.cs | 29 +-- .../Scripts/Extensions/TypeExtensions.cs | 11 - .../Scripts/IMGUI/InspectorNoticeGUI.cs | 17 +- .../Build/SerializeReferenceBuildGate.cs | 2 - .../Build/SerializeReferenceCiGate.cs | 7 - .../Build/SerializeReferenceGateScanner.cs | 8 - .../Diagnostics/BreakageEntry.cs | 1 - .../SerializeReferenceBreakageDetector.cs | 14 +- .../SerializeReferenceBreakageHook.cs | 1 - ...ReferenceBreakageNotificationController.cs | 6 - .../Drawers/SerializeReferenceDropHandler.cs | 2 - .../Drawers/SerializeReferenceEditorGUI.cs | 51 ++-- .../Drawers/SerializeReferenceIMGUIList.cs | 26 +- .../SerializeReferenceIMGUIPropertyDrawer.cs | 20 -- .../SerializeReferenceListAddBehavior.cs | 12 +- .../Editing/MissingReferenceGroup.cs | 10 - .../Editing/SerializeReferenceBatchEditor.cs | 9 - .../SerializeReferenceConstraintCache.cs | 3 - .../Editing/SerializeReferenceGraphEditor.cs | 19 -- .../SerializeReferenceOpenCopyGuard.cs | 3 - .../Extensions/GenericArgumentRequirement.cs | 8 - .../Extensions/ReferenceGraphDocument.cs | 3 - .../Extensions/ReferenceGraphEdge.cs | 3 - .../Extensions/ReferenceGraphNode.cs | 4 - .../Extensions/ReferenceGraphRoot.cs | 3 - .../Extensions/SerializeReferenceClipboard.cs | 3 - .../SerializeReferenceDuplicateGuard.cs | 37 +-- ...erializeReferenceEditorCacheInvalidator.cs | 3 - .../SerializeReferenceGraphScanner.cs | 38 +-- .../Extensions/SerializeReferenceHelpers.cs | 159 +++--------- .../Extensions/SerializeReferenceLinker.cs | 8 +- .../SerializeReferenceMissingListGuard.cs | 14 -- .../SerializeReferenceMovedFromResolver.cs | 22 +- .../Extensions/SerializeReferenceNesting.cs | 11 +- .../SerializeReferenceRepairSuggestions.cs | 17 -- .../Extensions/SerializeReferenceRidColor.cs | 6 - .../SerializeReferenceSharedNavigation.cs | 12 - .../Extensions/SerializeReferenceTemplates.cs | 5 - .../Index/SerializeReferenceDeleteGuard.cs | 5 - .../Index/SerializeReferenceTypeUsageIndex.cs | 13 - ...alizeReferenceTypeUsageIndexInvalidator.cs | 2 - .../SerializeReferenceUsageSearchProvider.cs | 3 - .../SerializeReferencePendingAssignment.cs | 21 +- .../SerializeReferenceScriptCreator.cs | 9 +- .../Settings/GateSeverity.cs | 1 - .../SerializeReferenceExcludedFoldersField.cs | 12 - .../Settings/SerializeReferenceSettings.cs | 8 - .../SerializeReferenceSettingsProvider.cs | 3 - .../Settings/SerializeReferenceSettingsUI.cs | 7 - .../SerializeReferenceSharedSettings.cs | 5 +- .../VisualElements/SerializeReferenceField.cs | 45 ---- .../SerializeReferenceListField.cs | 4 - .../SerializeReferenceNamePrompt.cs | 1 - .../Yaml/ManagedTypeName.cs | 8 - .../Yaml/MissingReferenceEntry.cs | 2 - .../Yaml/RequiredFieldDescriptor.cs | 5 - .../Yaml/RequiredViolationEntry.cs | 2 - .../SerializeReferences/Yaml/RewriteEdit.cs | 1 - .../Yaml/SerializeReferenceYaml.cs | 14 -- .../Yaml/SerializeReferenceYamlEditor.Read.cs | 17 +- .../SerializeReferenceYamlEditor.Restore.cs | 16 +- .../SerializeReferenceYamlEditor.Rewrite.cs | 15 +- .../Yaml/SerializeReferenceYamlEditor.Scan.cs | 29 +-- .../Yaml/SerializeReferenceYamlEditor.cs | 18 +- .../Yaml/SerializeReferenceYamlProbeCache.cs | 7 - ...alizeReferenceYamlProbeCacheInvalidator.cs | 2 - .../CustomDrawerRegistry.cs | 27 +-- .../SerializePropertyExtensions.Layout.cs | 13 +- .../SerializePropertyExtensions.Path.cs | 27 +-- .../SerializePropertyExtensions.Reflection.cs | 48 +--- .../SerializePropertyExtensions.SetValue.cs | 164 ++++++++++--- .../SerializePropertyExtensions.cs | 17 +- .../AspidFastToolsPreferencesProvider.cs | 10 - .../Scripts/Settings/AspidSettingsScope.cs | 14 -- .../Scripts/Settings/AspidSettingsUI.cs | 125 +--------- .../ComponentTypeSelectorPropertyDrawer.cs | 4 - .../Drawers/SerializableTypePropertyDrawer.cs | 1 - .../Types/Drawers/TypeIMGUIPropertyDrawer.cs | 1 - .../Drawers/TypeSelectorPropertyDrawer.cs | 7 - .../Drawers/TypeUIToolkitPropertyDrawer.cs | 2 - .../Types/Extensions/TypeExtensions.cs | 26 +- .../MonoScriptIMGUIPropertyDrawer.cs | 3 - .../MonoScriptUIToolkitPropertyDrawer.cs | 6 - .../SerializableMonoScriptUtility.cs | 13 - .../Types/Selectors/GenericArgumentFilter.cs | 5 - .../Types/Selectors/GenericTypeResolver.cs | 103 ++------ .../Types/Selectors/HierarchyBuilder.cs | 10 - .../Types/Selectors/NavigationController.cs | 24 -- .../Settings/TypeSelectorPreferences.cs | 2 - .../Scripts/Types/Selectors/TreeNode.cs | 12 - .../Scripts/Types/Selectors/TreeNodeKind.cs | 4 - .../Scripts/Types/Selectors/TypeInfo.cs | 17 +- .../Types/Selectors/TypeSelectorFilter.cs | 38 +-- .../Types/Selectors/TypeSelectorFooterHint.cs | 4 - .../Selectors/TypeSelectorIconResolver.cs | 11 - .../Selectors/TypeSelectorView.Generics.cs | 11 +- .../Types/Selectors/TypeSelectorView.Input.cs | 11 - .../Selectors/TypeSelectorView.Navigation.cs | 2 - .../Types/Selectors/TypeSelectorView.Rows.cs | 16 -- .../Types/Selectors/TypeSelectorView.View.cs | 6 - .../Types/Selectors/TypeSelectorView.cs | 37 +-- .../Types/Selectors/TypeSelectorWindow.cs | 12 +- .../Scripts/Types/SerializableTypeUtility.cs | 5 - .../Types/TypeSelectorConstraintResolver.cs | 19 +- .../Scripts/Types/TypeSelectorHelpers.cs | 7 - .../Scripts/Types/TypeSelectorRequiredGate.cs | 22 +- .../Editor/Scripts/Types/TypeUtility.cs | 9 - .../VisualElements/InspectorTypeField.cs | 24 +- .../Scripts/Types/VisualElements/TypeField.cs | 17 +- .../Field/EnumFlagsFieldExtensions.cs | 4 +- .../Extensions/IBindableExtensions.cs | 14 +- .../Extensions/PropertyFieldExtensions.cs | 12 +- .../VisualElementExtensions.Bind.cs | 8 +- .../VisualElementExtensions.Commands.cs | 4 +- .../VisualElementExtensions.OwnerWindow.cs | 11 +- .../AspidAnimatedDotsBackground.cs | 12 +- .../AspidAnimatedDotsBackgroundColorsStyle.cs | 3 - .../AspidAnimatedDotsBackgroundSizeStyle.cs | 3 - .../AspidAnimatedLogo/AspidAnimatedLogo.cs | 12 +- .../AspidAnimatedLogoLayerImageStyle.cs | 5 - ...pidAnimatedLogoPulseHoverAmplitudeStyle.cs | 3 - .../AspidAnimatedLogoPulseSpeedStyle.cs | 3 - .../AspidAnimatedTitle/AspidAnimatedTitle.cs | 2 - .../AspidAnimatedTitleColorAnimationStyle.cs | 3 - .../Styles/AspidAnimatedTitleColorsStyle.cs | 2 - .../AspidAnimatedTitleWaveAnimationStyle.cs | 3 - .../Components/AspidContainers/AspidBox.cs | 3 - .../AspidDividingLines/AspidDividingLine.cs | 2 - .../Styles/AspidDividingLineDirectionStyle.cs | 2 - .../Styles/AspidDividingLineSizeStyle.cs | 2 - .../AspidGradientButton.cs | 8 +- .../Styles/AspidGradientButtonColorsStyle.cs | 2 - .../Components/AspidHelpBoxes/AspidHelpBox.cs | 3 - .../AspidHoverGradientOverlay.cs | 12 +- .../AspidHoverGradientOverlayColorStyle.cs | 2 - .../AspidHoverGradientOverlayMetricsStyle.cs | 3 - .../AspidInspectorHeader.cs | 5 - .../AspidInspectorHeaderGradientStyle.cs | 2 - .../Components/AspidLabels/AspidLabel.cs | 2 - .../AspidLabels/Styles/AspidLabelFontStyle.cs | 2 - .../AspidLabels/Styles/AspidLabelSizeStyle.cs | 2 - .../Components/AspidSwitches/AspidSwitch.cs | 37 +-- .../AspidWindowFooters/AspidWindowFooter.cs | 7 - .../Internal/Components/InspectorNotice.cs | 49 +--- .../Internal/DoubleClickTracker.cs | 2 - .../VisualElements/Internal/NavRing.cs | 38 +-- .../Internal/Styles/AspidStyles.cs | 1 - .../Internal/Styles/InlineStyle.cs | 2 - .../Internal/Styles/StatusStyle.cs | 3 - .../Styles/Theme/AspidThemeSettings.cs | 9 - .../Styles/Theme/AspidThemeSettingsUI.cs | 6 - .../Theme/AspidThemeStyleSheetExtensions.cs | 4 - .../Internal/Styles/ThemeStyle.cs | 3 - .../Asset/SerializeReferenceGraphAnalysis.cs | 17 -- .../Asset/SerializeReferenceGraphSummary.cs | 10 - .../SerializeReferenceGraphView.Cards.cs | 24 +- .../SerializeReferenceGraphView.Nodes.cs | 77 +----- .../SerializeReferenceGraphView.Picker.cs | 26 -- .../Asset/SerializeReferenceGraphView.cs | 87 +------ .../SerializeReferenceProjectSummary.cs | 13 +- .../SerializeReferenceProjectView.Actions.cs | 32 +-- .../SerializeReferenceProjectView.Cards.cs | 49 +--- .../Project/SerializeReferenceProjectView.cs | 74 +----- .../References/Shared/AuditPickerHost.cs | 20 +- .../Shared/ManagedReferenceFilter.cs | 6 - .../Shared/SerializeReferenceAuditUI.cs | 12 - .../References/Shared/ViolationFieldLabels.cs | 6 - .../Scripts/Windows/Settings/SettingsView.cs | 36 +-- .../Editor/Scripts/Windows/TabWindow.cs | 31 +-- .../Scripts/Windows/TabWindowShortcuts.cs | 9 - .../Windows/Welcome/WelcomeSettings.cs | 6 - .../Windows/Welcome/WelcomeSettingsUI.cs | 2 - .../Scripts/Windows/Welcome/WelcomeView.cs | 34 +-- .../Windows/Welcome/WelcomeWindowStartup.cs | 7 - .../SerializeReferenceSettingsTests.cs | 20 ++ .../Editor/Types/GenericTypeResolverTests.cs | 21 ++ .../TypeSelectorConstraintResolverTests.cs | 14 ++ .../VisualElementInteractionTests.cs | 100 ++++++++ .../VisualElementInteractionTests.cs.meta | 2 + 201 files changed, 746 insertions(+), 3211 deletions(-) create mode 100644 Aspid.FastTools/Packages/tech.aspid.fasttools/Tests/Editor/VisualElements/VisualElementInteractionTests.cs create mode 100644 Aspid.FastTools/Packages/tech.aspid.fasttools/Tests/Editor/VisualElements/VisualElementInteractionTests.cs.meta diff --git a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Resources/UI/Aspid-FastTools-Default-Dark.uss b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Resources/UI/Aspid-FastTools-Default-Dark.uss index 2d113b5a..24b0c8e4 100644 --- a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Resources/UI/Aspid-FastTools-Default-Dark.uss +++ b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Resources/UI/Aspid-FastTools-Default-Dark.uss @@ -1,92 +1,86 @@ :root { - /* ---------------------------------- ICONS ---------------------------------- */ - --aspid-icons-status-success: resource("Icons/aspid_icon_medium_green_256x253"); + + --aspid-icons-status-success: resource("Icons/aspid_icon_medium_green_256x253"); --aspid-icons-status-warning: resource("Icons/aspid_icon_medium_yellow_256x253"); - --aspid-icons-status-error: resource("Icons/aspid_icon_medium_red_256x253"); + --aspid-icons-status-error: resource("Icons/aspid_icon_medium_red_256x253"); --aspid-icons-status-info: resource("Icons/aspid_icon_medium_blue_256x253"); --aspid-icons-home: resource("Icons/aspid_icon_home"); --aspid-icons-settings: resource("Icons/aspid_icon_settings"); - /* ------------------------------- BACKGROUNDS ------------------------------- */ - --aspid-colors-bg-darkness: rgb(26, 26, 26); /* #1A1A1A */ - --aspid-colors-bg-dark: rgb(36, 36, 36); /* #242424 */ - --aspid-colors-bg-light: rgb(46, 46, 46); /* #2E2E2E */ - --aspid-colors-bg-lightness: rgb(56, 56, 56); /* #383838 */ - - /* ---------------------- TEXT: GENERAL (High Contrast) ---------------------- */ - --aspid-colors-text-lightness: rgb(220, 220, 220); /* #DCDCDC */ - --aspid-colors-text-light: rgb(190, 190, 190); /* #BEBEBE */ - --aspid-colors-text-dark: rgb(150, 150, 150); /* #969696 */ - --aspid-colors-text-darkness: rgb(110, 110, 110); /* #6E6E6E */ - - /* -------------------- SHADES: GENERAL (Lines & Borders) -------------------- */ - --aspid-colors-shade-darkness: rgb(45, 45, 45); /* #2D2D2D */ - --aspid-colors-shade-dark: rgb(60, 60, 60); /* #3C3C3C */ - --aspid-colors-shade-light: rgb(80, 80, 80); /* #505050 */ - --aspid-colors-shade-lightness: rgb(100, 100, 100); /* #646464 */ - - /* ---------------- STATUS: BACKGROUNDS (Deep Gemstone Bases) ---------------- */ - --aspid-colors-status-success-darkness: rgb(8, 40, 20); /* #082814 */ - --aspid-colors-status-success-dark: rgb(12, 65, 30); /* #0C411E */ - --aspid-colors-status-success-light: rgb(20, 90, 45); /* #145A2D */ - --aspid-colors-status-success-lightness: rgb(30, 120, 60); /* #1E783C */ - - --aspid-colors-status-warning-darkness: rgb(45, 30, 5); /* #2D1E05 */ - --aspid-colors-status-warning-dark: rgb(85, 55, 10); /* #55370A */ - --aspid-colors-status-warning-light: rgb(125, 85, 20); /* #7D5514 */ - --aspid-colors-status-warning-lightness: rgb(165, 115, 30); /* #A5731E */ - - --aspid-colors-status-error-darkness: rgb(50, 10, 10); /* #320A0A */ - --aspid-colors-status-error-dark: rgb(85, 20, 20); /* #551414 */ - --aspid-colors-status-error-light: rgb(125, 35, 35); /* #7D2323 */ - --aspid-colors-status-error-lightness: rgb(165, 50, 50); /* #A53232 */ - - --aspid-colors-status-info-darkness: rgb(8, 25, 50); /* #081932 */ - --aspid-colors-status-info-dark: rgb(15, 45, 85); /* #0F2D55 */ - --aspid-colors-status-info-light: rgb(25, 75, 130); /* #194B82 */ - --aspid-colors-status-info-lightness: rgb(35, 105, 180); /* #2369B4 */ - - /* --------------- STATUS ACCENTS: TEXT (Gemstone Brilliance) ---------------- */ - --aspid-colors-status-success-text-darkness: rgb(55, 110, 65); /* #376E41 */ - --aspid-colors-status-success-text-dark: rgb(85, 175, 100); /* #55AF64 */ - --aspid-colors-status-success-text-light: rgb(120, 235, 145); /* #78EB91 */ - --aspid-colors-status-success-text-lightness: rgb(180, 255, 200); /* #B4FFC8 */ - - --aspid-colors-status-warning-text-darkness: rgb(120, 85, 35); /* #785523 */ - --aspid-colors-status-warning-text-dark: rgb(185, 135, 60); /* #B9873C */ - --aspid-colors-status-warning-text-light: rgb(245, 185, 85); /* #F5B955 */ - --aspid-colors-status-warning-text-lightness: rgb(255, 235, 175); /* #FFEBAF */ - - --aspid-colors-status-error-text-darkness: rgb(125, 45, 45); /* #7D2D2D */ - --aspid-colors-status-error-text-dark: rgb(185, 65, 65); /* #B94141 */ - --aspid-colors-status-error-text-light: rgb(235, 95, 95); /* #EB5F5F */ - --aspid-colors-status-error-text-lightness: rgb(255, 175, 175); /* #FFAFAF */ - - --aspid-colors-status-info-text-darkness: rgb(40, 80, 125); /* #28507D */ - --aspid-colors-status-info-text-dark: rgb(65, 130, 185); /* #4182B9 */ - --aspid-colors-status-info-text-light: rgb(90, 185, 240); /* #5AB9F0 */ - --aspid-colors-status-info-text-lightness: rgb(190, 240, 255); /* #BEF0FF */ - - /* ---------------- STATUS ACCENTS: SHADES (Muted Gemstones) ----------------- */ - --aspid-colors-status-success-shade-darkness: rgb(25, 55, 30); /* #19371E */ - --aspid-colors-status-success-shade-dark: rgb(40, 85, 50); /* #285532 */ - --aspid-colors-status-success-shade-light: rgb(65, 130, 80); /* #418250 */ - --aspid-colors-status-success-shade-lightness: rgb(95, 190, 115); /* #5FBE73 */ - - --aspid-colors-status-warning-shade-darkness: rgb(55, 40, 20); /* #372814 */ - --aspid-colors-status-warning-shade-dark: rgb(90, 65, 30); /* #5A411E */ - --aspid-colors-status-warning-shade-light: rgb(135, 100, 50); /* #876432 */ - --aspid-colors-status-warning-shade-lightness: rgb(185, 140, 75); /* #B98C4B */ - - --aspid-colors-status-error-shade-darkness: rgb(55, 25, 25); /* #371919 */ - --aspid-colors-status-error-shade-dark: rgb(90, 45, 45); /* #5A2D2D */ - --aspid-colors-status-error-shade-light: rgb(135, 65, 65); /* #874141 */ - --aspid-colors-status-error-shade-lightness: rgb(190, 95, 95); /* #BE5F5F */ - - --aspid-colors-status-info-shade-darkness: rgb(20, 35, 55); /* #142337 */ - --aspid-colors-status-info-shade-dark: rgb(35, 65, 95); /* #23415F */ - --aspid-colors-status-info-shade-light: rgb(55, 100, 145); /* #376491 */ - --aspid-colors-status-info-shade-lightness: rgb(85, 145, 200); /* #5591C8 */ + --aspid-colors-bg-darkness: rgb(26, 26, 26); + --aspid-colors-bg-dark: rgb(36, 36, 36); + --aspid-colors-bg-light: rgb(46, 46, 46); + --aspid-colors-bg-lightness: rgb(56, 56, 56); + + --aspid-colors-text-lightness: rgb(220, 220, 220); + --aspid-colors-text-light: rgb(190, 190, 190); + --aspid-colors-text-dark: rgb(150, 150, 150); + --aspid-colors-text-darkness: rgb(110, 110, 110); + + --aspid-colors-shade-darkness: rgb(45, 45, 45); + --aspid-colors-shade-dark: rgb(60, 60, 60); + --aspid-colors-shade-light: rgb(80, 80, 80); + --aspid-colors-shade-lightness: rgb(100, 100, 100); + + --aspid-colors-status-success-darkness: rgb(8, 40, 20); + --aspid-colors-status-success-dark: rgb(12, 65, 30); + --aspid-colors-status-success-light: rgb(20, 90, 45); + --aspid-colors-status-success-lightness: rgb(30, 120, 60); + + --aspid-colors-status-warning-darkness: rgb(45, 30, 5); + --aspid-colors-status-warning-dark: rgb(85, 55, 10); + --aspid-colors-status-warning-light: rgb(125, 85, 20); + --aspid-colors-status-warning-lightness: rgb(165, 115, 30); + + --aspid-colors-status-error-darkness: rgb(50, 10, 10); + --aspid-colors-status-error-dark: rgb(85, 20, 20); + --aspid-colors-status-error-light: rgb(125, 35, 35); + --aspid-colors-status-error-lightness: rgb(165, 50, 50); + + --aspid-colors-status-info-darkness: rgb(8, 25, 50); + --aspid-colors-status-info-dark: rgb(15, 45, 85); + --aspid-colors-status-info-light: rgb(25, 75, 130); + --aspid-colors-status-info-lightness: rgb(35, 105, 180); + + --aspid-colors-status-success-text-darkness: rgb(55, 110, 65); + --aspid-colors-status-success-text-dark: rgb(85, 175, 100); + --aspid-colors-status-success-text-light: rgb(120, 235, 145); + --aspid-colors-status-success-text-lightness: rgb(180, 255, 200); + + --aspid-colors-status-warning-text-darkness: rgb(120, 85, 35); + --aspid-colors-status-warning-text-dark: rgb(185, 135, 60); + --aspid-colors-status-warning-text-light: rgb(245, 185, 85); + --aspid-colors-status-warning-text-lightness: rgb(255, 235, 175); + + --aspid-colors-status-error-text-darkness: rgb(125, 45, 45); + --aspid-colors-status-error-text-dark: rgb(185, 65, 65); + --aspid-colors-status-error-text-light: rgb(235, 95, 95); + --aspid-colors-status-error-text-lightness: rgb(255, 175, 175); + + --aspid-colors-status-info-text-darkness: rgb(40, 80, 125); + --aspid-colors-status-info-text-dark: rgb(65, 130, 185); + --aspid-colors-status-info-text-light: rgb(90, 185, 240); + --aspid-colors-status-info-text-lightness: rgb(190, 240, 255); + + --aspid-colors-status-success-shade-darkness: rgb(25, 55, 30); + --aspid-colors-status-success-shade-dark: rgb(40, 85, 50); + --aspid-colors-status-success-shade-light: rgb(65, 130, 80); + --aspid-colors-status-success-shade-lightness: rgb(95, 190, 115); + + --aspid-colors-status-warning-shade-darkness: rgb(55, 40, 20); + --aspid-colors-status-warning-shade-dark: rgb(90, 65, 30); + --aspid-colors-status-warning-shade-light: rgb(135, 100, 50); + --aspid-colors-status-warning-shade-lightness: rgb(185, 140, 75); + + --aspid-colors-status-error-shade-darkness: rgb(55, 25, 25); + --aspid-colors-status-error-shade-dark: rgb(90, 45, 45); + --aspid-colors-status-error-shade-light: rgb(135, 65, 65); + --aspid-colors-status-error-shade-lightness: rgb(190, 95, 95); + + --aspid-colors-status-info-shade-darkness: rgb(20, 35, 55); + --aspid-colors-status-info-shade-dark: rgb(35, 65, 95); + --aspid-colors-status-info-shade-light: rgb(55, 100, 145); + --aspid-colors-status-info-shade-lightness: rgb(85, 145, 200); } .aspid-fasttools-inspector-container { @@ -102,7 +96,6 @@ margin-left: -1px; } -/* -------------------------------------------------- BACKGROUNDS --------------------------------------------------- */ .aspid-fasttools-background { } .aspid-fasttools-background--rounded { @@ -126,7 +119,6 @@ background-color: var(--aspid-colors-bg-lightness); } -/* -------------------------------- SUCCESS: BACKGROUNDS -------------------------------- */ .aspid-fasttools-background.aspid-fasttools-theme--darkness.aspid-fasttools-status--success { background-color: var(--aspid-colors-status-success-darkness); } @@ -143,7 +135,6 @@ background-color: var(--aspid-colors-status-success-lightness); } -/* -------------------------------- WARNING: BACKGROUNDS -------------------------------- */ .aspid-fasttools-background.aspid-fasttools-theme--darkness.aspid-fasttools-status--warning { background-color: var(--aspid-colors-status-warning-darkness); } @@ -160,7 +151,6 @@ background-color: var(--aspid-colors-status-warning-lightness); } -/* --------------------------------- ERROR: BACKGROUNDS --------------------------------- */ .aspid-fasttools-background.aspid-fasttools-theme--darkness.aspid-fasttools-status--error { background-color: var(--aspid-colors-status-error-darkness); } @@ -177,7 +167,6 @@ background-color: var(--aspid-colors-status-error-lightness); } -/* --------------------------------- INFO: BACKGROUNDS ---------------------------------- */ .aspid-fasttools-background.aspid-fasttools-theme--darkness.aspid-fasttools-status--info { background-color: var(--aspid-colors-status-info-darkness); } @@ -193,9 +182,7 @@ .aspid-fasttools-background.aspid-fasttools-theme--lightness.aspid-fasttools-status--info { background-color: var(--aspid-colors-status-info-lightness); } -/*--------------------------------------------------------------------------------------------------------------------*/ -/* ---------------------------------------------------- LABELS ------------------------------------------------------ */ Label.aspid-fasttools-theme--darkness { color: var(--aspid-colors-text-darkness); } @@ -212,7 +199,6 @@ Label.aspid-fasttools-theme--lightness { color: var(--aspid-colors-text-lightness); } -/* ------------------------ SUCCESS: LABELS ------------------------*/ Label.aspid-fasttools-theme--darkness.aspid-fasttools-status--success { color: var(--aspid-colors-status-success-text-darkness); } @@ -229,7 +215,6 @@ Label.aspid-fasttools-theme--lightness.aspid-fasttools-status--success { color: var(--aspid-colors-status-success-text-lightness); } -/* ------------------------ WARNING: LABELS ------------------------*/ Label.aspid-fasttools-theme--darkness.aspid-fasttools-status--warning { color: var(--aspid-colors-status-warning-text-darkness); } @@ -246,7 +231,6 @@ Label.aspid-fasttools-theme--lightness.aspid-fasttools-status--warning { color: var(--aspid-colors-status-warning-text-lightness); } -/* ------------------------- ERROR: LABELS -------------------------*/ Label.aspid-fasttools-theme--darkness.aspid-fasttools-status--error { color: var(--aspid-colors-status-error-text-darkness); } @@ -263,7 +247,6 @@ Label.aspid-fasttools-theme--lightness.aspid-fasttools-status--error { color: var(--aspid-colors-status-error-text-lightness); } -/* ------------------------- INFO: LABELS --------------------------*/ Label.aspid-fasttools-theme--darkness.aspid-fasttools-status--info { color: var(--aspid-colors-status-info-text-darkness); } @@ -279,9 +262,7 @@ Label.aspid-fasttools-theme--light.aspid-fasttools-status--info { Label.aspid-fasttools-theme--lightness.aspid-fasttools-status--info { color: var(--aspid-colors-status-info-text-lightness); } -/*--------------------------------------------------------------------------------------------------------------------*/ -/* --------------------------------------------------- BUTTONS ------------------------------------------------------ */ Button.aspid-fasttools-theme--darkness { color: var(--aspid-colors-text-darkness); background-color: var(--aspid-colors-bg-darkness); @@ -302,7 +283,6 @@ Button.aspid-fasttools-theme--lightness { background-color: var(--aspid-colors-bg-lightness); } -/* ----------------------- SUCCESS: BUTTONS ------------------------*/ Button.aspid-fasttools-theme--darkness.aspid-fasttools-status--success { color: var(--aspid-colors-status-success-text-darkness); background-color: var(--aspid-colors-status-success-darkness); @@ -323,7 +303,6 @@ Button.aspid-fasttools-theme--lightness.aspid-fasttools-status--success { background-color: var(--aspid-colors-status-success-lightness); } -/* ----------------------- WARNING: BUTTONS ------------------------*/ Button.aspid-fasttools-theme--darkness.aspid-fasttools-status--warning { color: var(--aspid-colors-status-warning-text-darkness); background-color: var(--aspid-colors-status-warning-darkness); @@ -344,7 +323,6 @@ Button.aspid-fasttools-theme--lightness.aspid-fasttools-status--warning { background-color: var(--aspid-colors-status-warning-lightness); } -/* ------------------------ ERROR: BUTTONS -------------------------*/ Button.aspid-fasttools-theme--darkness.aspid-fasttools-status--error { color: var(--aspid-colors-status-error-text-darkness); background-color: var(--aspid-colors-status-error-darkness); @@ -365,7 +343,6 @@ Button.aspid-fasttools-theme--lightness.aspid-fasttools-status--error { background-color: var(--aspid-colors-status-error-lightness); } -/* ------------------------ INFO: BUTTONS --------------------------*/ Button.aspid-fasttools-theme--darkness.aspid-fasttools-status--info { color: var(--aspid-colors-status-info-text-darkness); background-color: var(--aspid-colors-status-info-darkness); @@ -385,4 +362,3 @@ Button.aspid-fasttools-theme--lightness.aspid-fasttools-status--info { color: var(--aspid-colors-status-info-text-lightness); background-color: var(--aspid-colors-status-info-lightness); } -/*--------------------------------------------------------------------------------------------------------------------*/ diff --git a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Resources/UI/Components/Aspid-FastTools-AspidAnimatedDotsBackground.uss b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Resources/UI/Components/Aspid-FastTools-AspidAnimatedDotsBackground.uss index c28274ae..186167f4 100644 --- a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Resources/UI/Components/Aspid-FastTools-AspidAnimatedDotsBackground.uss +++ b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Resources/UI/Components/Aspid-FastTools-AspidAnimatedDotsBackground.uss @@ -9,9 +9,6 @@ --aspid-fasttools-colors-dot_blob-color_2: var(--aspid-colors-status-warning-text-dark); --aspid-fasttools-colors-dot_blob-color_3: var(--aspid-colors-status-error-text-dark); - /* Status washes: dim, desaturated hues a status class paints across all three blobs at once, so the canvas - reads as one calm state wash instead of the default good→bad gradient. Component-scoped — the palette's - status families are tuned for text and borders and read far too saturated behind a whole window. */ --aspid-fasttools-colors-dot_blob-info: rgb(38, 54, 77); --aspid-fasttools-colors-dot_blob-success: rgb(41, 71, 54); --aspid-fasttools-colors-dot_blob-warning: rgb(77, 64, 31); @@ -22,9 +19,7 @@ --aspid-fasttools-metrics-dot_scale_reference: 420; } -/* Declared after :root deliberately — a status class and :root carry the same specificity, so source order is what - lets a status win over the default gradient. Dropping the class falls back to :root, which is how StatusStyle.Type.None - restores the three-tone signal look. */ +/* Status rules must follow :root to win at equal specificity and restore the default palette when removed. */ .aspid-fasttools-status--info { --aspid-fasttools-colors-dot_blob-color_1: var(--aspid-fasttools-colors-dot_blob-info); --aspid-fasttools-colors-dot_blob-color_2: var(--aspid-fasttools-colors-dot_blob-info); diff --git a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Resources/UI/Components/Aspid-FastTools-AspidBox.uss b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Resources/UI/Components/Aspid-FastTools-AspidBox.uss index 2acfd217..084cab64 100644 --- a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Resources/UI/Components/Aspid-FastTools-AspidBox.uss +++ b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Resources/UI/Components/Aspid-FastTools-AspidBox.uss @@ -1,3 +1,3 @@ :root { flex-grow: 1; -} \ No newline at end of file +} diff --git a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Resources/UI/Components/Aspid-FastTools-AspidHelpBox.uss b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Resources/UI/Components/Aspid-FastTools-AspidHelpBox.uss index 44754fa9..4aa05016 100644 --- a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Resources/UI/Components/Aspid-FastTools-AspidHelpBox.uss +++ b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Resources/UI/Components/Aspid-FastTools-AspidHelpBox.uss @@ -28,7 +28,6 @@ border-color: var(--aspid-colors-status-info-shade-lightness); } -/* --- ICON --- */ .aspid-fasttools-help-box__icon { min-width: 34px; max-width: 34px; @@ -57,13 +56,10 @@ --aspid-fasttools-icons-help_box: resource("d_console.erroricon@2x"); } -/* --- TEXT --- */ .aspid-fasttools-help-box__text-container { flex-grow: 1; } -/* Small gap below the title (and its dividing line) so the message is not glued to the line above; - only present when a title is set, so title-less help boxes keep the message centered on the icon. */ .aspid-fasttools-help-box__title { margin-bottom: 4px; } @@ -73,11 +69,11 @@ overflow: hidden; } -:root.aspid-fasttools-status--success AspidLabel > Label { +:root.aspid-fasttools-status--success AspidLabel > Label { color: var(--aspid-colors-status-success-text-lightness); } -:root.aspid-fasttools-status--warning AspidLabel > Label { +:root.aspid-fasttools-status--warning AspidLabel > Label { color: var(--aspid-colors-status-warning-text-lightness); } @@ -85,6 +81,6 @@ color: var(--aspid-colors-status-error-text-lightness); } -:root.aspid-fasttools-status--info AspidLabel > Label { +:root.aspid-fasttools-status--info AspidLabel > Label { color: var(--aspid-colors-status-info-text-lightness); } diff --git a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Resources/UI/Components/Aspid-FastTools-AspidLabel.uss b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Resources/UI/Components/Aspid-FastTools-AspidLabel.uss index 7bc96c06..5d55d683 100644 --- a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Resources/UI/Components/Aspid-FastTools-AspidLabel.uss +++ b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Resources/UI/Components/Aspid-FastTools-AspidLabel.uss @@ -12,7 +12,6 @@ AspidDividingLine { margin: 5px 0 0 0; } -/* --- LABEL SIZES --- */ .aspid-fasttools-label-size--h1 { font-size: 36px; } @@ -41,7 +40,6 @@ AspidDividingLine { font-size: 12px; } -/* General Text */ .aspid-fasttools-theme--darkness Label { color: var(--aspid-colors-text-darkness); } @@ -58,7 +56,6 @@ AspidDividingLine { color: var(--aspid-colors-text-lightness); } -/* Status Text */ AspidLabel.aspid-fasttools-status--success.aspid-fasttools-theme--darkness > Label { color: var(--aspid-colors-status-success-text-darkness); } diff --git a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Resources/UI/Components/Aspid-FastTools-InspectorNotice.uss b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Resources/UI/Components/Aspid-FastTools-InspectorNotice.uss index 6415d795..6970fbb4 100644 --- a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Resources/UI/Components/Aspid-FastTools-InspectorNotice.uss +++ b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Resources/UI/Components/Aspid-FastTools-InspectorNotice.uss @@ -1,13 +1,8 @@ -/* --- Inspector notice --- - Compact single-row notice shared by the [SerializeReference] and [TypeSelector] drawers (missing type, required, - shared reference): a small warning icon, a terse yellow message and a bold, right-aligned clickable action word that - lightens on hover. The full explanation rides the element's tooltip on hover. The palette variables come from the - Aspid theme sheet the element adds itself. */ .aspid-fasttools-inspector-notice { flex-direction: row; align-items: center; flex-wrap: wrap; - /* No left inset of its own — the foldout's left margin already clears the stripe gutter for the whole content. */ + margin: 2px 0 1px 2px; } @@ -22,25 +17,16 @@ background-image: resource("d_console.warnicon"); } -/* Both states use the same yellow text per the design — warning, not info. */ .aspid-fasttools-inspector-notice__message { color: var(--aspid-colors-status-warning-text-light); -unity-font-style: normal; white-space: normal; } -/* The shared notice's message is itself clickable — it reveals the other members of the group (scroll + pulse) — so - it carries the link cursor. Its hover lighten comes from code, since the rid colour is dynamic; no underline — that - affordance is reserved for the action words, so the message reads as a label that happens to navigate. */ .aspid-fasttools-inspector-notice__message--navigable { cursor: link; } -/* The clickable action word (Fix / Make unique): bold, pinned to the right of the row — margin-left:auto eats the free - space so it reads as a deliberate affordance rather than a word trailing the message — underlined (a rich-text - set from code, since USS has no text-decoration) so "this is a button" is unambiguous, and it lightens on hover. - Missing type keeps the warning amber here; the shared notice overrides the colour inline with its per-rid colour - (and lightens on hover from code, since that colour is dynamic). */ .aspid-fasttools-inspector-notice__action { margin-left: auto; color: var(--aspid-colors-status-warning-text-light); @@ -52,9 +38,6 @@ color: var(--aspid-colors-status-warning-text-lightness); } -/* Trailing Smart Fix suggestion ("· → Pistol?"): a second clickable word riding just after Fix (a 6px gap, so the two - actions cluster together at the right edge), same warning palette, underlined and lightening on hover like Fix. - Hidden by default; the --visible modifier reveals it when a suggestion is set. */ .aspid-fasttools-inspector-notice__suggestion { margin-left: 6px; color: var(--aspid-colors-status-warning-text-light); @@ -67,8 +50,6 @@ display: flex; } -/* The "·" between Fix and the suggestion: decoration, not an action — same palette and weight as its neighbours, - but never underlined, no link cursor, no hover (an underlined separator would read as part of the button). */ .aspid-fasttools-inspector-notice__suggestion-separator { margin-left: 6px; color: var(--aspid-colors-status-warning-text-light); @@ -84,10 +65,6 @@ color: var(--aspid-colors-status-warning-text-lightness); } -/* Leading rid swatch: a small colour-coded circle at the head of the shared row. It shares its per-rid colour inline - with the message text and the field's left stripe, so the whole field reads in one colour and aliased fields match at - a glance by their lined-up swatches. Its colour is set inline from code (unique per reference); hidden by default, - the --visible modifier reveals it when a colour is supplied (the shared notice). */ .aspid-fasttools-inspector-notice__dot { width: 8px; height: 8px; @@ -101,20 +78,11 @@ display: flex; } -/* --- Shared-reference variant --- - A shared reference is not an error but a "these two fields point at the same object" state, so it drops the warning - amber for a calm treatment carried by the reference's per-rid colour: the leading swatch, the message text, the - Make-unique action and the field's left stripe are all tinted that one colour (set inline from code), with no icon. - The Make-unique action keeps the base action treatment (bold, pinned right, underlined, lightens on hover) — only - its colour differs: the inline rid colour (its hover lighten comes from code, since that colour is dynamic). */ .aspid-fasttools-inspector-notice--shared .aspid-fasttools-inspector-notice__icon { display: none; } -/* Info variant — a non-actionable, dim hint (the multi-object "different types" notice that stands in for the - suppressed per-instance child fields). Swaps the warning icon for the info icon and the yellow text for the - generic dim shade, so it reads as informational rather than something to fix. */ .aspid-fasttools-inspector-notice--info .aspid-fasttools-inspector-notice__icon { background-image: resource("d_console.infoicon"); diff --git a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Resources/UI/Components/Aspid-FastTools-WindowFooter.uss b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Resources/UI/Components/Aspid-FastTools-WindowFooter.uss index 8236794e..2026a421 100644 --- a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Resources/UI/Components/Aspid-FastTools-WindowFooter.uss +++ b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Resources/UI/Components/Aspid-FastTools-WindowFooter.uss @@ -1,13 +1,9 @@ -/* Shared bottom bar for Aspid FastTools editor windows: a faded dividing line above a version/GitHub row. - Kept transparent so a host window's dotted canvas reads continuously behind it. The row mirrors the old - Welcome-footer inset (10px sides) and the block lifts the text off the window's bottom edge. */ .aspid-fasttools-window-footer { flex-shrink: 0; margin-top: 2px; padding-bottom: 8px; } -/* The divider is faded so it reads as a hairline separator rather than a hard rule. */ .aspid-fasttools-window-footer AspidDividingLine { opacity: 0.5; } @@ -20,7 +16,6 @@ padding-right: 10px; } -/* Version: dim grey at rest, brightening on hover to signal it links to the tagged release. */ .aspid-fasttools-window-footer__version { color: var(--aspid-colors-text-darkness); font-size: 14px; @@ -30,9 +25,6 @@ color: var(--aspid-colors-text-light); } -/* The keyboard-ring key, absolutely centred over the whole row (independent of the side links' widths) in the - quietest text tone — present for discoverability, but quieter than the version so it never competes. Click- - transparent from code so the links keep their hits. */ .aspid-fasttools-window-footer__keys { position: absolute; left: 0; @@ -44,7 +36,6 @@ -unity-text-align: middle-center; } -/* GitHub link: the signature green, brightening on hover. */ .aspid-fasttools-window-footer__link { color: var(--aspid-colors-status-success-text-dark); font-size: 14px; diff --git a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Resources/UI/Enums/Aspid-FastTools-EnumValues.uss b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Resources/UI/Enums/Aspid-FastTools-EnumValues.uss index 71e43165..112bdb21 100644 --- a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Resources/UI/Enums/Aspid-FastTools-EnumValues.uss +++ b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Resources/UI/Enums/Aspid-FastTools-EnumValues.uss @@ -1,25 +1,20 @@ -/* Negative right margin cancels the inspector's default field padding, aligning the card's - right edge with sibling fields. */ +/* The negative margin cancels the inspector's field padding. */ :root { margin: 1px -2px 1px 3px; } -/* Zeroes Unity's built-in field margin, which otherwise double-counts against this drawer's - own padding and pushes nested fields inward. */ +/* Cancel Unity's field margins to avoid double indentation inside the drawer. */ .unity-property-field__inspector-property .unity-base-field__inspector-field { margin-left: 0; margin-right: 0; } -/* Toggle carries an extra 1px inset Unity bakes into its own style — nudge it back to match - the other fields. */ +/* Toggle adds a built-in one-pixel inset beyond the other fields. */ .unity-property-field__inspector-property Toggle.unity-base-field__inspector-field { margin-left: -1px; } -/* Header and container are separate elements styled to read as one rounded card — top corners - on the header, bottom on the container, same border color to hide the seam. */ .aspid-fasttools-enum-values__header { padding: 5px 5px 2px 5px; border-width: 1px; @@ -36,8 +31,6 @@ background-color: var(--aspid-colors-bg-lightness); } -/* Single-line values sit next to the key in two equal columns (mirrors the IMGUI layout); - values with a foldout keep the default column flow — key line, then a "Value" foldout. */ .aspid-fasttools-enum-value--inline { flex-direction: row; } diff --git a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Resources/UI/SerializeReferences/Aspid-FastTools-ReferenceGraph.uss b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Resources/UI/SerializeReferences/Aspid-FastTools-ReferenceGraph.uss index 65d13b8a..5b1af523 100644 --- a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Resources/UI/SerializeReferences/Aspid-FastTools-ReferenceGraph.uss +++ b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Resources/UI/SerializeReferences/Aspid-FastTools-ReferenceGraph.uss @@ -1,26 +1,11 @@ -/* --- Inspect Asset view --- - Transparent content floating over the window's shared dotted canvas: a boxed asset-picker card (Aspid title + - description, then a single control that fuses the asset field into the Rescan action), then either a centred hero - (no asset / no managed references) or a scrolling per-document tree of indented node cards. Each node row shows the - stored type short name - (amber when the type is missing), the dim rid and MISSING / SHARED badges; a missing row carries a trailing amber - Fix gradient button that expands an inline type picker. Orphaned rids — reachable from no root — collect in a - trailing warning-tinted group per document. */ -/* The scrolled content. flex-grow lets it stretch to the viewport when the graph is short (so the empty hero centres); - flex-shrink:0 stops the scroll's content-container from compressing it when it overflows (UIToolkit's flex-shrink - defaults to 1, which would squash the cards instead of scrolling) — the pair gives "fill when small, scroll when - tall". */ +/* Prevent flex compression so tall content scrolls instead of collapsing its cards. */ .aspid-fasttools-reference-graph__content { flex-grow: 1; flex-shrink: 0; padding: 12px; } -/* The asset-picker card joins the Project Audit family: a translucent dark fill (the same rgba(20,20,20,0.55) the - Project Audit panel and group cards carry) so the window's dotted canvas reads through it rather than sitting on an - opaque slab, framed by a hairline border and an 8px radius. AspidBox would otherwise paint an opaque - --aspid-colors-bg-darkness fill and a 10px --rounded radius; the compound selector raises specificity over both - that theme rule and the box's own `:root { flex-grow: 1; }`, pinning the card to its content height. */ +/* The compound selector outranks AspidBox's theme and :root layout rules. */ .aspid-fasttools-reference-graph__card.aspid-fasttools-background { flex-grow: 0; margin-bottom: 12px; @@ -31,8 +16,6 @@ background-color: rgba(20, 20, 20, 0.55); } -/* Title + one-line description, mirroring the Project Audit panel's __panel-title / __panel-description rhythm: the - title sits tight above the description, the description carries the dim tone and the gap down to the asset field. */ .aspid-fasttools-reference-graph__card-title { padding-left: 4px; margin-bottom: 2px; @@ -45,18 +28,12 @@ margin-bottom: 12px; } -/* The asset field rides inside the Rescan button as its leading content: it grows to fill the row (the "Rescan" label - pins to the right) and sheds the ObjectField's default field margin so its left edge stays flush in the row. */ .aspid-fasttools-reference-graph__asset { flex-grow: 1; margin: 0; } -/* Rescan hosts the asset field: a 30px row (the gradient button's own default height, so this panel's action matches the - Project References "Scan Project" button height exactly and the two tabs' top panels read at the same size), with the - "Rescan" label flush left, the centred field filling the row to its right. Code drops the label text when no asset is - selected, keeping the field — and so the picker — reachable. Scoped under __card so these layout overrides outrank the - gradient button's own :root defaults (padding / margin) on the class tie. */ +/* The parent scope outranks the gradient button's :root spacing rules. */ .aspid-fasttools-reference-graph__card .aspid-fasttools-reference-graph__rescan { height: 30px; @@ -65,16 +42,11 @@ margin: 0; } -/* The button's own label carries the "Rescan" word. FillWithTrailingContent stops it growing (so the hosted field owns - the row), leaving it to pin left before the field; the right margin spaces it off the field. */ .aspid-fasttools-reference-graph__rescan .aspid-fasttools-gradient-button__label { margin-right: 8px; -unity-text-align: middle-left; } -/* Centred hero for the two empty states (no asset / no managed references): a large dimmed info icon, a headline - and a dimmed explanation, filling the space below the asset card. The --hidden modifier swaps it for the results - scroll (and vice versa), mirroring the Project Audit view's hero. */ .aspid-fasttools-reference-graph__empty { flex-grow: 1; padding: 20px; @@ -111,11 +83,7 @@ -unity-text-align: middle-center; } -/* One scroll wraps the whole view (asset card + hero + overview + document list) so everything between the window's - tabs and footer scrolls as one — the asset card is no longer pinned above a separately-scrolling list. flex-grow - fills the area between toolbar and footer; the content-container override lets the inner content stretch to the - viewport (UIToolkit's default flex-shrink:0 keeps it from collapsing when it overflows) so the empty hero still - centres while a long graph scrolls. */ +/* Growing the content container centers short content while taller content remains scrollable. */ .aspid-fasttools-reference-graph__scroll { flex-grow: 1; } @@ -125,16 +93,10 @@ flex-shrink: 0; } -/* The per-document list, swapped for the centred empty hero by the --hidden modifier. */ .aspid-fasttools-reference-graph__list--hidden { display: none; } -/* --- Scan overview --- - A status headline over a dim one-line breakdown, sitting between the asset card and the document scroll — the - Inspect Asset twin of the Project Audit results header. The headline's AspidLabel drives its own colour (amber when - the graph carries missing / orphaned references, green when every type resolves) and its divider; the --hidden - modifier drops the whole block in the empty / prefab-instance states. */ .aspid-fasttools-reference-graph__overview { flex-grow: 0; } @@ -153,10 +115,6 @@ white-space: normal; } -/* Accent legend under the hint — decodes the two card tones (amber = broken/orphaned/required, blue = pending - migration). Rendered only when both bands are on screen at once (see ShowOverview), matching the Project - References legend. Tucked into the hint's 10px gap: its own 10px bottom margin replaces the spacing to the - cards below. */ .aspid-fasttools-reference-graph__legend { flex-direction: row; align-items: center; @@ -174,8 +132,6 @@ margin-right: 16px; } -/* Same 6px dot idiom as the Welcome sample-state markers; the muted -text-dark swatches match the card sweeps - rather than the bright title text, keeping the key quieter than the cards it explains. */ .aspid-fasttools-reference-graph__legend-dot { width: 6px; height: 6px; @@ -192,16 +148,7 @@ color: var(--aspid-colors-text-dark); } -/* --- Per-document group --- */ -/*.aspid-fasttools-reference-graph__document {*/ -/* margin-bottom: 0px;*/ -/*}*/ - -/* The document header band, styled like the Project Audit group header: a clickable gradient row carrying the document - title + a reference count on the left and a collapse chevron on the right. It wears the same resting card surface as - the node cards below it (translucent dark fill + a hairline border) so the header reads at the same weight as its - group rather than floating transparent over the dotted canvas; an amber hover glow lights it when the document has - issues. Scoped under __document so these overrides outrank the gradient button's own :root defaults. */ +/* The document scope outranks the gradient button's :root layout rules. */ .aspid-fasttools-reference-graph__document .aspid-fasttools-reference-graph__document-header { flex-direction: row; @@ -216,21 +163,17 @@ --aspid-fasttools-colors-gradient_button-accent: var(--aspid-colors-text-lightness); } -/* A document carrying missing / orphaned references glows amber on hover, matching the broken-node accent. */ .aspid-fasttools-reference-graph__document .aspid-fasttools-reference-graph__document-header--issues { --aspid-fasttools-colors-gradient_button-accent: var(--aspid-colors-status-warning-text-light); } -/* The chevron (the button's own Text label) fills the gap left of it and right-aligns, pinning to the band's right. */ .aspid-fasttools-reference-graph__document-header .aspid-fasttools-gradient-button__label { flex-grow: 1; font-size: 11px; -unity-text-align: middle-right; } -/* Leading content: the document title + a dim reference count, hugging the left of the band, shrinkable so a long - title ellipsises rather than shoving the chevron off the band. */ .aspid-fasttools-reference-graph__document-header-row { flex-direction: row; align-items: center; @@ -247,12 +190,10 @@ font-size: 13px; } -/* The title turns amber when the document carries missing / orphaned references. */ .aspid-fasttools-reference-graph__document-header--issues .aspid-fasttools-reference-graph__document-title { color: var(--aspid-colors-status-warning-text-light); } -/* The dim reference-count subtitle beside the title. */ .aspid-fasttools-reference-graph__document-count { margin-left: 10px; flex-shrink: 0; @@ -261,13 +202,7 @@ font-size: 11px; } -/* --- Node cards --- - Each managed reference is its own card laid out over two lines, mirroring the Project Audit group header: a top band - carrying the stored type (an amber status pill when missing / orphaned) with the MISSING / SHARED badges beside it - and, for a missing node, the inline Fix dropdown docked right; a bottom line with the dim field path and the rid. - Translucent fill + 8px radius keep them in the window's card family; indentation (margin-left, set in code) carries - the tree depth. AspidBox would otherwise paint an opaque fill and a 10px --rounded radius; the compound selector - raises specificity over both that theme rule and the box's own `:root { flex-grow: 1; }`. */ +/* The compound selector outranks AspidBox's theme and :root layout rules. */ .aspid-fasttools-reference-graph__node.aspid-fasttools-background { flex-grow: 0; margin-bottom: 5px; @@ -278,46 +213,25 @@ background-color: rgba(20, 20, 20, 0.55); } -/* A missing reference is called out at rest by its amber type pill and the "Fix Missing" action alone — the card keeps - the neutral node border until its picker opens (see __node--picking below), so the resting stack stays calm rather - than framing every broken row in amber. */ - -/* Picker open: the whole card becomes the active surface — a warm amber frame (the same accent the type pill and Fix - action wear) lifts it off the dotted canvas while the welded selector sheds its own box (see __picker--attached), so - band, selector and meta line read as one active card. Mirrors the Project Audit group's --picking state. */ .aspid-fasttools-reference-graph__node--picking.aspid-fasttools-background { border-color: var(--aspid-colors-status-warning-shade-light); } -/* A migration card's active frame follows its own accent — the calm info blue, not the broken-card amber - (mirrors the Project References __group--migrate --picking frame). */ .aspid-fasttools-reference-graph__node--migrate.aspid-fasttools-reference-graph__node--picking.aspid-fasttools-background { border-color: var(--aspid-colors-status-info-shade-light); } -/* A back-edge leaf (a cycle) is a single dim line, no bottom row — a fainter fill and a dimmer border sink it behind - the live nodes. */ .aspid-fasttools-reference-graph__node--back-edge.aspid-fasttools-background { border-color: var(--aspid-colors-shade-dark); background-color: rgba(20, 20, 20, 0.35); } -/* An unassigned slot is now an editable dropdown (its "Assign ▼" band gives the empty field a type), so it keeps the - same fill and border as every live card — only its dim italic "" placeholder (below) sets it apart. No - background / border override here, so it inherits the base __node card surface and reads at the same weight as its - neighbours rather than sinking behind them. */ - -/* Its "" placeholder type reads dim and italic — it is a placeholder, not a real stored type. */ .aspid-fasttools-reference-graph__node--empty .aspid-fasttools-reference-graph__node-type { color: var(--aspid-colors-text-darkness); -unity-font-style: italic; } -/* The top band of an editable node: a type dropdown styled as a flat row (transparent gradient fill) — the type pill + - badges hug the left as leading content, the collapse chevron / label pins right. Neutral by default (a healthy / empty - card's plain "▼"); the --missing modifier repaints it amber for the missing card's "Fix Missing ▼". Padding/height - match the Project Audit group header band (__group-fix-all). Scoped under __node so these props outrank the gradient - button's own :root defaults (the layout overrides win on the class tie too). */ +/* The node scope outranks the gradient button's :root layout rules. */ .aspid-fasttools-reference-graph__node .aspid-fasttools-reference-graph__node-band { flex-direction: row; @@ -329,29 +243,21 @@ --aspid-fasttools-colors-gradient_button-accent: var(--aspid-colors-shade-lightness); } -/* A missing node's band wears the amber accent — it is a "fix this" call to action, not a routine value dropdown. Scoped - under __node to match (and override, by source order) the base band's accent at equal specificity. */ .aspid-fasttools-reference-graph__node .aspid-fasttools-reference-graph__node-band--missing { --aspid-fasttools-colors-gradient_button-accent: var(--aspid-colors-status-warning-text-light); } -/* The band's right-pinned label (the chevron, or "Fix Missing ▼") fills the gap left of it then right-aligns, pinning it - to the band's right edge. Dim by default so a healthy / empty card's chevron reads as a quiet dropdown affordance. - Font size is left to the gradient button's default so a missing band matches the Project Audit "Fix all" action. */ .aspid-fasttools-reference-graph__node-band .aspid-fasttools-gradient-button__label { flex-grow: 1; -unity-text-align: middle-right; color: var(--aspid-colors-text-dark); } -/* The missing band's "Fix Missing ▼" label takes the amber accent, completing its call-to-action read. */ .aspid-fasttools-reference-graph__node-band--missing .aspid-fasttools-gradient-button__label { color: var(--aspid-colors-status-warning-text-light); } -/* A pending-migration band wears the info accent instead: its "Fix ▼" is the calm escape hatch beside the Migrate - row — never the amber alarm, and not the dim value-dropdown grey of a healthy card either (matches the info pill). */ .aspid-fasttools-reference-graph__node .aspid-fasttools-reference-graph__node-band--migrate { --aspid-fasttools-colors-gradient_button-accent: var(--aspid-colors-status-info-text-light); @@ -361,12 +267,7 @@ color: var(--aspid-colors-status-info-text-light); } -/* --- Band divider + underline sweep --- - The Project References group cards' idiom carried over: a dim hairline separates the band from the card body, - and an accent sweep riding exactly ON that line scales in from the left while the band is hovered. The sweep is - the band's sibling, so :hover can't reach it — code mirrors the band's hover onto the card's --header-hover - modifier, which the sweep rule listens to. The band carries no bottom margin, so the divider's own top margin - holds the band-to-line gap. */ +/* USS cannot select the sibling sweep from button hover; code mirrors hover onto the card modifier. */ .aspid-fasttools-reference-graph__node-divider { flex-grow: 0; margin-top: 4px; @@ -374,9 +275,7 @@ opacity: 0.5; } -/* Pulled up past the divider's 4px bottom margin plus its 1px line so the sweep sits exactly ON the line; the 4px - bottom margin gives the swallowed gap back, keeping the body rhythm intact. Neutral by default (a healthy / - empty card's quiet dropdown); the modifiers repaint it in the card's accent. */ +/* The negative margin overlays the divider; the bottom margin restores the displaced gap. */ .aspid-fasttools-reference-graph__node-sweep { height: 1px; margin-top: -5px; @@ -389,12 +288,10 @@ transition-timing-function: ease-out; } -/* A missing / required band sweeps amber — the same "fix this" accent its band and type pill wear. */ .aspid-fasttools-reference-graph__node-sweep--missing { background-color: var(--aspid-colors-status-warning-text-dark); } -/* A migration card sweeps in its calm info tone — nothing on it is broken. */ .aspid-fasttools-reference-graph__node-sweep--migrate { background-color: var(--aspid-colors-status-info-text-dark); } @@ -403,18 +300,11 @@ scale: 1 1; } -/* Picker open: the dropdown is inserted between the band and the divider, which would strand the line and its - sweep under the whole selector — drop both while the card is picking. */ .aspid-fasttools-reference-graph__node--picking .aspid-fasttools-reference-graph__node-divider, .aspid-fasttools-reference-graph__node--picking .aspid-fasttools-reference-graph__node-sweep { display: none; } -/* Smart Fix / Migrate as a member of the row family: a bold, left-aligned accent verb over the same flat hover - fill as the Project References action rows, instead of a filled gradient pill floating over the glass card. - Each card keeps ONE accent: the Smart Fix verb stays in the broken card's warning amber (matching the inspector - notice's amber suggestion), and the --info modifier swaps in the migration card's calm info tone. Text lightens - with the fill on hover, both on the shared 0.25s ease-out beat. */ .aspid-fasttools-reference-graph__node-action { padding: 2px 4px; border-radius: 3px; @@ -439,17 +329,10 @@ color: var(--aspid-colors-status-info-text-light); } -/* --- Keyboard navigation --- - Arrow keys walk one flat focus ring over every actionable element in the graph (Rescan, each document header, - node band, action row and orphan Clear); Enter activates the highlighted one, Escape drops the highlight. The - highlight mirrors each element's own hover treatment so keyboard focus and mouse hover read as one state: the - plain action rows take the flat hover fill below; the gradient-button ring members never carry the focused - class at all — SetNavFocus drives their hover in code through AspidGradientButton.Highlighted. */ .aspid-fasttools-reference-graph__nav-target--focused { background-color: var(--aspid-colors-bg-dark); } -/* The action rows also lighten their accent text on hover — mirror that for the keyboard highlight too. */ .aspid-fasttools-reference-graph__node-action.aspid-fasttools-reference-graph__nav-target--focused { color: var(--aspid-colors-status-warning-text-light); } @@ -458,44 +341,32 @@ color: var(--aspid-colors-status-info-text-light); } -/* The band's leading content (and the non-missing top row): the type pill + badges hugging the left, shrinkable so a - long type ellipsises rather than shoving the Fix action off the band. */ .aspid-fasttools-reference-graph__node-band-row { flex-direction: row; align-items: center; flex-shrink: 1; } -/* Header line of a back-edge leaf (its only line) — a plain row holding the dim "↩ rid N" label. */ .aspid-fasttools-reference-graph__node-header { flex-direction: row; align-items: center; } -/* The stored type short name. An Aspid status label drives its colour (amber pill when missing / orphaned, light text - otherwise), so this only constrains layout. */ .aspid-fasttools-reference-graph__node-type { flex-shrink: 1; } -/* A back-edge leaf (a cycle pointing at a rid already on the render path) reads dim and italic. */ .aspid-fasttools-reference-graph__node--back-edge .aspid-fasttools-reference-graph__node-type { color: var(--aspid-colors-text-darkness); -unity-font-style: italic; } -/* Bottom line: the field path, then the rid (+ an orphan's Clear), below the band's divider (which carries the - vertical gap). Horizontal padding matches the band's 4px so the path lines up under the type pill. */ .aspid-fasttools-reference-graph__node-footer { flex-direction: row; align-items: center; padding: 0 4px; } -/* The field/element path the reference sits under — a dim italic lead-in that grows so the rid pins to the right. - min-width:0 lets it shrink below its content's intrinsic width; a long path stays on one line with its START - elided, so the leaf field — the part that identifies the slot — survives the cut (matches the Project References - entry paths). Footer text is selectable for copying; the I-beam signals that. */ .aspid-fasttools-reference-graph__node-root-label { flex-grow: 1; flex-shrink: 1; @@ -509,7 +380,6 @@ cursor: text; } -/* The rid: a dim, small subtitle on the right of the bottom line. Selectable for copying, like the path. */ .aspid-fasttools-reference-graph__node-rid { flex-shrink: 0; color: var(--aspid-colors-text-dark); @@ -518,9 +388,6 @@ cursor: text; } -/* --- Badges --- - Sit on the top band after the type pill (where the Project Audit header's count sits); the left margin spaces them - off it. */ .aspid-fasttools-reference-graph__node-badges { flex-direction: row; align-items: center; @@ -548,7 +415,6 @@ background-color: var(--aspid-colors-status-info-dark); } -/* The deterministic per-rid colour chip beside the SHARED badge (its background colour is set inline from code). */ .aspid-fasttools-reference-graph__chip { width: 8px; height: 8px; @@ -556,10 +422,6 @@ border-radius: 4px; } -/* --- Orphaned group --- - A warning-accented sibling of the node cards: a faint warm translucent fill (echoing the warning palette, like the - Project Audit summary's tinted block) under an amber border and an 8px radius, so the orphan region reads as a - warning zone over the dotted canvas without going opaque. The amber header carries the rest of the status read. */ .aspid-fasttools-reference-graph__orphan-group.aspid-fasttools-background { flex-grow: 0; margin-top: 6px; @@ -574,8 +436,6 @@ margin-bottom: 4px; } -/* Each orphan's Clear action sits on its card's footer line after the rid / Fix, warm-tinted to match the group; the - left margin spaces it off whatever precedes it. */ .aspid-fasttools-reference-graph__clear-orphan { margin: 0 0 0 8px; padding: 2px 6px; @@ -589,12 +449,7 @@ -unity-text-align: middle-center; } - -/* --- Inline type picker (mirrors the Project Audit picker exactly) --- - The selector view expanded inline, welded into the card being fixed (see TogglePicker): translucent dark fill, - neutral border, 8px radius and a fixed height (the ListView needs bounds to virtualize). padding:0 plus the header / - footer bleed rules below let the selector's full-bleed strips reach the box edges — the same treatment the Project - Audit picker uses, so the two selectors look identical. */ +/* The fixed height gives ListView the bounds required for virtualization. */ .aspid-fasttools-reference-graph__picker.aspid-fasttools-background { flex-grow: 0; height: 300px; @@ -607,54 +462,38 @@ background-color: rgba(20, 20, 20, 0.55); } -/* Welded under a node's Fix band: the selector sheds its own box (no border, radius or fill) so it dissolves into the - active card; the card's --picking accent frames the whole thing. */ .aspid-fasttools-reference-graph__picker--attached.aspid-fasttools-background { border-width: 0; border-radius: 0; background-color: rgba(0, 0, 0, 0); } -/* Bleed the selector's header strip to the picker box edges so it reads as a filled header bar, not an inset rectangle. */ .aspid-fasttools-reference-graph__picker .aspid-fasttools-type-selector__header { background-color: #00000000; margin: 0; margin-bottom: 5px; } -/* The boxless attached picker keeps the breadcrumb strip's rounded corners (the card frames it). */ .aspid-fasttools-reference-graph__picker--attached .aspid-fasttools-type-selector__header { border-radius: 8px; } -/* Bleed the selector's footer to the picker box edges, mirroring the header strip above. */ .aspid-fasttools-reference-graph__picker .aspid-fasttools-type-selector__footer { margin: 5px -7px 0; } -/* In the attached (boxless) picker the footer pulls level with the list as a rounded plate rather than a bar pasted on - the edge: the same 8px radius, no top divider. */ .aspid-fasttools-reference-graph__picker--attached .aspid-fasttools-type-selector__footer { margin: 5px 0 0 0; border-top-width: 0; border-radius: 8px; } -/* Match the Project Audit selector's row insets so the rows line up identically. The rows' visual body (the - fill-carrying content wrapper inside each transparent row shell) rounds and insets to match the window's card - idiom; the equivalent rule lives in the Project Audit stylesheet, which this window does not load. */ .aspid-fasttools-reference-graph__picker .aspid-fasttools-type-selector__item-content { border-radius: 8px; padding-left: 8px; } -/* The picker chrome follows the host card's accent, so the whole active card speaks one hue: the focused-search - frame, the hover cues (magnifier, navigable breadcrumbs) and the keyboard-selected row go warning amber on a - broken / required card, and the --migrate rules below swap all of them to the migration card's calm info tone - (the picker's own default). Selection is recoloured via the picker's row_bg VARIABLE, never background-color: - the divider row routes its fill through an inset backplate that reads that variable, so painting the property - directly would flood its divider zone. The compound selectors mirror the Project References rules verbatim — - they exist purely for specificity over the base stylesheet's own variable-setting selection rule. */ +/* Override row_bg rather than background-color so selection stays out of the divider zone; compound selectors outrank the base picker rules. */ .aspid-fasttools-reference-graph__node--picking .aspid-fasttools-type-selector__header--search-focused { border-color: var(--aspid-colors-status-warning-text-light); } @@ -667,7 +506,6 @@ color: var(--aspid-colors-status-warning-text-lightness); } -/* Hover rides the same accent one tone darker than selection, mirroring the base picker's hover/selection pair. */ .aspid-fasttools-reference-graph__node--picking .aspid-fasttools-type-selector .unity-collection-view__item.aspid-fasttools-type-selector__item:hover { --aspid-fasttools-colors-type_selector-row_bg: var(--aspid-colors-status-warning-shade-darkness); @@ -682,8 +520,6 @@ --aspid-fasttools-colors-type_selector-row_bg: var(--aspid-colors-status-warning-shade-dark); } -/* A migration card also wears --picking while its picker is open, so these sit after the amber cues above and - swap them back to the card's info accent. */ .aspid-fasttools-reference-graph__node--migrate .aspid-fasttools-type-selector__header--search-focused { border-color: var(--aspid-colors-status-info-text-light); } diff --git a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Resources/UI/SerializeReferences/Aspid-FastTools-SerializeReference-ExcludedFolders.uss b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Resources/UI/SerializeReferences/Aspid-FastTools-SerializeReference-ExcludedFolders.uss index 35e5a95b..b97ded65 100644 --- a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Resources/UI/SerializeReferences/Aspid-FastTools-SerializeReference-ExcludedFolders.uss +++ b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Resources/UI/SerializeReferences/Aspid-FastTools-SerializeReference-ExcludedFolders.uss @@ -1,21 +1,9 @@ -/* --- Excluded scan folders list --- - Replaces the multiline "one path per line" TextField with a compact add/remove list. A flat member of its settings - section card — no frame or fill of its own (the old panel read as a box-in-box inside the glass card): a header row - (caption + dim status hint + a "+" pinned right) over one indented flat row per excluded folder. The header is the - add target and washes green on hover; folder rows lift neutrally and wash red over their ✕ (delete intent). Self- - styled from the Aspid palette so it reads the same on the in-window Settings tab and the Preferences page; on the - branded surfaces the settings sheet adds the storage-scope stripe down the whole control's left edge. */ .aspid-fasttools-excluded-folders { margin-bottom: 2px; } -/* The header row — the flat action-row idiom (the settings rows' geometry): caption left, dim hint and the "+" glyph - pinned right. The whole row is the add click target; its add state (--add, code-toggled so the keyboard ring can - mirror it) lifts the row with the same neutral fill as every other settings row — the green add intent lives on - the "+" glyph alone. */ .aspid-fasttools-excluded-folders__header { - /* Starts 9px in — past the control's scope stripe (3px at the root's edge) plus 6px of clear air — so the - header's lit fill never touches the line; matches the settings rows' backplate inset. */ + margin-left: 9px; flex-direction: row; align-items: center; @@ -31,21 +19,18 @@ background-color: var(--aspid-colors-bg-dark); } -/* The caption mirrors the settings rows' captions (same tone, filling the row so the hint and "+" pin right). */ .aspid-fasttools-excluded-folders__header-caption { flex-grow: 1; color: var(--aspid-colors-text-light); -unity-text-align: middle-left; } -/* Dim status hint beside the "+" while no folder is excluded; empty otherwise. */ .aspid-fasttools-excluded-folders__hint { margin-right: 6px; color: var(--aspid-colors-text-darkness); -unity-text-align: middle-right; } -/* The "+" add glyph, pinned to the right of the header row: a small flat square that leads the row's green wash. */ .aspid-fasttools-excluded-folders__add { flex-shrink: 0; width: 18px; @@ -60,18 +45,13 @@ -unity-text-align: middle-center; } -/* While the header is in its add state (hovered or keyboard-focused), the "+" brightens to the signature green so - the glyph leads the green wash. */ .aspid-fasttools-excluded-folders__header--add .aspid-fasttools-excluded-folders__add { color: var(--aspid-colors-status-success-text-light); } -/* Each folder row: a flat indented member under the header (the indent tucks the paths under the caption), the same - radius and 0.25s hover beat as the settings rows. The full-row tints are toggled from code (USS can't repaint a row - from a child's hover state); exactly one is ever set at a time. Neutral lift signals the row is editable; the row - washes red while its ✕ is hovered (delete intent) — the quarter-alpha wash of the settings danger hovers. */ +/* Code mirrors child hover onto the row because USS cannot select a parent from child hover. */ .aspid-fasttools-excluded-folders__entry { - /* The same 9px stripe inset as the header, so a lit folder row stays clear of the line too. */ + margin-left: 9px; flex-direction: row; align-items: center; @@ -91,9 +71,6 @@ background-color: rgba(125, 35, 35, 0.25); } -/* min-width:0 lets a long path shrink and ellipsize instead of shoving the remove button off the row; stretching it - to the full row height (with the text kept vertically centred) makes the whole left of the row a click target for - editing, with no dead band above or below the caption. */ .aspid-fasttools-excluded-folders__path { flex-grow: 1; flex-shrink: 1; @@ -106,8 +83,6 @@ -unity-text-align: middle-left; } -/* The ✕ remove: a small flat square, dim at rest; it brightens to the error tone while its row is in the delete - state (see the --danger descendant rule below). */ .aspid-fasttools-excluded-folders__remove { flex-shrink: 0; width: 18px; @@ -122,8 +97,6 @@ -unity-text-align: middle-center; } -/* While the row is in its delete state (its ✕ hovered), the cross brightens to a hotter red box than the row wash, - marking it as the focal delete target. */ .aspid-fasttools-excluded-folders__entry--danger .aspid-fasttools-excluded-folders__remove { background-color: var(--aspid-colors-status-error-light); color: var(--aspid-colors-status-error-text-light); diff --git a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Resources/UI/SerializeReferences/Aspid-FastTools-SerializeReference-Window.uss b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Resources/UI/SerializeReferences/Aspid-FastTools-SerializeReference-Window.uss index 407c976e..9340fbdc 100644 --- a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Resources/UI/SerializeReferences/Aspid-FastTools-SerializeReference-Window.uss +++ b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Resources/UI/SerializeReferences/Aspid-FastTools-SerializeReference-Window.uss @@ -1,31 +1,16 @@ -/* --- Managed References window chrome --- - The shell hosting the two modes (Inspect Asset / Project Audit). One animated dotted canvas, owned by the window, - fills the whole window behind everything; the toolbar and the mode content are transparent and float over it, so - the dots read continuously from the tab strip down through the content. The active mode is marked by a bright, - bold label and a signature green underline (a child bar, not a border-bottom — see below). */ .aspid-fasttools-serialize-reference-window { flex-grow: 1; } -/* Docked-window floor for the mode content. - EditorWindow.minSize (480×360) only bounds the window while it floats; once docked into a column its - rootVisualElement ignores it and can be squeezed far below that, collapsing the cards into character-wrapped - text and vertical labels. Squeezing that far can't be prevented in a dock, so instead of letting the content - crush, the container that hosts the active mode gets a hard width floor: below it the content stops shrinking - and slides off the right edge (clipped by the window) rather than breaking. The toolbar and footer are left - flexible on purpose so their tabs/links stay reachable in a narrow dock — only the content is floored. */ +/* EditorWindow.minSize does not constrain docked windows; this width floor prevents the mode content collapsing. */ .aspid-fasttools-serialize-reference-window__container { min-width: 340px; } -/* The shared dotted canvas: a black base over which the dots paint; it absolutely fills the window via its own - stylesheet, sitting behind the toolbar and the mode content. Its blob tint is driven from code per view state. */ .aspid-fasttools-serialize-reference-window__background { background-color: rgb(0, 0, 0); } -/* The tab strip is fully transparent so the dotted canvas shows through behind the tabs. A little top padding lifts - the tabs off the window's top edge instead of jamming them against it. */ .aspid-fasttools-serialize-reference-window__toolbar { flex-direction: row; flex-shrink: 0; @@ -33,10 +18,7 @@ background-color: rgba(0, 0, 0, 0); } -/* Each mode tab: a large, flat, full-flex button stripped of Unity's button chrome and of any fill, so it sits - directly on the canvas. The element-qualified selector outranks the built-in `.unity-button` rules. The small - horizontal margin opens a gap between the two tabs (and their underlines). The active underline rides a child bar - (see below), not a border-bottom, so it always repaints. */ +/* The element-qualified selector outranks Unity's built-in button chrome. */ Button.aspid-fasttools-serialize-reference-window__toolbar-button { flex-grow: 1; margin: 0 4px; @@ -50,29 +32,22 @@ Button.aspid-fasttools-serialize-reference-window__toolbar-button { -unity-text-align: middle-center; } -/* Hover on an inactive tab: a faint light wash (kept translucent so the dots still read) and a brighter label. */ Button.aspid-fasttools-serialize-reference-window__toolbar-button:hover { background-color: rgba(255, 255, 255, 0.05); color: var(--aspid-colors-text-light); } -/* Active tab: brightest label, bold weight. Placed after the base/hover rules so it wins at equal specificity. */ Button.aspid-fasttools-serialize-reference-window__toolbar-button--active { color: var(--aspid-colors-text-lightness); -unity-font-style: bold; } -/* The active tab also lifts on hover (same faint wash as inactive tabs) for consistent feedback, but keeps its - brightest label rather than dimming to the inactive-hover colour. */ Button.aspid-fasttools-serialize-reference-window__toolbar-button--active:hover { background-color: rgba(255, 255, 255, 0.05); color: var(--aspid-colors-text-lightness); } -/* Underline accent: a 2px child bar pinned to the tab's bottom edge. Toggling a child's background-color via the - parent's --active class repaints reliably, whereas flipping a border-bottom-color did not redraw until the next - relayout (the underline only appeared after a window resize). Inactive tabs keep only a near-invisible - baseline (the card-border tone) so the active tab's signature green owns the hierarchy. */ +/* A child background repaints on tab changes; border-bottom-color changes can remain stale until relayout. */ .aspid-fasttools-serialize-reference-window__tab-underline { position: absolute; left: 0; @@ -86,10 +61,6 @@ Button.aspid-fasttools-serialize-reference-window__toolbar-button--active .aspid background-color: var(--aspid-colors-status-success-text-light); } -/* The tab-switch shortcut hint (⌥2 / Alt+2): plain dim text pinned to the tab's right edge — no cap/frame. Absolutely - positioned and vertically centred (top:50% + translate) so it floats over the button's centred label without - shifting it — the same float-over-content approach as the underline bar. Dim at rest so it reads as a hint, not a - competing label. picking-mode is set to ignore in code so it never intercepts the tab's clicks. */ .aspid-fasttools-serialize-reference-window__tab-hint { position: absolute; right: 8px; @@ -100,16 +71,10 @@ Button.aspid-fasttools-serialize-reference-window__toolbar-button--active .aspid -unity-text-align: middle-center; } -/* On the active tab the hint brightens in step with the label, so it stays legible against the bolder active text. */ Button.aspid-fasttools-serialize-reference-window__toolbar-button--active .aspid-fasttools-serialize-reference-window__tab-hint { color: var(--aspid-colors-text-light); } -/* The edge tabs (home on the left, settings on the right) are square and icon-only, so they opt out of the flex sizing - the label tabs use (flex-grow:1): a fixed-width square that stretches to the strip's full height, so the bottom - underline bar lines up with the mode tabs' underlines. No label and no padding — the glyph lives on an inner - __tab-icon element, leaving the button's own background free for the shared hover wash. The extra --square class - outranks the base toolbar-button rule. */ Button.aspid-fasttools-serialize-reference-window__toolbar-button--square { flex-grow: 0; flex-shrink: 0; @@ -120,9 +85,6 @@ Button.aspid-fasttools-serialize-reference-window__toolbar-button--square { justify-content: center; } -/* The square-tab glyph: a white silhouette tinted per state. Dim grey at rest, matching the inactive label tone. - Sized close to the 30px tab so the icon reads clearly rather than looking lost. The actual image is supplied by a - per-glyph modifier (--home / --settings) so home and settings share every rule but the picture. */ .aspid-fasttools-serialize-reference-window__tab-icon { width: 24px; height: 24px; @@ -137,22 +99,15 @@ Button.aspid-fasttools-serialize-reference-window__toolbar-button--square { background-image: var(--aspid-icons-settings); } -/* Hover brightens the glyph; the faint background wash comes from the base :hover rule above. */ Button.aspid-fasttools-serialize-reference-window__toolbar-button--square:hover .aspid-fasttools-serialize-reference-window__tab-icon { -unity-background-image-tint-color: var(--aspid-colors-text-light); } -/* Active square tab: the glyph contour lights up the signature green (matching the bottom underline bar), so the - selected edge tab reads green just like the mode tabs' active accent. */ Button.aspid-fasttools-serialize-reference-window__toolbar-button--square.aspid-fasttools-serialize-reference-window__toolbar-button--active .aspid-fasttools-serialize-reference-window__tab-icon { -unity-background-image-tint-color: var(--aspid-colors-status-success-text-light); } -/* --- Scrollbar --- - Every vertical scroller in the window re-dressed from Unity's bright default into the window's quiet family: no - step buttons, no track — just a slim translucent thumb that brightens while the scroller is hovered. The settings - sheet mirrors these rules for the Preferences page, which lives outside this window. Scoped to the scroller so no - real slider (a base-slider too) is touched. */ +/* Scope base-slider rules to the scroller so ordinary sliders keep their geometry. */ .aspid-fasttools-serialize-reference-window .unity-scroller--vertical { width: 12px; margin: 0; @@ -165,8 +120,7 @@ Button.aspid-fasttools-serialize-reference-window__toolbar-button--square.aspid- display: none; } -/* With the step buttons gone, the slider re-pins to the scroller's full height (the default offsets reserve room - for them). */ +/* Reset offsets reserved for the removed scroller step buttons. */ .aspid-fasttools-serialize-reference-window .unity-scroller--vertical .unity-scroller__slider { margin: 0; padding: 0; @@ -181,8 +135,6 @@ Button.aspid-fasttools-serialize-reference-window__toolbar-button--square.aspid- background-color: rgba(0, 0, 0, 0); } -/* The thumb: slim, rounded, translucent white so it reads over both the bare canvas and the glass cards; hover - brightens it on the shared 0.25s beat. The default focus halo is dropped with the rest of the chrome. */ .aspid-fasttools-serialize-reference-window .unity-scroller--vertical .unity-base-slider__dragger { left: 3px; width: 6px; diff --git a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Resources/UI/SerializeReferences/Aspid-FastTools-SerializeReference.uss b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Resources/UI/SerializeReferences/Aspid-FastTools-SerializeReference.uss index e6747daa..65f1ffb7 100644 --- a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Resources/UI/SerializeReferences/Aspid-FastTools-SerializeReference.uss +++ b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Resources/UI/SerializeReferences/Aspid-FastTools-SerializeReference.uss @@ -2,14 +2,11 @@ margin: 0; } -/* Lay the foldout header out as a row and centre its items so the dropdown and the open-script - button line up vertically, mirroring the single-row SerializableType field. */ .aspid-fasttools-serialize-reference .unity-foldout__toggle { flex-direction: row; align-items: center; } -/* Keep the expand arrow at its natural width on the far left of the header. */ .aspid-fasttools-serialize-reference .unity-foldout__toggle > .unity-foldout__input { @@ -17,23 +14,16 @@ flex-shrink: 0; } -/* The type dropdown fills the header row up to the open-script button. Its left margin is set - in code to offset the arrow so the dropdown begins at the inspector value column. */ .aspid-fasttools-serialize-reference__dropdown { flex-grow: 1; } -/* Cancel the EnumField caption's built-in -2px left margin so the text indents from the box - border like SerializableType's field instead of hugging the edge. */ +/* Cancel the EnumField caption's built-in negative margin. */ .aspid-fasttools-serialize-reference__dropdown .unity-enum-field__text { margin-left: 0; } -/* Missing stored type: ellipsize the caption from the LEFT, so the class name — the informative tail of - "" — survives truncation instead of the namespace prefix, and tint it the same warning - amber as the stripe and the notice, so the broken value reads as part of one warning instead of looking healthy. - The overflow trio is spelled out because the caption is a bare TextElement carrying only the enum-field text - class — it never received Unity's base-popup-field ellipsis rules. */ +/* The bare TextElement does not inherit popup-field ellipsis rules, so all overflow properties are required here. */ .aspid-fasttools-serialize-reference__dropdown--missing .unity-enum-field__text { overflow: hidden; white-space: nowrap; @@ -42,10 +32,6 @@ color: var(--aspid-colors-status-warning-text-light); } -/* Collapse the expand arrow when there is nothing to expand: the reference is empty, or the assigned type declares - no serialized fields at all. display:none (not just visibility:hidden) so the arrow reserves NO space either, - letting the label slide left to hug the status stripe instead of trailing behind an empty arrow slot (matches the - IMGUI drawer's flat missing / required layout). */ .aspid-fasttools-serialize-reference--empty .unity-foldout__toggle .unity-foldout__checkmark, @@ -55,7 +41,6 @@ display: none; } -/* Open-script button mirrors the SerializableType drawer affordance. */ .aspid-fasttools-serialize-reference Button { padding: 0; min-width: 18px; @@ -75,22 +60,11 @@ background-image: resource("d_FolderOpened Icon"); } -/* Indent the foldout content (header arrow, notice, child fields) so the status stripe sits in a small left gutter - with breathing room on both sides instead of flush against the arrow and the notice text. Padding (not margin) so it - stacks on top of Unity's margin-based foldout nesting indent rather than overriding it — otherwise nested fields and - list elements lose their indent and their arrow slides back under the stripe. The stripe is an absolutely-positioned - sibling of the foldout, so this padding shifts only the content, not the stripe. Gated by --striped (set from code - only while a stripe is shown), so a field with no stripe keeps its natural position instead of a needless indent. */ +/* Use padding to preserve Unity's margin-based foldout nesting indent. */ .aspid-fasttools-serialize-reference--striped > .unity-foldout { padding-left: 8px; } -/* --- Status stripe --- - A 2 px absolutely-positioned bar near the left edge of the field root, flagging the field at a glance. Inset a little - from the edge and sitting in the gutter opened by the foldout's left padding above, so it clears both the inspector - edge and the content. Hidden (zero width) by default; the --active modifier reveals it. A missing type paints it the - warning amber via the --warning class; a shared reference paints it inline from code in that reference's per-rid - colour, matching the notice's swatch and tinted message (see __dot). */ .aspid-fasttools-serialize-reference__stripe { margin-left: 0; position: absolute; @@ -109,13 +83,6 @@ background-color: var(--aspid-colors-status-warning-text-light); } -/* --- Group-navigation pulse --- - The overlay the click-to-navigate pulse tints in the group's rid colour (animated inline from code, so no colour - here). It reuses the stripe's left anchor (left:2px pulled by the same -11px margin); the right edge starts at the - root's own (right:0) and is stretched further inline per flash — a negative `right` measured against the - InspectorElement — so the pulse spans from the stripe's line to the inspector's right edge as one band, whatever - the nesting depth. The root's own background could not reach either side: the stripe hangs left of the root in the - gutter and the root stops short of the inspector edge. Transparent (zero footprint) whenever not pulsing. */ .aspid-fasttools-serialize-reference__flash { position: absolute; left: 2px; @@ -125,31 +92,17 @@ bottom: 0; } -/* Pull the notices back into the stripe gutter to align with it — only meaningful while the gutter exists, so it is - gated by --striped too. Without a stripe (a required-only or plain field) the notices keep their natural position. */ .aspid-fasttools-serialize-reference--striped .aspid-fasttools-serialize-reference__notices { margin-left: -14px; } -/* --- Project Audit view --- - Transparent content floating over the window's shared dotted canvas (tinted by status from code — green when - clean, amber when something needs fixing). A single Scan Project action sits in a slim translucent panel at the - top-left; the old boxed header card was dropped since the active tab already names the mode. Below sits either a - centred hero for the terminal states (success = nothing to repair, info = canceled) or warning-accented group - cards, one per broken stored type, each with a bulk Fix all over its entry rows. */ -/* The scrolled content. flex-grow lets it stretch to the viewport when the results are short (so the empty hero - centres); flex-shrink:0 stops the scroll's content-container from compressing it when it overflows (UIToolkit's - flex-shrink defaults to 1, which would squash the panel and group cards instead of scrolling) — the pair gives - "fill when small, scroll when tall". */ +/* Prevent flex compression so tall content scrolls instead of collapsing its cards. */ .aspid-fasttools-repair-references__content { flex-grow: 1; flex-shrink: 0; padding: 12px; } -/* Full-width translucent header panel, stacked: the audit's title + one-line description, then a full-width Scan - Project button below. The semi-transparent dark fill lets the window's dotted canvas read through. Replaces both - the old solid header card and the bare floating button. */ .aspid-fasttools-repair-references__panel { flex-direction: column; flex-shrink: 0; @@ -166,9 +119,7 @@ margin-bottom: 2px; } -/* The description carries the gap above the button (margin-bottom), so the button-to-description spacing matches the - button-to-panel-edge spacing (the panel's 12px bottom padding). Put on the plain Label here — not the button's - margin-top — because the gradient button's own :root margin would override an equal-specificity rule. */ +/* Space the plain label because the button's :root margin wins an equal-specificity rule. */ .aspid-fasttools-repair-references__panel-description { padding-left: 4px; color: var(--aspid-colors-text-dark); @@ -176,19 +127,13 @@ margin-bottom: 12px; } -/* Scan Project / Rescan sweeps every text asset under Assets/; it is the panel's header action, styled to read like - the group cards' "Fix all" header rows rather than a separate pill floating over the panel. The descendant selector - outranks the gradient button's own :root, so the panel zeros the button's margin and left padding — the label's left - edge lines up with the panel title / description above and the group type names / entry paths below — and clears the - gradient_button bg to transparent so no pill stands on the panel fill. The bold label plus the gradient button's own - accent hover (label tint + glow) carry the affordance, matching the flat group headers. */ +/* The descendant selector outranks the gradient button's :root spacing rules. */ .aspid-fasttools-repair-references__panel .aspid-fasttools-repair-references__scan-project { margin: 0; padding-left: 4px; } -/* Centred hero for the two terminal states: package icon in the status colour, headline, dimmed explanation. */ .aspid-fasttools-repair-references__empty { flex-grow: 1; padding: 20px; @@ -247,10 +192,6 @@ white-space: normal; } -/* Accent legend under the hint — decodes the two card tones (amber = broken/required, blue = pending migration). - Rendered only when both bands are on screen at once (see RenderGroups), so an all-amber or all-blue result list - never carries a key explaining a color it doesn't show. Tucked into the hint's 10px gap: its own 10px bottom - margin replaces the spacing to the cards below. */ .aspid-fasttools-repair-references__legend { flex-direction: row; align-items: center; @@ -268,8 +209,6 @@ margin-right: 16px; } -/* Same 6px dot idiom as the Welcome sample-state markers; the muted -text-dark swatches match the header sweeps - rather than the bright title text, keeping the key quieter than the cards it explains. */ .aspid-fasttools-repair-references__legend-dot { width: 6px; height: 6px; @@ -286,35 +225,18 @@ color: var(--aspid-colors-text-dark); } -/* One receipt per bulk Fix all, stacked in the summary list (newest at the bottom); the margin both spaces stacked - receipts apart and holds the gap from the last one to the group list / empty area below. The list container itself - has no styling — empty until the first fix, so it leaves no footprint then. */ .aspid-fasttools-repair-references__summary { margin-bottom: 10px; } -/* Pull the summary into the same translucent-dark family as the Scan panel and the group cards. The base help-box's - status state paints a solid bright fill, which read as a foreign block over the window's dotted canvas while every - other surface here is the rgba dark fill with status carried by accents. So keep that same fill and let the amber - read come from a muted warning border plus the help-box's own amber icon/text — not a loud surface. Scoped under - __results and qualified with the status class so it outranks the base :root.aspid-fasttools-status--warning rule the - help box attaches to itself. */ +/* The status-qualified selector outranks the help box's warning background rule. */ .aspid-fasttools-repair-references__results .aspid-fasttools-repair-references__summary.aspid-fasttools-status--warning { background-color: rgba(90, 65, 30, 0.25); border-color: var(--aspid-colors-status-warning-shade-dark); } -/* The receipt's Undo chip — reverts exactly that fix. Positioned absolute against the help box's top-right corner - (right matches its 12px padding so the right edge sits flush to the content edge) rather than sitting in the row's - flex flow. Out of flow, it reserves no width, so the help box's text container — the title with its full-width - dividing line, and the message below — stretches edge to edge instead of being cut short at the chip; the chip then - overlays the right end of the title line. Sized to the title band so it stays between the card's top border and the - title's dividing line (≈8px top padding + ~18px H5 title + 5px line margin) instead of overflowing past the line: - a fixed 20px height with the auto label height/3px vertical padding dropped, then nudged to top:6px to sit centred - in the band. font-size drops to 12px so the chip reads as a secondary action under the bold title rather than - matching its weight. Scoped under __summary (two classes) to outrank the gradient button's lone-class :root - height/padding/font/colour defaults. */ +/* The summary scope outranks the gradient button's :root dimensions and text rules. */ .aspid-fasttools-repair-references__summary .aspid-fasttools-repair-references__summary-undo { position: absolute; @@ -340,11 +262,7 @@ -unity-text-align: middle-center; } -/* One scroll wraps the whole view (the Find-missing panel + hero + results header/hint/summary + group list) so - everything between the window's tabs and footer scrolls as one — the panel is no longer pinned above a separately- - scrolling list. flex-grow fills the area between toolbar and footer; the content-container override lets the inner - content stretch to the viewport (UIToolkit's default flex-shrink:0 keeps it from collapsing when it overflows) so - the empty hero still centres while a long result list scrolls. */ +/* Growing the content container centers short content while taller content remains scrollable. */ .aspid-fasttools-repair-references__scroll { flex-grow: 1; } @@ -354,13 +272,7 @@ flex-shrink: 0; } -/* Inline type picker: the selector view expanded as an accordion full-width in the group card, below the header - row. A translucent fill (the cards'/panel's value) lets the window's dotted canvas read through it too, framed by - a neutral border and the card's 8px radius rather than the warning amber it used to carry. It composites over the - card's own translucent fill, so the open picker still sits a touch denser than the bare card — enough figure to - read as an active surface without going opaque. Fixed height — the selector's ListView needs bounds to virtualize. - Zero padding plus the header/footer bleed rules below let the selector's full-bleed strips reach the box border; - overflow keeps those strips inside the rounded corners. */ +/* The fixed height gives ListView the bounds required for virtualization. */ .aspid-fasttools-repair-references__picker.aspid-fasttools-background { flex-grow: 0; height: 300px; @@ -373,19 +285,12 @@ background-color: rgba(20, 20, 20, 0.55); } -/* Picker open under a group header (the card path): the selector sheds its own box — no border, no radius, no fill — - so it dissolves into the active card rather than sitting in a separate rounded box. Its internal breadcrumb strip, - list backing and footer still provide the structure; the card's accent frame (see __group--picking) now frames the - whole thing. Height/padding/margins stay inherited from the base rule so the strips' edge-bleed math still holds. */ .aspid-fasttools-repair-references__picker--attached.aspid-fasttools-background { border-width: 0; border-radius: 0; background-color: rgba(0, 0, 0, 0); } -/* Bleed the selector's header strip to the picker box edges (top + sides) so it reads as a filled header bar, not an - inset rectangle. With the box's zero padding, zero margins already reach the edges — only a 5px bottom gap to the - list remains; the picker's overflow:hidden + 8px radius round the header's top corners to match the box. */ .aspid-fasttools-repair-references__picker .aspid-fasttools-type-selector__header { background-color: #00000000; @@ -393,29 +298,16 @@ margin-bottom: 5px; } -/* The boxless attached picker keeps the breadcrumb strip's rounded corners (the group card's accent frame is what - boxes it now), so the strip reads as a rounded pill under the group header rather than a square band. */ .aspid-fasttools-repair-references__picker--attached .aspid-fasttools-type-selector__header { border-radius: 8px; } -/* The search field now lives inside the header strip and fills it via the base stylesheet's __search-field rule - (flex-grow), so the host no longer needs to stretch a root-level ToolbarSearchField. */ - -/* Bleed the selector's footer to the picker box edges (bottom + sides), mirroring the header strip above, with a 5px - gap to the list. */ .aspid-fasttools-repair-references__picker .aspid-fasttools-type-selector__footer { margin: 5px -7px 0; } -/* In the attached (boxless) picker the footer keeps its dark fill — the host rule above only nudges its margins — so - full-bleed it reads as a square strip welded across the rounded card. Pull it level with the header pill and list - panel instead: a rounded plate at the same 8px radius, inset to the list's width with a 5px gap above (the selector's - own 5px bottom padding holds the matching gap below) and no top divider — so header, list and footer read as one - rounded stack rather than a bar pasted onto the bottom edge. The base footer's opaque dark fill is kept: a - frame-only variant read too faint, an accent-tinted fill fought the list's accent states. */ .aspid-fasttools-repair-references__picker--attached .aspid-fasttools-type-selector__footer { margin: 5px 0 0 0; @@ -423,21 +315,11 @@ border-radius: 8px; } -/* The rows' visual body (the fill-carrying content wrapper inside each transparent row shell) rounds and insets - to match the window's card idiom. */ .aspid-fasttools-type-selector .aspid-fasttools-type-selector__item-content { border-radius: 8px; padding-left: 8px; } -/* --- Project-mode group cards --- - Each broken stored type becomes a card that shares the Scan panel's surface: a translucent dark fill so the - window's dotted canvas reads through, a neutral border and the panel's 8px radius (overriding AspidBox's opaque - darkness fill and its 10px --rounded). The warning read comes from the amber type header and the amber-tinted - dotted canvas behind it, not a warm border — so the cards belong to the same family as the Scan panel above them. - Layout: the whole header is one flat clickable row (the type name + entry/file counts on the left, the bulk - "Fix all (N) ▼" action on the right), a dim divider carrying the hover sweep, an optional Smart Fix / Migrate - action row, then a list of ping-only entry rows. */ .aspid-fasttools-repair-references__group.aspid-fasttools-background { flex-grow: 0; margin-bottom: 10px; @@ -448,33 +330,20 @@ background-color: rgba(20, 20, 20, 0.55); } -/* Picker open: the whole card becomes the active surface. A warm amber frame (tied to the broken-type header) and a - denser fill lift it off the dotted canvas, and the embedded selector drops its own box (see __picker--attached) so - header, selector and entry rows all read as one active card rather than a button stacked over a separate dropdown. */ .aspid-fasttools-repair-references__group--picking.aspid-fasttools-background { border-color: var(--aspid-colors-status-warning-shade-light); - /*background-color: rgba(20, 20, 20, 0.72);*/ + } -/* A migration card's active frame follows its own header accent — the calm info blue, not the broken-card amber. */ .aspid-fasttools-repair-references__group--migrate.aspid-fasttools-repair-references__group--picking.aspid-fasttools-background { border-color: var(--aspid-colors-status-info-shade-light); } -/* The picker chrome follows the host card's header accent too, so the whole active card speaks one hue: the - focused-search frame (no fill — just the border) and the keyboard-selected row go warning amber on a broken-type - card, and the --migrate rules below swap both to the migration card's calm info tone (the picker's own default). - Selection is recoloured via the picker's row_bg VARIABLE, never background-color: the divider row routes its - fill through an inset backplate that reads that variable, so painting the property directly would flood its - divider zone again. The rules carry the .aspid-fasttools-type-selector block class (and a :focus variant) - purely for specificity over the base stylesheet's own variable-setting selection rule, which loads deeper in - the hierarchy and would otherwise win the tie and keep the picker's default info blue. */ +/* Override row_bg rather than background-color to preserve the divider zone; the compound selectors outrank the base picker state rules. */ .aspid-fasttools-repair-references__group--picking .aspid-fasttools-type-selector__header--search-focused { border-color: var(--aspid-colors-status-warning-text-light); } -/* The header's hover cues (the magnifier, the navigable breadcrumbs) light up in the card's accent too, instead - of the picker's default info blue. */ .aspid-fasttools-repair-references__group--picking .aspid-fasttools-type-selector__search-button:hover { -unity-background-image-tint-color: var(--aspid-colors-status-warning-text-light); } @@ -482,10 +351,7 @@ .aspid-fasttools-repair-references__group--picking .aspid-fasttools-type-selector__breadcrumb--link:hover { color: var(--aspid-colors-status-warning-text-lightness); } -/* Hover rides the same accent one tone darker than selection, mirroring the base picker's hover/selection pair. - The extra __item compound (the class sits on the same shell) pushes each rule above every base-stylesheet - state rule; within the host, hover stays before the selected rules so a hovered selected row keeps the - stronger selection tone. */ +/* Keep hover before selection so a hovered selected row retains its selection color. */ .aspid-fasttools-repair-references__group--picking .aspid-fasttools-type-selector .unity-collection-view__item.aspid-fasttools-type-selector__item:hover { --aspid-fasttools-colors-type_selector-row_bg: var(--aspid-colors-status-warning-shade-darkness); @@ -504,8 +370,6 @@ border-color: var(--aspid-colors-status-info-text-light); } -/* A migration card also wears --picking while its picker is open, so these sit after the amber hover cues above - and swap them back to the card's info accent. */ .aspid-fasttools-repair-references__group--migrate .aspid-fasttools-type-selector__search-button:hover { -unity-background-image-tint-color: var(--aspid-colors-status-info-text-light); } @@ -527,15 +391,7 @@ --aspid-fasttools-colors-type_selector-row_bg: var(--aspid-colors-status-info-shade-dark); } -/* The header IS one clickable row, but flat — the gradient fill is set transparent so it matches the card surface - instead of reading as a pill. Its info line (type name + count) hugs the left and the "Fix all (N) ▼" action label - pins to the right. Horizontal padding matches the entry rows below (4px) so the type name lines up with the entry - paths and the action lines up with their rid column; height is freed to auto and margin-bottom holds the gap to the - rows / docked picker below. The accent only drives a subtle amber hover glow — the lone cue that the row is - interactive. - The selector is scoped under the card (.__group …) on purpose: the AspidGradientButton's own stylesheet sets these - same properties on :root, which ties on specificity with a lone class and wins, so the descendant selector raises - specificity to override its padding/height/margin (the gradient colour vars already cascade through). */ +/* The card scope outranks the gradient button's :root spacing rules. */ .aspid-fasttools-repair-references__group .aspid-fasttools-repair-references__group-fix-all { flex-direction: row; @@ -549,21 +405,16 @@ ); } -/* Picker open: drop the gap so the dropdown docks directly under the header row. */ .aspid-fasttools-repair-references__group--picking .aspid-fasttools-repair-references__group-fix-all { margin-bottom: 0; } -/* The "Fix all (N) ▼" action label: its own flex-grow fills the gap left of it, then the text right-aligns so the - action pins to the right edge of the header row (lining up with the entry rows' right-hand rid column). */ .aspid-fasttools-repair-references__group-fix-all .aspid-fasttools-gradient-button__label { -unity-text-align: middle-right; } -/* A pending-migration group's manual Fix all keeps the card's calm info tone — the amber accent above is the - "broken" alarm, and nothing on a migration card is broken (matches the info pill and the Migrate all row). */ .aspid-fasttools-repair-references__group .aspid-fasttools-repair-references__group-fix-all--migrate { --aspid-fasttools-colors-gradient_button-accent: var( @@ -571,12 +422,8 @@ ); } -/* --- Header divider + underline sweep --- - The Welcome sample cards' idiom carried over: a dim hairline separates the header row from the card body, and an - accent sweep riding exactly ON that line scales in from the left while the header button is hovered. The sweep is - the button's sibling, so :hover can't reach it — code mirrors the button's hover onto the card's --header-hover - modifier, which the sweep rule listens to. */ -/* Pulled 2px into the Fix all row's 6px bottom margin, so the header-to-line gap lands on Welcome's 4px. */ +/* USS cannot select the sibling sweep from button hover; code mirrors hover onto the card modifier. */ + .aspid-fasttools-repair-references__group-divider { flex-grow: 0; margin-top: -2px; @@ -584,9 +431,7 @@ opacity: 0.5; } -/* Pulled up past the divider's 4px bottom margin plus its 1px line so the sweep sits exactly ON the line; the 4px - bottom margin gives the swallowed gap back, keeping the body rhythm intact. Amber — the broken-type alarm, - matching the header's hover accent. */ +/* The negative margin overlays the divider; the bottom margin restores the displaced gap. */ .aspid-fasttools-repair-references__group-sweep { height: 1px; margin-top: -5px; @@ -599,7 +444,6 @@ transition-timing-function: ease-out; } -/* A migration card sweeps in its calm info tone — nothing on it is broken (matches its header/migrate accents). */ .aspid-fasttools-repair-references__group-sweep--migrate { background-color: var(--aspid-colors-status-info-text-dark); } @@ -608,22 +452,16 @@ scale: 1 1; } -/* Picker open: the dropdown is inserted between the header and the divider, which would strand the line and its - sweep under the whole selector — drop both while the card is picking. */ .aspid-fasttools-repair-references__group--picking .aspid-fasttools-repair-references__group-divider, .aspid-fasttools-repair-references__group--picking .aspid-fasttools-repair-references__group-sweep { display: none; } -/* The info line: the broken type name with the dimmed count beside it, hugging the left of the header row. */ .aspid-fasttools-repair-references__group-header-row { flex-direction: row; align-items: center; } -/* The Required-violations card has no Fix all button hosting its header row, so the static row carries the same - 4px horizontal padding and 6px gap the button gives the broken-type cards — keeping every card's header and - entry paths on one shared left edge. */ .aspid-fasttools-repair-references__group-header-row--static { padding: 4px 4px; margin-bottom: 6px; @@ -633,19 +471,12 @@ flex-shrink: 1; } -/* Dimmed "N entries · M files" count, sitting beside the type header. */ .aspid-fasttools-repair-references__group-count { margin-left: 10px; color: var(--aspid-colors-text-dark); -unity-font-style: normal; } -/* Smart Fix / Migrate all as a member of the entry-row family: a bold, left-aligned accent verb over the same - flat hover fill as the ping rows below it, instead of a filled gradient pill floating over the glass card. - Each card keeps ONE accent: the Smart Fix verb stays in the broken card's warning amber (matching the inspector - notice's amber suggestion), and the --info modifier swaps in the migration card's calm info tone — an - authoritative [MovedFrom] rename is a pending migration, not an alarm (and never an error: nothing is actually - broken for Unity). Text lightens with the fill on hover, both on the shared 0.25s ease-out beat. */ .aspid-fasttools-repair-references__group-action { padding: 2px 4px; border-radius: 3px; @@ -670,15 +501,13 @@ color: var(--aspid-colors-status-info-text-light); } -/* A single broken reference inside a group: asset path on the left, dim rid on the right; the whole row pings. - The path is start-elided to one line (see __group-entry-path), so the row is always single-line. */ .aspid-fasttools-repair-references__group-entry { flex-direction: row; align-items: center; padding: 2px 4px; border-radius: 3px; cursor: link; - /* The hover fill fades in and out on the window's shared 0.25s ease-out beat instead of snapping. */ + transition-property: background-color; transition-duration: 0.25s; transition-timing-function: ease-out; @@ -688,9 +517,6 @@ background-color: var(--aspid-colors-bg-dark); } -/* min-width:0 lets the path shrink below its content's intrinsic width (default min-width:auto would refuse), and a - long path stays on one line: the START is elided so the filename — the part that identifies the asset — survives - the cut. The full path lives in the tooltip and in selection-copy. */ .aspid-fasttools-repair-references__group-entry-path { flex-grow: 1; flex-shrink: 1; @@ -699,11 +525,10 @@ overflow: hidden; text-overflow: ellipsis; -unity-text-overflow-position: start; - /* Entry text is selectable for copying (paths are the payload); the I-beam signals that over the row's link cursor. */ + cursor: text; } -/* flex-shrink:0 keeps the rid at its natural width and pinned to the right however many lines the path wraps to. */ .aspid-fasttools-repair-references__group-entry-rid { flex-shrink: 0; margin-left: 10px; @@ -712,8 +537,6 @@ cursor: text; } -/* A required-violation row's "Component.field" column — same dim, right-pinned treatment as __group-entry-rid, one - column over for a different row shape (Required violations card has no rid). */ .aspid-fasttools-repair-references__group-entry-field { flex-shrink: 0; margin-left: 10px; @@ -722,19 +545,11 @@ cursor: text; } -/* --- Keyboard navigation --- - Arrow keys walk one flat focus ring over every actionable element in the audit (Rescan, each card's Fix all, - Smart Fix / Migrate rows, entry rows); Enter activates the highlighted one, Escape drops the highlight. The - highlight mirrors each element's own hover treatment so keyboard focus and mouse hover read as one state: - rows take the flat hover fill from the class below; the gradient-button ring members (Rescan, Fix all) never - carry the focused class at all — their hover is painted in code, and SetNavFocus drives it through - AspidGradientButton.Highlighted (the same accent overlay + label tint as a real mouse hover), because this - flat fill would show through their fading gradient as a gray pill. */ +/* Gradient buttons use their animated hover in code; applying this flat fill would show through the gradient. */ .aspid-fasttools-repair-references__nav-target--focused { background-color: var(--aspid-colors-bg-dark); } -/* The action rows also lighten their accent text on hover — mirror that for the keyboard highlight too. */ .aspid-fasttools-repair-references__group-action.aspid-fasttools-repair-references__nav-target--focused { color: var(--aspid-colors-status-warning-text-light); } @@ -743,7 +558,6 @@ color: var(--aspid-colors-status-info-text-light); } -/* A compatible MonoScript dragged over the field highlights the header as a valid drop target. */ .aspid-fasttools-serialize-reference--drop-target { background-color: var(--aspid-colors-bg-light); border-left-width: 2px; diff --git a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Resources/UI/Types/Aspid-FastTools-TypeSelector-View.uxml b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Resources/UI/Types/Aspid-FastTools-TypeSelector-View.uxml index 934367f7..c23acc4d 100644 --- a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Resources/UI/Types/Aspid-FastTools-TypeSelector-View.uxml +++ b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Resources/UI/Types/Aspid-FastTools-TypeSelector-View.uxml @@ -1,7 +1,5 @@ - + diff --git a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Resources/UI/Types/Aspid-FastTools-TypeSelector.uss b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Resources/UI/Types/Aspid-FastTools-TypeSelector.uss index d94a94fa..a31e759b 100644 --- a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Resources/UI/Types/Aspid-FastTools-TypeSelector.uss +++ b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Resources/UI/Types/Aspid-FastTools-TypeSelector.uss @@ -1,6 +1,3 @@ -/* row_bg: the per-row state fill (transparent at rest, hover grey, selection accent) — set by the state rules in - the Item section and painted by the row root / the divider row's backplate. Declared here so every row resolves - a value even at rest. */ .aspid-fasttools-type-selector { --aspid-fasttools-colors-type_selector-row_bg: rgba(0, 0, 0, 0); padding: 5px 2px; @@ -8,10 +5,6 @@ flex-direction: column; } -/* ---------------------------------------------------- Header ------------------------------------------------------ */ -/* Header strip: one neutral bar that carries either the breadcrumb trail + magnifier (resting) or the search field - (active). It bleeds to the box edges in the embedding hosts (the host stylesheet cancels the surrounding padding); - here it only owns its own fill and spacing. The mode swap is driven from code (display). */ .aspid-fasttools-type-selector__header { min-height: 20px; flex-shrink: 0; @@ -26,16 +19,10 @@ border-color: var(--aspid-colors-bg-dark); } -/* While the search field holds focus, the otherwise-invisible header border (it rests at the background colour) lights - up in the accent hue, framing the strip as the active command input. The modifier is toggled from code on focus - in/out; this is the only rule that gives that focus state any visual weight. */ .aspid-fasttools-type-selector__header--search-focused { border-color: var(--aspid-colors-status-info-text-light); } -/* The breadcrumb trail fills the strip and shrinks/ellipsises under pressure, leaving the magnifier pinned right. - Clicking anywhere on it but a navigable crumb opens the search field, so the strip carries the text caret cursor as - the "click to search" cue; the link crumbs override it back to the link cursor. */ .aspid-fasttools-type-selector__trail { flex-grow: 1; flex-shrink: 1; @@ -47,9 +34,6 @@ cursor: text; } -/* Magnifier button: the only search affordance in the resting header. Pinned to the left edge, ahead of the breadcrumb - trail. Flat and transparent so it reads as chrome, it lifts a step on hover; clicking it (or typing) swaps the trail - for the search field. */ .aspid-fasttools-type-selector__search-button { width: 11px; height: 11px; @@ -66,15 +50,8 @@ -unity-background-image-tint-color: var(--aspid-colors-status-info-text-light); } -/* The search field overlays the same header strip when active and fills it on every edge. Unity's ToolbarSearchField - otherwise keeps an intrinsic max-width that leaves the field narrower than the strip, so it is cleared and the field - grows into the whole row. align-self:stretch makes it span the header's full height (the header otherwise centers a - shorter field, leaving a gap above and below), and the negative margin cancels the header's 2px/8px padding so the - field bleeds flush to all four header edges (overflow:hidden trims it exactly at the border). */ -/* The field bleeds flush to the header edges; its own 1px rounded border would then sit right on the header's - overflow:hidden clip line and paint unevenly (clipped corners, half-pixel edges). So the field drops its frame and - fill entirely and blends into the header strip — the header itself becomes the search input (magnifier + caret + - text), command-palette style. Focus is conveyed by the caret; no separate box is drawn. */ +/* Clear ToolbarSearchField's intrinsic maximum width; negative margins cancel the surrounding header padding. */ +/* The header clips the field's border corners, so the header supplies the frame instead. */ .aspid-fasttools-type-selector__search-field { width: auto; max-width: none; @@ -87,14 +64,12 @@ background-color: rgba(0, 0, 0, 0); } -/* The inner text input stretches to fill the field so the typing area spans the strip, not just the field's frame. */ .aspid-fasttools-type-selector__search-field .unity-search-field-base__text-field { flex-grow: 1; max-width: none; } -/* Match the input to the frameless field: no border/radius/fill of its own, so the header background shows through. */ .aspid-fasttools-type-selector__search-field .unity-base-text-field__input { font-size: 12px; border-width: 0; @@ -102,8 +77,6 @@ background-color: rgba(0, 0, 0, 0); } -/* The built-in magnifier renders tiny (8x8) and hugs the left edge. Enlarge it and give it a left inset so it sits - with breathing room and reads at the weight of the list-row icons. */ .aspid-fasttools-type-selector__search-field .unity-search-field-base__search-button { width: 11px; @@ -115,7 +88,6 @@ ToolbarSearchField TextElement { -unity-font-style: bold; } -/* A breadcrumb crumb. Ancestors are dim and shrink/ellipsis under pressure; the current crumb is bright and pinned. */ .aspid-fasttools-type-selector__breadcrumb { font-size: 12px; -unity-font-style: bold; @@ -126,15 +98,10 @@ ToolbarSearchField TextElement { white-space: nowrap; } -/* The current crumb is bright; it still ellipsises under pressure (a flattened single-child namespace chain can be - very long) — its full text rides the tooltip so nothing is lost. */ .aspid-fasttools-type-selector__breadcrumb--current { color: var(--aspid-colors-text-lightness); } -/* Ancestor crumbs are navigable. At rest they sit in the trail's readable neutral (the base crumb colour); the accent - blue — the picker's interactive hue (search hover, focus frame) — is reserved for hover, so colour signals "you can - click this" only under the cursor. The current crumb is not a link and keeps its bright white. */ .aspid-fasttools-type-selector__breadcrumb--link { cursor: link; } @@ -148,16 +115,11 @@ ToolbarSearchField TextElement { flex-shrink: 0; color: var(--aspid-colors-text-dark); } -/* ------------------------------------------------------------------------------------------------------------------ */ -/* The list fills the space between the header and the footer hint; the empty-state hint takes its place when the - list is empty (both flex-grow, only one is shown at a time). */ .aspid-fasttools-type-selector .unity-list-view { flex-grow: 1; } -/* Inline error (e.g. a generic-argument constraint violation). Visibility is toggled in code; only the - error palette and spacing live here. */ .aspid-fasttools-type-selector__error { margin: 2px 4px; flex-shrink: 0; @@ -165,7 +127,6 @@ ToolbarSearchField TextElement { color: var(--aspid-colors-status-error-text-light); } -/* Centred stand-in shown when the list is empty (most often a search miss). */ .aspid-fasttools-type-selector__empty-hint { flex-grow: 1; padding: 20px; @@ -174,8 +135,6 @@ ToolbarSearchField TextElement { color: var(--aspid-colors-text-dark); } -/* Footer strip pinned to the bottom edge; bleeds to the box edges in embedding hosts. Pairs the keyboard - affordance (left) with the settings gear (right). */ .aspid-fasttools-type-selector__footer { margin: 5px -2px -5px; padding: 3px 8px; @@ -187,7 +146,6 @@ ToolbarSearchField TextElement { border-top-color: var(--aspid-colors-shade-darkness); } -/* The hint takes all footer width the gear leaves free and trims to an ellipsis rather than pushing the gear out. */ .aspid-fasttools-type-selector__footer-hint { flex-grow: 1; flex-shrink: 1; @@ -198,8 +156,6 @@ ToolbarSearchField TextElement { color: var(--aspid-colors-text-darkness); } -/* Settings gear: flat and transparent like the header magnifier, resting in the hint's muted tone so it reads as - chrome; hover lights the glyph up as the click affordance. Opens the Aspid FastTools window's Settings tab. */ .aspid-fasttools-type-selector__settings-button { width: 14px; height: 14px; @@ -217,15 +173,7 @@ ToolbarSearchField TextElement { -unity-background-image-tint-color: var(--aspid-colors-text-light); } -/* ------------------------------------------------------ Item ------------------------------------------------------ */ -/* Each row is a transparent SHELL (the collection-view item, which doubles as the makeItem root) holding one - __item-content wrapper that carries everything visual: the horizontal layout, the tight edge padding and the - state fill. The fill is carried by the --aspid-fasttools-colors-type_selector-row_bg variable — the hover and - selected rules SET it on the shell, the content wrapper PAINTS it (hosts recolour selection by overriding the - variable, never background-color). The split exists for the divider row: the ListView owns its items' box - (DynamicHeight slots rows by measured height, ignores their margins, and interferes with their padding), so - the divider zone is made of shell height alone — the shell grows and the content wrapper, pinned to its - bottom, stays an ordinary 22px row that hover/selection light up without ever flooding the divider's air. */ +/* ListView owns row height and ignores item margins; a taller transparent shell reserves divider space above the fill-carrying content. */ .aspid-fasttools-type-selector .unity-collection-view__item { height: 22px; padding: 0; @@ -234,8 +182,7 @@ ToolbarSearchField TextElement { flex-direction: column; } -/* The shell never paints — these mirror every state selector Unity's default theme fills at, so the stock grey - and selection blue can't leak through the transparent shell in any hover/selected/focused combination. */ +/* Clear every shell state to prevent Unity's default hover and selection fills leaking through. */ .aspid-fasttools-type-selector .unity-collection-view__item:hover, .aspid-fasttools-type-selector .unity-collection-view__item--selected, .aspid-fasttools-type-selector .unity-collection-view__item--selected:hover, @@ -243,7 +190,6 @@ ToolbarSearchField TextElement { background-color: rgba(0, 0, 0, 0); } -/* The row's visual body. The padding-left is kept tight so the rows sit close to the picker's left edge. */ .aspid-fasttools-type-selector__item-content { height: 22px; flex-shrink: 0; @@ -254,32 +200,21 @@ ToolbarSearchField TextElement { background-color: var(--aspid-fasttools-colors-type_selector-row_bg); } -/* Hover lifts the surface a dim step of the picker's interactive accent — the same hue as the selection fill - below, one tone darker, so hovered and selected read as two strengths of one accent instead of grey vs colour. */ .aspid-fasttools-type-selector .unity-collection-view__item:hover { --aspid-fasttools-colors-type_selector-row_bg: var(--aspid-colors-status-info-shade-darkness); } -/* The selected row wears the picker's interactive accent (the same info blue as the focused-search frame and the - link hovers) as a dark fill, so keyboard selection reads as "active" in the picker's own hue instead of a grey - step that washed out against hover. */ .aspid-fasttools-type-selector .unity-collection-view__item--selected, .aspid-fasttools-type-selector .unity-collection-view__item--selected:hover, .aspid-fasttools-type-selector .unity-collection-view:focus .unity-collection-view__item--selected { --aspid-fasttools-colors-type_selector-row_bg: var(--aspid-colors-status-info-shade-dark); } -/* The first ordinary root category after the pinned block (, Favorites, Recent) carries the divider zone - as extra shell height above its bottom-pinned content: 4px of air, the 1px line, 4px of air, then the usual - 22px row. The modifier is toggled in code on the shell. */ .aspid-fasttools-type-selector .unity-collection-view__item.aspid-fasttools-type-selector__item--after-pinned { height: 31px; } -/* The divider hairline: present in every row template, shown only on the row wearing --after-pinned. Absolutely - positioned in the divider zone (line at 4px: 4px of air to the pinned block above and to the content below), - so it takes no part in the row layout, ignores the content's border-radius and sits outside every fill. */ .aspid-fasttools-type-selector__item-divider { display: none; position: absolute; @@ -294,9 +229,6 @@ ToolbarSearchField TextElement { display: flex; } -/* The block class on the shell exists for the modifier grammar (--after-pinned, --in-section, --current) and - descendant hooks; the shell's own layout lives in the collection-view__item rules above, the horizontal row - layout in __item-content. */ .aspid-fasttools-type-selector__item { flex-grow: 1; } @@ -308,16 +240,10 @@ ToolbarSearchField TextElement { flex-shrink: 0; } -/* A collapsed section's identity icon (star / history clock) is dimmed; expanded it is full strength. With the foldout - arrow gone, this brightness shift is the open/closed cue for the group. */ .aspid-fasttools-type-selector__item-icon--collapsed { opacity: 0.4; } -/* Text-glyph alternative to the image icon (the circle, the section chevron). It mirrors the image icon's box - exactly — the same 14x14 leading slot, the glyph middle-centered inside it — so a font glyph centres by its box like - the icon does, instead of by a taller text line-box that left it riding above the caption. A font glyph stays crisp - at any size, so the font is sized up a touch to read at the icons' weight while the fixed box keeps it aligned. */ .aspid-fasttools-type-selector__item-glyph { width: 14px; height: 14px; @@ -333,9 +259,6 @@ ToolbarSearchField TextElement { flex-grow: 1; } -/* Marks the value the field currently holds (the matching type row, or when the field is empty). - Green is the "this is set" state colour — the picker's blue stays reserved for interaction — and the bold - caption keeps the row readable as the current value even where the check is easy to miss. */ .aspid-fasttools-type-selector__item-check { margin-left: 4px; flex-shrink: 0; @@ -349,9 +272,6 @@ ToolbarSearchField TextElement { -unity-font-style: bold; } -/* Dim right-aligned counter: how many pickable types a namespace holds (recursive) or how many rows a - Favorites/Recent section carries — most useful while a section is collapsed. Sits between the caption - and the drill-in arrow at the arrow's muted tone, so it reads as meta-information, not content. */ .aspid-fasttools-type-selector__item-count { margin-left: 4px; margin-right: 2px; @@ -364,11 +284,6 @@ ToolbarSearchField TextElement { color: var(--aspid-colors-text-dark); } -/* is a plain leaf row like any type — same label colour, same icon rail — distinguished only by its minus - glyph (bound in code) and its label, so it sits with the list instead of reading as a different kind of element. */ -/* ------------------------------------------------------------------------------------------------------------------ */ - -/* ----------------------------------------------- Favorites & Recents ---------------------------------------------- */ .aspid-fasttools-type-selector__favorite-toggle { margin: 0; padding: 0; @@ -383,20 +298,17 @@ ToolbarSearchField TextElement { opacity: 0; } -/* Reveal the star on row hover (or whenever the type is already favorited). */ .aspid-fasttools-type-selector__item:hover .aspid-fasttools-type-selector__favorite-toggle { opacity: 1; } -/* Reveal it on the keyboard-selected row too, so a keyboard user sees the star state and the Space-to-toggle target. */ .aspid-fasttools-type-selector .unity-collection-view__item--selected .aspid-fasttools-type-selector__favorite-toggle { opacity: 1; } -/* The gold star is the one place amber survives in the picker chrome — it is a favourite marker, not a warning. */ .aspid-fasttools-type-selector__favorite-toggle--favorite-on { opacity: 1; color: var(--aspid-colors-status-warning-text-light); @@ -406,25 +318,17 @@ ToolbarSearchField TextElement { color: var(--aspid-colors-status-warning-text-lightness); } -/* Section header (Favorites / Recents) reads as a normal row — same height, font size and background as the rest of - the list — set apart only by its leading collapse chevron, a bold weight and a slightly dimmer caption. No caps, no - shrunken font, no special fill, so the list stays visually uniform. */ .aspid-fasttools-type-selector__section-title { background-color: rgba(0, 0, 0, 0); cursor: link; } -/* Same caption colour as the rest of the list — the section is set apart only by its chevron and bold weight, not a - different hue. (It is navigable/selectable too; the wrapper carries the green accent bar when it is the active row.) */ .aspid-fasttools-type-selector__section-title .aspid-fasttools-type-selector__item-title { color: var(--aspid-colors-text-lightness); -unity-font-style: bold; } -/* Favorites/Recents item rows: a small indent so they read as nested under their header. The selection accent bar - (on the collection-view wrapper) is the only vertical line, so the rows carry no left rule of their own. */ .aspid-fasttools-type-selector__item--in-section { margin-left: 6px; } -/* ------------------------------------------------------------------------------------------------------------------ */ diff --git a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Resources/UI/Windows/Aspid-FastTools-Settings.uss b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Resources/UI/Windows/Aspid-FastTools-Settings.uss index 2cfbd5dc..325dcade 100644 --- a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Resources/UI/Windows/Aspid-FastTools-Settings.uss +++ b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Resources/UI/Windows/Aspid-FastTools-Settings.uss @@ -1,20 +1,8 @@ -/* --- Settings surface --- - The package's branded settings surface, shared by the SerializeReference window's Settings tab (SettingsView) and - the Preferences → Aspid FastTools page (AspidFastToolsPreferencesProvider) — class names and the resource path live - on AspidSettingsUI, which both surfaces compose from. The per-area controls are the branded AspidSwitch and - excluded-folders list (both self-styled) plus plain Unity fields — the Build / CI gate EnumField, the recents - SliderInt, the theme-override ObjectField — whose native bright fills would float oddly over the dark canvas, so - this sheet repaints them into the dark palette, groups each package area into a glass section card of flat rows, - and stripes every row by its storage scope. Every rule is scoped under the block class; the SerializeReference - Project Settings page never adds it and keeps its native Unity look. */ .aspid-fasttools-settings { padding: 12px 12px 0 12px; flex-grow: 1; } -/* --- Surface header --- - The audit tabs' results-header idiom: an underlined heading sitting on the bare canvas (no card of its own — the - line under the AspidLabel carries the header weight), a dim one-line description under it, then the scope legend. */ .aspid-fasttools-settings__header { margin-bottom: 12px; } @@ -28,10 +16,6 @@ white-space: normal; } -/* Section card — each package area (the first is "References") is one glass group card: the same translucent dark - fill, neutral hairline frame and 8px radius as the Project References group cards and the Scan panel, so the - window's dotted canvas reads through every card on every tab alike. The controls inside are flat rows, not nested - boxes. */ .aspid-fasttools-settings__section { margin-bottom: 10px; padding: 8px 10px; @@ -41,9 +25,6 @@ background-color: rgba(20, 20, 20, 0.55); } -/* Section title — the card's static header row: a bold bright caption whose 4px side padding lines it up with the - flat rows' content below (the group cards' static-header geometry). The dividing line under it is its own element - (__section-divider), not a border. */ .aspid-fasttools-settings__section-title { padding: 4px 4px; margin-bottom: 6px; @@ -52,9 +33,6 @@ -unity-font-style: bold; } -/* The dim hairline between the card's header and its rows — the group cards' header-divider geometry (pulled 2px - into the title's 6px bottom margin so the header-to-line gap lands on the shared 4px). No sweep rides it: a - settings header is static, there is nothing to hover. */ .aspid-fasttools-settings__section-divider { flex-grow: 0; margin-top: -2px; @@ -62,31 +40,19 @@ opacity: 0.5; } -/* The section body (__section-content) is a bare hook with no rules of its own — the card's padding and the title's - margin carry the spacing; it exists so the row selectors below can scope to a section's direct children. */ - -/* Each control row (caption + switch / dropdown / slider — plus the custom __row rows like the clear-saved-lists - actions) is a flat member of its section card — no box of its own, just the scope stripe on its left edge and a - flat hover fill (the entry-row idiom of the other tabs) so the eye can trace a wide row from its caption to its - control pinned on the right. Only a section's direct children are rows — a field nested inside another control - (the slider's inline value box is itself a TextField, i.e. a .unity-base-field) must not pick up the row chrome. */ +/* Style direct children only; nested TextFields such as slider value inputs must not receive row chrome. */ .aspid-fasttools-settings__section-content > .unity-base-field, .aspid-fasttools-settings__row { margin-left: 0; margin-right: 0; margin-bottom: 2px; - /* The 17px left inset = 3px stripe + 6px clear air + the backplate's 8px content inset, so captions sit 8px - inside the fill edge like any card content. */ + padding: 4px 4px 4px 17px; - /* Centre every row's caption against its control so the captions sit at one height across all rows. The switches - already centre via inline styles; this brings the EnumField row (which has none) onto the same baseline. */ + align-items: center; } -/* The backplate carrying a scoped row's hover / keyboard-focus fill (mounted by WithScopeStripe behind the row's - content): inset 9px past the stripe so a lit row never touches the line, rounded at the cards' full 8px. The fill - cannot live on the row itself — its box starts at the stripe — and the stripe cannot move out into a margin - gutter: some field types (AspidSwitch, EnumField) clip children to their bounds. */ +/* Keep the fill on an inset backplate: the stripe occupies the row edge, and some fields clip children to their bounds. */ .aspid-fasttools-settings__row-backplate { position: absolute; left: 9px; @@ -94,7 +60,7 @@ top: 0; bottom: 0; border-radius: 8px; - /* The hover lift fades in and out on the window's shared 0.25s ease-out beat instead of snapping. */ + transition-property: background-color; transition-duration: 0.25s; transition-timing-function: ease-out; @@ -105,10 +71,6 @@ background-color: var(--aspid-colors-bg-dark); } -/* The footer's reset row has no scope stripe (its two buttons act on different scopes), so no backplate is mounted — - its hover fill stays on the row itself — and the rows' 17px stripe gutter collapses away: the -4px margin cancels - the 4px text padding, landing the caption exactly on the header/card left grid while the hover fill keeps 4px of - air around the text. */ .aspid-fasttools-settings__footer .aspid-fasttools-settings__row { margin-left: -4px; padding-left: 4px; @@ -122,46 +84,31 @@ background-color: var(--aspid-colors-bg-dark); } -/* A row note — one dim line under a row surfacing what its tooltip would otherwise hide (the gate values' meaning, - what a team-wide toggle really does). The left padding lands the text on the rows' caption edge (3px scope stripe - + 8px inset); the negative top margin tucks it under its row, and the bottom margin re-opens the rhythm to the - next row. */ .aspid-fasttools-settings__row-note { margin-top: -1px; margin-bottom: 6px; - /* 17px = the rows' 9px stripe gutter + their 8px content inset, so the note sits on its row's caption edge. */ + padding-left: 17px; font-size: 11px; color: var(--aspid-colors-text-dark); white-space: normal; } -/* Custom (non-BaseField) rows lay out like the field rows: caption left, controls pinned right. */ .aspid-fasttools-settings__row { flex-direction: row; min-height: 32px; } -/* The custom row's caption mirrors .unity-base-field__label: light palette tone, filling the row so the controls pin - to the right edge. */ .aspid-fasttools-settings__row-caption { flex-grow: 1; color: var(--aspid-colors-text-light); -unity-text-align: middle-left; } -/* --- Storage-scope marking --- - The tab mixes settings persisted in the committed ProjectSettings asset (team-wide) with per-user EditorPrefs ones, - so every row wears a scope stripe on its left edge: green = shared with the team, blue = just this machine. The - stripe is a separate element mounted by WithScopeStripe — not a border-left, which would bend around the row's - corner radius — absolutely pinned to the row's left edge with a small vertical inset, so it stays a straight line - whatever the row's rounding, and the rows' left padding keeps a clear gap between the line and the content. The - scope utility classes are applied by the section builders; the stripes only paint inside this window — the Project - Settings page never loads this sheet. The legend in the surface header decodes the two colours. */ +/* A separate stripe stays straight where a border-left would follow the row corner radius. */ .aspid-fasttools-settings__scope-stripe { position: absolute; - /* Pinned inside the row's box (some field types clip children to their bounds, so it cannot hang outside); - the fill-carrying backplate starts 9px in, leaving 6px of clear air between the line and a lit row. */ + /* Keep the stripe inside the row bounds because some field types clip their children. */ left: 0; top: 3px; bottom: 3px; @@ -177,14 +124,10 @@ background-color: var(--aspid-colors-status-info-lightness); } -/* The scope legend: one dim line pairing each stripe colour with what it means for persistence. Lives in the surface - header, under the description — the reader meets the key before the first striped row. The 6px-dot idiom of the - window's other legends (the audit's amber/blue key, the Welcome sample markers), so every legend in the window - reads as one family. Items flow in a row and wrap on narrow windows. */ .aspid-fasttools-settings__legend { flex-direction: row; flex-wrap: wrap; - /* The 1px left margin optically lands the dots on the header text edge (the text has no side padding). */ + margin: 6px 0 0 1px; } @@ -194,8 +137,6 @@ margin-right: 16px; } -/* Each legend dot is painted by the same scope class the rows' stripes wear (the fill rules below), so the key and - the marking stay one system. */ .aspid-fasttools-settings__legend-swatch { width: 6px; height: 6px; @@ -216,9 +157,6 @@ color: var(--aspid-colors-text-dark); } -/* --- Action buttons (custom rows) --- - Small framed buttons pinned to a row's right edge, framed like the other settings inputs. The danger modifier gives - a destructive action (clearing a saved list) the red hover family instead of the signature green. */ .aspid-fasttools-settings__action { height: 22px; margin: 0 0 0 6px; @@ -228,7 +166,7 @@ border-color: var(--aspid-colors-shade-dark); border-radius: 8px; color: var(--aspid-colors-text-light); - /* The status wash (fill, frame and text together) fades on the same 0.25s ease-out beat as the row cards. */ + transition-property: background-color, border-color, color; transition-duration: 0.25s; transition-timing-function: ease-out; @@ -246,19 +184,12 @@ background-color: rgba(125, 35, 35, 0.25); } -/* An action tied to the per-user scope (the per-user reset) hovers in the scope's blue instead of the signature - green, mirroring how the shared action's green hover matches its stripe. */ .aspid-fasttools-settings__action--info:hover { border-color: var(--aspid-colors-status-info-text-dark); color: var(--aspid-colors-status-info-text-light); background-color: rgba(25, 75, 130, 0.25); } -/* --- Reset footer --- - Pinned under the scroll (a sibling of it, so it never scrolls away): a hairline over one action row that resets - each storage scope to its defaults. The buttons wear the same scope classes as the settings rows, so each carries - its scope's stripe on its left edge at rest. The row card keeps the tab's side inset but drops its stacking margin — - the footer's own padding spaces it. */ .aspid-fasttools-settings__footer { padding: 8px 0; border-top-width: 1px; @@ -269,13 +200,7 @@ margin-bottom: 0; } -/* --- Recent-items slider --- - The SliderInt row joins the family: caption and control split 50/50 like the EnumField row, a slim dark track with - a muted round handle (the switch handle's palette), and the drag-value text box framed like the other inputs. - Every slider rule is scoped under __section-content — NOT the surface root — because a ScrollView's scrollbar is - internally a .unity-base-slider too: root-scoped rules would repaint the tab's own vertical scroller into a stray - bright pill (the dragger fill + re-pinned geometry below). The scroller lives outside the section cards, so the - tighter scope reaches only the real settings slider. */ +/* Scope base-slider rules to section content; ScrollView scrollers also use this class. */ .aspid-fasttools-settings__section-content .unity-base-slider .unity-base-field__label { flex-grow: 1; flex-basis: 0; @@ -288,11 +213,7 @@ flex-basis: 0; } -/* Track and handle are re-sized from Unity's defaults, so their vertical geometry is re-pinned instead of inheriting - offsets tuned for the default sizes — otherwise the handle rides below the track's centre line. The track centres - cleanly with top:50% + a minus-half-height margin; the handle's top offset is partly forced by BaseSlider itself, - so its margin carries an extra -4px measured against the 22px input row to land the handle's centre exactly on the - track's (change either height and this needs re-measuring). */ +/* BaseSlider partly controls the handle offset; remeasure the compensating margin when track or handle height changes. */ .aspid-fasttools-settings__section-content .unity-base-slider__tracker { top: 50%; height: 4px; @@ -313,15 +234,11 @@ background-color: rgba(189, 189, 196, 0.85); } -/* Unity's separate focus halo (the dragger-border element) is sized for the default handle and pokes out from under - the smaller one as a stray blue disc — hide it; the handle carries the focus tint itself below. */ +/* Unity's default focus halo is larger than the resized handle; the handle supplies the focus tint instead. */ .aspid-fasttools-settings__section-content .unity-base-slider__dragger-border { display: none; } -/* Hovering the slider washes the handle's border in the muted signature green — the same accent family as the - switches, the dropdown and the folder list; keyboard focus pulls in the bright accent, replacing the hidden - default halo. */ .aspid-fasttools-settings__section-content .unity-base-slider:hover .unity-base-slider__dragger { border-color: var(--aspid-colors-status-success-shade-light); } @@ -330,10 +247,7 @@ border-color: var(--aspid-colors-status-success-text-light); } -/* The slider's inline value box: a fixed-size TextField (it must not stretch with the row) framed like the other - settings inputs instead of Unity's bright default field. The height is set on the TextField itself and mirrored on - its input — Unity sizes the slider's inline field shorter than our input, which otherwise clips the digit's - bottom against the frame. */ +/* Match the TextField and input heights to prevent Unity clipping the value text. */ .aspid-fasttools-settings__section-content .unity-base-slider__text-field { flex-grow: 0; flex-shrink: 0; @@ -357,18 +271,12 @@ border-color: var(--aspid-colors-status-success-text-light); } -/* Field captions read in the palette's light text tone, matching the window's other labels, and align to the row's - vertical centre so every caption — switch or dropdown — sits on the same height. */ .aspid-fasttools-settings .unity-base-field__label { color: var(--aspid-colors-text-light); -unity-text-align: middle-left; } -/* The Build / CI gate EnumField, branded to the window: a dark field framed by a neutral shade border with the - package's small radius, the caption text in the light palette tone, and the signature green washing in on hover and - when the popup is open — the same accent family as the switches and the folder list. EnumField derives from - BaseField, not BasePopupField, so its input box is `.unity-enum-field__input` — the base-popup class is listed - alongside so the rule holds across Unity versions. */ +/* Include both enum-field and base-popup input classes for the native field variants. */ .aspid-fasttools-settings .unity-enum-field__input, .aspid-fasttools-settings .unity-base-popup-field__input { min-height: 24px; @@ -383,8 +291,6 @@ color: var(--aspid-colors-text-lightness); } -/* Split the row evenly: the caption takes the left half, the dropdown the right half (flex-grow:1 + flex-basis:0 on - both shares the width 50/50) instead of the dropdown stretching the whole row. */ .aspid-fasttools-settings .unity-enum-field .unity-base-field__label { flex-grow: 1; flex-basis: 0; @@ -397,14 +303,11 @@ flex-basis: 0; } -/* The dropdown chevron rides in the dim palette tone at rest instead of Unity's bright default arrow. */ .aspid-fasttools-settings .unity-enum-field__arrow, .aspid-fasttools-settings .unity-base-popup-field__arrow { -unity-background-image-tint-color: var(--aspid-colors-text-dark); } -/* Hover washes the border to the muted signature green and brightens the chevron — the dropdown joins the same - green-accent family as the switches and the folder list. */ .aspid-fasttools-settings .unity-enum-field:hover .unity-enum-field__input, .aspid-fasttools-settings .unity-base-popup-field:hover .unity-base-popup-field__input { border-color: var(--aspid-colors-status-success-shade-light); @@ -415,15 +318,10 @@ -unity-background-image-tint-color: var(--aspid-colors-status-success-text-light); } -/* Open / focused: the border pulls in the bright signature green (the same accent as the active-tab underline) so the - live dropdown reads clearly. */ .aspid-fasttools-settings .unity-enum-field:focus .unity-enum-field__input { border-color: var(--aspid-colors-status-success-text-light); } -/* --- Theme-override ObjectField --- - The Appearance section's StyleSheet picker joins the family: caption and control split 50/50 like the EnumField - row, the input framed as the other dark inputs, with the same green hover wash. */ .aspid-fasttools-settings .unity-object-field .unity-base-field__label { flex-grow: 1; flex-basis: 0; @@ -450,7 +348,6 @@ border-color: var(--aspid-colors-status-success-shade-light); } -/* The object-picker button on the input's right edge, toned down to the dark family instead of Unity's bright chip. */ .aspid-fasttools-settings .unity-object-field__selector { background-color: rgba(255, 255, 255, 0.06); border-radius: 6px; @@ -460,21 +357,14 @@ background-color: rgba(255, 255, 255, 0.16); } -/* --- Keyboard navigation --- - Arrow keys walk one flat focus ring over every actionable element on the tab (switch rows, the gate dropdown, the - excluded-folders panel, the slider row, the action buttons); Enter activates the highlighted one, ←/→ nudge the - highlighted slider, Escape drops the highlight. The highlight mirrors each element's own hover treatment so - keyboard focus and mouse hover read as one state (the other tabs' nav idiom). Rows take the flat hover fill: */ .aspid-fasttools-settings__nav-target--focused > .aspid-fasttools-settings__row-backplate { background-color: var(--aspid-colors-bg-dark); } -/* The footer's reset row would take the flat fill directly if it ever joins the ring (it has no backplate). */ .aspid-fasttools-settings__footer .aspid-fasttools-settings__row.aspid-fasttools-settings__nav-target--focused { background-color: var(--aspid-colors-bg-dark); } -/* Action buttons mirror their own hover families — signature green, red for destructive, blue for per-user scope. */ .aspid-fasttools-settings__action.aspid-fasttools-settings__nav-target--focused { border-color: var(--aspid-colors-status-success-shade-light); color: var(--aspid-colors-status-success-text-light); @@ -493,10 +383,7 @@ background-color: rgba(25, 75, 130, 0.25); } -/* The excluded-folders control joins the ring per element: its add header highlights via the same neutral fill it - shows on mouse hover (Enter opens the add-folder picker) with the "+" brightening to green alongside, and each - folder row takes the flat neutral fill of its own hover (Enter re-picks the folder, Delete/Backspace removes it). - Two classes per rule outrank the control's self-styled base rules, whose sheet loads deeper. */ +/* Two classes outrank the control stylesheet loaded deeper in the hierarchy. */ .aspid-fasttools-excluded-folders__header.aspid-fasttools-settings__nav-target--focused { background-color: var(--aspid-colors-bg-dark); } @@ -510,12 +397,7 @@ background-color: var(--aspid-colors-bg-dark); } -/* --- Scrollbar --- - The surface's vertical scroller re-dressed from Unity's bright default into the window's quiet family: no - step buttons, no track — just a slim translucent thumb that brightens while the scroller is hovered. Mirrors the - window sheet's scroller rules (which cover the window's other tabs) so the Preferences page — which loads only - this sheet — scrolls in the same chrome. Scoped to the scroller so the Recent-items slider (a base-slider too, - scoped under __section-content) is untouched. */ +/* Scope base-slider rules to the scroller so the recent-items slider keeps its geometry. */ .aspid-fasttools-settings .unity-scroller--vertical { width: 12px; margin: 0; @@ -528,8 +410,7 @@ display: none; } -/* With the step buttons gone, the slider re-pins to the scroller's full height (the default offsets reserve room - for them). */ +/* Reset offsets reserved for the removed scroller step buttons. */ .aspid-fasttools-settings .unity-scroller--vertical .unity-scroller__slider { margin: 0; padding: 0; @@ -544,8 +425,6 @@ background-color: rgba(0, 0, 0, 0); } -/* The thumb: slim, rounded, translucent white so it reads over both the bare canvas and the glass cards; hover - brightens it on the shared 0.25s beat. The default focus halo is dropped with the rest of the chrome. */ .aspid-fasttools-settings .unity-scroller--vertical .unity-base-slider__dragger { left: 3px; width: 6px; @@ -565,11 +444,6 @@ display: none; } -/* --- Standalone canvas --- - The SerializeReference window owns its dotted canvas and the Settings tab floats over it; a surface hosted - anywhere else (the Preferences page) brings its own via this pair: the host fills the page and stacks the dots - component behind the surface, and the background class gives the dots the black base they paint over (mirroring - the window's __background rule). */ .aspid-fasttools-settings-canvas { flex-grow: 1; } diff --git a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Resources/UI/Windows/Welcome/Aspid-FastTools-Welcome.uss b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Resources/UI/Windows/Welcome/Aspid-FastTools-Welcome.uss index e58f9269..772a7cc1 100644 --- a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Resources/UI/Windows/Welcome/Aspid-FastTools-Welcome.uss +++ b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Resources/UI/Windows/Welcome/Aspid-FastTools-Welcome.uss @@ -2,8 +2,6 @@ flex-grow: 1; } -/* 12px matches the References tabs' content padding; inner sections keep vertical padding only so the - cards' side inset stays this single value across tabs. */ .aspid-fasttools-welcome__content { padding: 12px; flex-grow: 1; @@ -33,21 +31,17 @@ AspidAnimatedTitle { -unity-font-style: bold; } -/* Keep the animated gradient inside the brand green family: the red/amber stops read as status colors - everywhere else in the window, so the title cycles dark → light green instead of the logo's traffic-light - triplet. Scoped under __hero to outrank the component's own :root defaults on specificity. */ +/* The hero scope outranks the animated title's :root palette. */ .aspid-fasttools-welcome__hero AspidAnimatedTitle { --aspid-fasttools-colors-animated_title-color_1: var(--aspid-colors-status-success-text-darkness); --aspid-fasttools-colors-animated_title-color_2: var(--aspid-colors-status-success-text-dark); --aspid-fasttools-colors-animated_title-color_3: var(--aspid-colors-status-success-text-light); } -/* The hero blurb reads directly over the dotted canvas — no plate behind it. */ .aspid-fasttools-welcome__description { white-space: normal; } -/* Quick links under the blurb, styled like the footer's GitHub link: signature green, brighter on hover. */ .aspid-fasttools-welcome__hero-links { margin-top: 6px; flex-direction: row; @@ -77,8 +71,7 @@ AspidAnimatedTitle { padding: 10px 0; } -/* Sample cards share the References group-card surface: translucent dark fill, neutral border, 8px radius — - overriding AspidBox's opaque darkness fill and its 10px --rounded (the double class outranks the box's own rules). */ +/* The compound selector outranks AspidBox's theme and rounded-corner rules. */ .aspid-fasttools-welcome__sample.aspid-fasttools-background { flex-grow: 0; margin-bottom: 10px; @@ -94,10 +87,7 @@ AspidAnimatedTitle { align-items: center; } -/* The whole header line is one flat clickable button, mirroring the References "Fix all" row: the gradient - fill is set transparent so it matches the card surface and the accent only drives the soft full-width hover - glow. Scoped under __sample because the button's own stylesheet sets height/padding/margin on :root, which - ties with a lone class — the descendant selector raises specificity to override them. */ +/* The sample scope outranks the gradient button's :root spacing rules. */ .aspid-fasttools-welcome__sample .aspid-fasttools-welcome__sample-header { flex-grow: 1; flex-direction: row; @@ -108,18 +98,14 @@ AspidAnimatedTitle { --aspid-fasttools-colors-gradient_button-bg: rgba(0, 0, 0, 0); } -/* The action verb ("Import ▼") right-aligns inside the header button's flex-grow label. */ .aspid-fasttools-welcome__sample-header .aspid-fasttools-gradient-button__label { -unity-text-align: middle-right; } -/* An imported sample's header verb is "Remove" — a destructive direct action, so its hover glow goes error - instead of the default success accent. */ .aspid-fasttools-welcome__sample .aspid-fasttools-welcome__sample-header--remove { --aspid-fasttools-colors-gradient_button-accent: var(--aspid-colors-status-error-text-light); } -/* The title + status badge line living as the header button's leading content. */ .aspid-fasttools-welcome__sample-info { flex-direction: row; align-items: center; @@ -131,8 +117,6 @@ AspidAnimatedTitle { color: var(--aspid-colors-text-light); } -/* State marker ahead of the title: a single dot quoting the dotted canvas — the unread-marker idiom, brand - blue while the sample is still waiting to be imported, so fresh samples catch the eye. */ .aspid-fasttools-welcome__sample-state-dot { width: 6px; height: 6px; @@ -145,37 +129,28 @@ AspidAnimatedTitle { transition-timing-function: ease-out; } -/* Hovering the header verb turns the dot the Import accent green — "about to become imported". */ .aspid-fasttools-welcome__sample--header-hover .aspid-fasttools-welcome__sample-state-dot { background-color: var(--aspid-colors-status-success-text-dark); } -/* Once imported the dot stays green — the sample is in the project. */ .aspid-fasttools-welcome__sample-state-dot--imported { background-color: var(--aspid-colors-status-success-text-dark); } -/* An imported card's verb is Remove, so hovering it previews the destructive tone instead of the green - (equal specificity with the hover rule above — this one wins by coming later). */ .aspid-fasttools-welcome__sample--header-hover .aspid-fasttools-welcome__sample-state-dot--imported { background-color: var(--aspid-colors-status-error-text-dark); } - .aspid-fasttools-welcome__sample-divider { flex-grow: 0; margin-top: 4px; margin-bottom: 2px; } -/* The hover sweep: an accent hairline lying exactly on the divider line (pulled up over it by the negative - margin), scaling in from the left edge while the header button is hovered — the cards' echo of the tab - strip's active underline. Triggered by the --header-hover card modifier (mirrored from the button in code), - since the sweep is the button's sibling and :hover can't reach across. */ +/* USS cannot select the sibling sweep from button hover; code mirrors hover onto the card modifier. */ .aspid-fasttools-welcome__sample-sweep { height: 1px; - /* Pulled up past the divider's 2px bottom margin plus its 1px line so the sweep sits exactly ON the - line; the 2px bottom margin gives the swallowed gap back, keeping the description rhythm intact. */ + /* The negative margin overlays the divider; the bottom margin restores the displaced gap. */ margin-top: -3px; margin-bottom: 2px; background-color: var(--aspid-colors-status-success-text-dark); @@ -190,13 +165,10 @@ AspidAnimatedTitle { scale: 1 1; } -/* A Remove card sweeps in the destructive tone, matching its header accent. */ .aspid-fasttools-welcome__sample-sweep--remove { background-color: var(--aspid-colors-status-error-text-dark); } -/* Shares the header's 4px horizontal inset so the description's left edge lines up with the title above; - the 6px top margin (+2px divider margin) mirrors the 8px of air above the line. */ .aspid-fasttools-welcome__sample-description { margin-top: 6px; padding: 0 4px; diff --git a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/Enums/EnumValueIMGUIPropertyDrawer.cs b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/Enums/EnumValueIMGUIPropertyDrawer.cs index 32152eea..b42e6646 100644 --- a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/Enums/EnumValueIMGUIPropertyDrawer.cs +++ b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/Enums/EnumValueIMGUIPropertyDrawer.cs @@ -64,8 +64,6 @@ public static void Draw(Rect position, SerializedProperty property) } DrawKey(keyRect, keyProperty, enumTypeProperty); - - // GetHeight reserves room for the expanded children, so they must be drawn too. EditorGUI.PropertyField(valueRect, valueProperty, label, includeChildren: hasFoldout); } diff --git a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/Enums/EnumValueUIToolkitPropertyDrawer.cs b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/Enums/EnumValueUIToolkitPropertyDrawer.cs index 3359cc99..c9b9066d 100644 --- a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/Enums/EnumValueUIToolkitPropertyDrawer.cs +++ b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/Enums/EnumValueUIToolkitPropertyDrawer.cs @@ -54,8 +54,7 @@ public static VisualElement Draw(SerializedProperty property) if (!hasFoldout) root.AddClass(InlineClass); - // The parent drawer writes _enumType straight into the SerializedProperty, which a - // hidden bound PropertyField does not report; track the property itself instead. + // Track the serialized property because direct writes do not notify a hidden PropertyField. root.TrackPropertyValue(serializedObject.FindProperty(enumTypePath), _ => UpdateValue()); return root; @@ -81,7 +80,7 @@ void UpdateValue() if (EnumInfo.IsFlags(enumValue.GetType())) { - // EnumFlagsField's dropdown keeps choices from the previous enum type otherwise. + // Reset before initialization to discard the previous enum type's dropdown choices. keyEnumFlagField .SetValue(null, notify: false) .Initialize(enumValue) diff --git a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/Enums/EnumValuesIMGUIPropertyDrawer.cs b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/Enums/EnumValuesIMGUIPropertyDrawer.cs index e11272ca..a78643a9 100644 --- a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/Enums/EnumValuesIMGUIPropertyDrawer.cs +++ b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/Enums/EnumValuesIMGUIPropertyDrawer.cs @@ -70,7 +70,7 @@ public static void Draw(Rect position, GUIContent label, SerializedProperty prop headerBackgroundRect, serializedObject, valuesProperty.propertyPath, enumTypeProperty.propertyPath, defaultValueProperty.propertyPath); - // Foldout arrows render to the left of the supplied rect; keep them inside the border. + // Foldout arrows render left of the supplied rectangle; reserve space inside the border. var contentInset = inset + EnumValueIMGUIPropertyDrawer.FoldoutArrowWidth; var valuesHeight = EditorGUI.GetPropertyHeight(valuesProperty, includeChildren: true); diff --git a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/Enums/EnumValuesPropertyDrawer.cs b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/Enums/EnumValuesPropertyDrawer.cs index d56e52e3..874769e5 100644 --- a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/Enums/EnumValuesPropertyDrawer.cs +++ b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/Enums/EnumValuesPropertyDrawer.cs @@ -21,7 +21,7 @@ public override VisualElement CreatePropertyGUI(SerializedProperty property) => private bool IsTypedVariant() { - // fieldInfo is the array/list field when the drawer renders an element. + // For collection elements, fieldInfo describes the array or list itself. var type = fieldInfo.FieldType; if (type.IsArray) diff --git a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/Enums/EnumValuesPropertyDrawerHelper.cs b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/Enums/EnumValuesPropertyDrawerHelper.cs index f746d4a3..15de93b9 100644 --- a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/Enums/EnumValuesPropertyDrawerHelper.cs +++ b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/Enums/EnumValuesPropertyDrawerHelper.cs @@ -14,10 +14,6 @@ internal static class EnumValuesPropertyDrawerHelper { private const string PopulateMenuItem = "Populate Missing Enum Members"; - // Resolves the row's key against the configured enum type; null means the type is not - // an enum (or has no members) and the raw string field should be shown instead. A key - // that no longer matches any member falls back to the first one and is persisted, so a - // renamed enum migrates instead of leaving the row unusable. public static Enum? ResolveKey(SerializedProperty keyProperty, SerializedProperty enumTypeProperty) { var enumType = Type.GetType(enumTypeProperty.stringValue, throwOnError: false); @@ -39,7 +35,6 @@ internal static class EnumValuesPropertyDrawerHelper return enumValue; } - // Every entry mirrors the parent's _enumType so its own drawer can pick the right field. public static void SyncEntryEnumTypes(SerializedProperty values, SerializedProperty enumType) { var enumTypeValue = enumType.stringValue; @@ -118,7 +113,6 @@ private static void PopulateMissing( var existing = CollectExistingKeys(values); var added = false; - // For [Flags] enums this includes named composites (All = A | B) as separate rows. foreach (var name in Enum.GetNames(type)) { if (!existing.Add(name)) continue; diff --git a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/Enums/EnumValuesUIToolkitPropertyDrawer.cs b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/Enums/EnumValuesUIToolkitPropertyDrawer.cs index d91ff332..e11ee192 100644 --- a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/Enums/EnumValuesUIToolkitPropertyDrawer.cs +++ b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/Enums/EnumValuesUIToolkitPropertyDrawer.cs @@ -57,8 +57,7 @@ public static VisualElement Draw(SerializedProperty property, bool isTyped) .AddChild(new PropertyField(serializedObject.FindProperty(defaultValuePath))) ); - // The TypeSelector drawer writes straight into the SerializedProperty, which a - // PropertyField change callback never sees; track the property itself instead. + // Track the serialized property because direct writes do not notify PropertyField change callbacks. if (!isTyped) root.TrackPropertyValue(serializedObject.FindProperty(enumTypePath), _ => UpdateValues()); diff --git a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/Extensions/EditorExtensions.cs b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/Extensions/EditorExtensions.cs index d9d1383e..67974bf2 100644 --- a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/Extensions/EditorExtensions.cs +++ b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/Extensions/EditorExtensions.cs @@ -8,22 +8,15 @@ namespace Aspid.FastTools.Editors { /// - /// Editor-side extension methods for and its subclass - /// that resolve human-readable script names, respecting the attribute. + /// Provides extension methods for resolving Unity object display names. /// public static class EditorExtensions { /// - /// Returns a human-readable display name for the given Unity object. - /// If the object's type (or any of its base types) is decorated with , - /// the name is taken from , which honours the menu name; - /// otherwise it falls back to applied to the type name. + /// Returns the inspector title when an inherited exists, or the nicified type name. /// - /// The object whose display name should be resolved. - /// - /// The display name string, or if is - /// or has been destroyed. - /// + /// The object whose display name to resolve. + /// The display name; otherwise, if is or destroyed. public static string GetScriptName(this Object obj) { if (!obj) return string.Empty; @@ -35,18 +28,10 @@ public static string GetScriptName(this Object obj) } /// - /// Returns the display name of a component with a 1-based numeric suffix appended when multiple - /// components of the exact same type exist on the same . The index reflects - /// the order returned by . - /// For example, the second AudioSource on the object is returned as "Audio Source (2)". + /// Returns the component display name with a one-based suffix when its object has multiple components of the exact same type. /// - /// The component whose indexed display name should be resolved. - /// - /// The display name with an index suffix if duplicates exist on the same object, - /// the plain display name if there is only one such component, - /// or if is - /// or has been destroyed. - /// + /// The component whose indexed display name to resolve. + /// The display name, indexed in component order when duplicates exist; otherwise, if is or destroyed. public static string GetScriptNameWithIndex(this Component targetComponent) { if (!targetComponent) return null; diff --git a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/Extensions/TypeExtensions.cs b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/Extensions/TypeExtensions.cs index c1860376..40f68f0c 100644 --- a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/Extensions/TypeExtensions.cs +++ b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/Extensions/TypeExtensions.cs @@ -4,19 +4,8 @@ // ReSharper disable once CheckNamespace namespace Aspid.FastTools.Editors { - /// - /// Editor-side extension methods for shared across the editor tooling. - /// internal static class TypeExtensions { - /// - /// Unwraps the element type of array or ; - /// returns unchanged when it is neither. - /// - /// - /// is matched by its open definition, so a single-argument generic wrapper - /// is not mistaken for a collection and unwrapped by accident. - /// internal static Type GetCollectionElementTypeOrSelf(this Type type) { if (type.IsArray) return type.GetElementType(); diff --git a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/IMGUI/InspectorNoticeGUI.cs b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/IMGUI/InspectorNoticeGUI.cs index d6c333c4..6918b747 100644 --- a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/IMGUI/InspectorNoticeGUI.cs +++ b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/IMGUI/InspectorNoticeGUI.cs @@ -5,14 +5,12 @@ // ReSharper disable once CheckNamespace namespace Aspid.FastTools.Editors { - // IMGUI twin of InspectorNotice, so both inspector modes render the same notice rows. internal static class InspectorNoticeGUI { - // Mirrors the UIToolkit palette: --aspid-colors-status-warning-text-light / -lightness. + // Keep these colors aligned with --aspid-colors-status-warning-text-light / -lightness. internal static readonly Color NoticeColor = new(245f / 255f, 185f / 255f, 85f / 255f); internal static readonly Color NoticeColorHover = new(255f / 255f, 235f / 255f, 175f / 255f); - // The rid color is dynamic, so hover lightens it instead of applying a static USS brighten. private const float ActionHoverLighten = 0.35f; private const float DotSize = 8f; @@ -23,7 +21,6 @@ internal static class InspectorNoticeGUI private static GUIStyle _actionStyle; private static GUIStyle _infoMessageStyle; - // Non-actionable info row: icon plus a dim message whose full detail rides the tooltip. internal static void DrawInfoNotice(Rect rect, string message, string detail) { _infoMessageStyle ??= new GUIStyle(EditorStyles.label) { wordWrap = false }; @@ -38,10 +35,6 @@ internal static void DrawInfoNotice(Rect rect, string message, string detail) GUI.Label(messageRect, messageContent, _infoMessageStyle); } - // Single-row notice: a message, a right-pinned action word and an optional Smart Fix suggestion after it. - // Without ridColor the row is an amber warning with a triangle icon; with it the row is the shared-reference - // variant — a rid-colored swatch instead of the icon, message and action tinted that color so aliased fields - // match at a glance. onMessageClick makes the message itself clickable. internal static void DrawNotice(Rect rect, string message, string actionText, string detail, Action onClick, string suggestionText = null, string suggestionDetail = null, Action onSuggestion = null, Color? ridColor = null, Action onMessageClick = null) @@ -83,7 +76,7 @@ internal static void DrawNotice(Rect rect, string message, string actionText, st } else { - // The style is shared across notices — reset the tint a clickable message may have left behind. + // Reset the shared style tint left by a previously drawn clickable message. _messageStyle.hover.textColor = baseColor; GUI.Label(messageRect, messageContent, _messageStyle); } @@ -101,7 +94,6 @@ internal static void DrawNotice(Rect rect, string message, string actionText, st var separatorContent = hasSuggestion ? new GUIContent("·") : null; var separatorWidth = hasSuggestion ? _actionStyle.CalcSize(separatorContent).x : 0f; - // Pin the action cluster to the right edge, but never let it overlap the message. var clusterWidth = actionWidth + (hasSuggestion ? suggestionGap + separatorWidth + suggestionGap + suggestionWidth : 0f); var actionX = Mathf.Max(messageRect.xMax + 6f, rect.xMax - clusterWidth); @@ -110,7 +102,6 @@ internal static void DrawNotice(Rect rect, string message, string actionText, st if (hasSuggestion) { - // The separator is decoration, not an action — a plain label with no link affordance. _actionStyle.normal.textColor = baseColor; _actionStyle.hover.textColor = baseColor; GUI.Label(new Rect(actionX + actionWidth + suggestionGap, rect.y, separatorWidth, rect.height), @@ -133,13 +124,13 @@ private static void DrawLink(Rect linkRect, GUIContent content, Color color, Col EditorGUIUtility.AddCursorRect(linkRect, MouseCursor.Link); - // IMGUI rich text has no , so the underline is a hand-drawn 1px line. + // IMGUI rich text has no underline tag; draw the underline explicitly. EditorGUI.DrawRect(new Rect(linkRect.x + 1f, linkRect.yMax - 3f, linkRect.width - 2f, 1f), drawColor); if (GUI.Button(linkRect, content, _actionStyle)) onClick(); } - // IMGUI has no circle primitive, so the swatch is a tinted, fully rounded 1x1 white texture. + // IMGUI has no circle primitive; round a tinted white texture instead. private static void DrawDot(float x, Rect rect, Color color) { var dotRect = new Rect(x, rect.y + (rect.height - DotSize) * 0.5f, DotSize, DotSize); diff --git a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Build/SerializeReferenceBuildGate.cs b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Build/SerializeReferenceBuildGate.cs index 2b0f19cf..32d6b259 100644 --- a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Build/SerializeReferenceBuildGate.cs +++ b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Build/SerializeReferenceBuildGate.cs @@ -7,8 +7,6 @@ // ReSharper disable once CheckNamespace namespace Aspid.FastTools.SerializeReferences.Editors { - // Fails or warns a player build on missing managed-reference types, per the Project Settings gate severity. - // Only the fast pure-YAML missing-type scan runs here; required-field scanning is opt-in from the CI gate. internal sealed class SerializeReferenceBuildGate : IPreprocessBuildWithReport { public int callbackOrder => 0; diff --git a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Build/SerializeReferenceCiGate.cs b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Build/SerializeReferenceCiGate.cs index 48b77f25..18e9f112 100644 --- a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Build/SerializeReferenceCiGate.cs +++ b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Build/SerializeReferenceCiGate.cs @@ -9,13 +9,6 @@ // ReSharper disable once CheckNamespace namespace Aspid.FastTools.SerializeReferences.Editors { - // Headless CI entry point: - // Unity -batchmode -quit -projectPath . -executeMethod Aspid.FastTools.SerializeReferences.Editors.SerializeReferenceCiGate.RunCheck - // - // Scans the project, writes a report, logs each violation and exits non-zero so a pipeline can fail the job. - // Severity comes from the committed Project Settings, so a clean runner enforces the checked-in choice: Off skips, - // Warn logs but exits 0, Fail exits 1. Flags: -srGateReport , -srGateRequired (also scan required fields), - // -srGateWarnOnly and -srGateFail (override severity; -srGateWarnOnly wins). internal static class SerializeReferenceCiGate { private const string DefaultReportPath = "SerializeReferenceGateReport.txt"; diff --git a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Build/SerializeReferenceGateScanner.cs b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Build/SerializeReferenceGateScanner.cs index 8655b9d2..815ed2a0 100644 --- a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Build/SerializeReferenceGateScanner.cs +++ b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Build/SerializeReferenceGateScanner.cs @@ -7,9 +7,6 @@ // ReSharper disable once CheckNamespace namespace Aspid.FastTools.SerializeReferences.Editors { - // Window-free, headless-safe project scanner for managed-reference gate violations, shared by the build gate and - // the CI entry point. Missing types come from the pure-YAML scan. Required fields are checked per object for saved - // assets; scenes, which LoadAllAssetsAtPath cannot read, go through the pure-YAML scan instead. internal static class SerializeReferenceGateScanner { // Per-run memo of BuildConstraintMap (LoadAllAssetsAtPath + full SerializedObject walk — heavy), built only @@ -22,13 +19,11 @@ internal static class SerializeReferenceGateScanner private static readonly Dictionary> _scriptRequiredFieldsCache = new(StringComparer.Ordinal); - // Scans every candidate asset under Assets/ for the enabled checks. onProgress (fraction, label) may be null. public static IReadOnlyList Scan(GateOptions options, Action onProgress = null) { var violations = new List(); var paths = AssetDatabase.GetAllAssetPaths().Where(SerializeReferenceHelpers.IsScanCandidate).ToArray(); - // Cleared up front so a recompile between runs is never served stale. _scriptRequiredFieldsCache.Clear(); _constraintMapCache.Clear(); @@ -56,7 +51,6 @@ public static IReadOnlyList Scan(GateOptions options, Action ScanAssetRequiredFields(string assetPath) { var violations = new List(); @@ -151,8 +145,6 @@ private static void CollectRequiredViolations(string assetPath, List= 0 && !visited.Add(id)) enterChildren = false; } - // Required applies to a managed reference (empty == null) and a [TypeSelector] string field - // (empty == null-or-empty); IsViolation dispatches on the property kind. if (iterator.propertyType is not (SerializedPropertyType.ManagedReference or SerializedPropertyType.String)) continue; if (!TypeSelectorRequiredGate.IsViolation(iterator)) continue; diff --git a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Diagnostics/BreakageEntry.cs b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Diagnostics/BreakageEntry.cs index b135b136..88e7e4f6 100644 --- a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Diagnostics/BreakageEntry.cs +++ b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Diagnostics/BreakageEntry.cs @@ -3,7 +3,6 @@ // ReSharper disable once CheckNamespace namespace Aspid.FastTools.SerializeReferences.Editors { - // One managed reference that just became missing, plus its pre-ranked best fix candidate. internal readonly struct BreakageEntry { public readonly long Rid; diff --git a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Diagnostics/SerializeReferenceBreakageDetector.cs b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Diagnostics/SerializeReferenceBreakageDetector.cs index d62735b9..a7dd1c88 100644 --- a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Diagnostics/SerializeReferenceBreakageDetector.cs +++ b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Diagnostics/SerializeReferenceBreakageDetector.cs @@ -6,10 +6,6 @@ // ReSharper disable once CheckNamespace namespace Aspid.FastTools.SerializeReferences.Editors { - // Detects managed references that JUST became missing by diffing the current resolve state against a per-session - // baseline kept in SessionState — the usage index is wiped on every domain reload and cannot remember one. The - // baseline is established silently on the first run, so pre-existing breakages never alarm. Reports only; it - // never repairs anything itself. internal static class SerializeReferenceBreakageDetector { private const string EstablishedKey = "Aspid.FastTools.SerializeReferences.Breakage.Established"; @@ -24,7 +20,6 @@ private static void EstablishBaselineOnce() => EditorApplication.delayCall += () if (Application.isBatchMode) return; if (SessionState.GetBool(EstablishedKey, false)) return; - // First run of the session: record what resolves now; pre-existing breakages are not "new". RunDetection(report: false); }; @@ -34,7 +29,6 @@ private static void RunDetection(bool report) { if (Application.isBatchMode) return; - // Opt-out: never baseline or scan while disabled; re-enabling silently re-baselines on the next change. if (!SerializeReferenceSettings.BreakageDetectionEnabled) return; // Type resolution flaps while scripts compile, so defer (never drop) until the editor settles. @@ -70,7 +64,6 @@ private static void RunDetection(bool report) result = BuildReport(unresolved, baseline); } - // Advance the baseline so a key that just broke drops out and is never re-alarmed on the next scan. SaveBaseline(resolvable); SessionState.SetBool(EstablishedKey, true); @@ -101,21 +94,18 @@ private static void RunDetectionCold(bool report) continue; } - // The [MovedFrom] resolver needs no index, so even the cold path tells a rename from a real breakage. SerializeReferenceMovedFromResolver.TryResolve(storedType, out var migrationTarget); entries.Add(new BreakageEntry(null, 0, 0, storedType, isRepairable: false, topSuggestion: null, migrationTarget)); brokenTypes.Add(key); } - // Advance the baseline so a just-broken type is never re-alarmed, mirroring the warm path. SaveBaseline(stillResolvable); if (entries.Count == 0) return; BreakageDetected?.Invoke(new BreakageReport(entries, brokenTypes.Count)); } - // Parses an "Assembly|Namespace|Class" key (see SerializeReferenceHelpers.StoredTypeKey) back into a ManagedTypeName. private static bool TryParseStoredTypeKey(string key, out ManagedTypeName storedType) { storedType = default; @@ -128,7 +118,6 @@ private static bool TryParseStoredTypeKey(string key, out ManagedTypeName stored return true; } - // Reports only the unresolved usages whose stored type was still resolvable in the baseline. private static BreakageReport BuildReport( List unresolved, HashSet baseline) @@ -143,7 +132,7 @@ private static BreakageReport BuildReport( foreach (var usage in unresolved) { var key = SerializeReferenceHelpers.StoredTypeKey(usage.StoredType); - if (!baseline.Contains(key)) continue; // was already broken (or never resolved) — not new + if (!baseline.Contains(key)) continue; var path = AssetDatabase.GUIDToAssetPath(usage.Guid); if (!byPath.TryGetValue(path, out var usages)) @@ -181,7 +170,6 @@ private static BreakageReport BuildReport( return entries.Count == 0 ? default : new BreakageReport(entries, types.Count); } - // Pre-ranks the best fix, priming the shared suggestion cache so the Repair window shows Smart Fix without delay. private static BreakageEntry BuildEntry( SerializeReferenceTypeUsageIndex.Usage usage, string path, diff --git a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Diagnostics/SerializeReferenceBreakageHook.cs b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Diagnostics/SerializeReferenceBreakageHook.cs index dd69c9be..66a3b331 100644 --- a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Diagnostics/SerializeReferenceBreakageHook.cs +++ b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Diagnostics/SerializeReferenceBreakageHook.cs @@ -6,7 +6,6 @@ // ReSharper disable once CheckNamespace namespace Aspid.FastTools.SerializeReferences.Editors { - // Drives the breakage detector on asset/script changes, debounced to one scan per change burst. internal sealed class SerializeReferenceBreakageHook : AssetPostprocessor { private static bool _scheduled; diff --git a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Diagnostics/SerializeReferenceBreakageNotificationController.cs b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Diagnostics/SerializeReferenceBreakageNotificationController.cs index e9331b7a..fe130b0f 100644 --- a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Diagnostics/SerializeReferenceBreakageNotificationController.cs +++ b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Diagnostics/SerializeReferenceBreakageNotificationController.cs @@ -7,8 +7,6 @@ // ReSharper disable once CheckNamespace namespace Aspid.FastTools.SerializeReferences.Editors { - // Surfaces a breakage report as a fade-out toast plus one console line. The same set is shown at most once per - // session (a content hash in SessionState), so a recompile that re-detects it does not nag. internal static class SerializeReferenceBreakageNotificationController { private const string ShownPrefix = "Aspid.FastTools.SerializeReferences.Breakage.Shown."; @@ -53,7 +51,6 @@ private static void OnBreakageDetected(BreakageReport report) ShowToast(message); - // A fully-migratable report logs at plain severity so the console matches the calm copy. var console = $"[Aspid FastTools] {message}. Open Tools/Aspid \U0001F40D/FastTools/Project References."; if (migratable == count) Debug.Log(console); else Debug.LogWarning(console); @@ -78,11 +75,8 @@ private static void ShowToast(string message) window.Repaint(); return; } - // No editor window open (rare) — the console warning above is the fallback signal. } - // A stable identity for the breakage set: its sorted, distinct stored-type keys. Two events affecting the same - // types are the "same" set and are not re-toasted. private static string ContentHash(BreakageReport report) { var keys = new SortedSet(System.StringComparer.Ordinal); diff --git a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Drawers/SerializeReferenceDropHandler.cs b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Drawers/SerializeReferenceDropHandler.cs index 96b16d39..b431e8b8 100644 --- a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Drawers/SerializeReferenceDropHandler.cs +++ b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Drawers/SerializeReferenceDropHandler.cs @@ -5,8 +5,6 @@ // ReSharper disable once CheckNamespace namespace Aspid.FastTools.SerializeReferences.Editors { - // Assigns a managed reference from a MonoScript dropped on a [SerializeReference] field. Shared by the UIToolkit - // field and the IMGUI drawer. internal static class SerializeReferenceDropHandler { // Resolves the first dragged script's class when it is assignable to the field and passes the diff --git a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Drawers/SerializeReferenceEditorGUI.cs b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Drawers/SerializeReferenceEditorGUI.cs index 18f95226..6d370ead 100644 --- a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Drawers/SerializeReferenceEditorGUI.cs +++ b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Drawers/SerializeReferenceEditorGUI.cs @@ -7,37 +7,23 @@ namespace Aspid.FastTools.SerializeReferences.Editors { /// - /// Provides utility methods for drawing [SerializeReference] properties with the package's type-dropdown - /// UI from a custom editor's own code, with no [TypeSelector] attribute. + /// Provides utility methods for drawing managed-reference type pickers in custom inspectors. /// /// - /// Call and from CreateInspectorGUI, and - /// from an IMGUI OnInspectorGUI; IMGUI lists go through - /// . + /// Use and in , + /// and in . /// - /// - /// - /// public override VisualElement CreateInspectorGUI() - /// { - /// var root = new VisualElement(); - /// root.Add(SerializeReferenceEditorGUI.CreateField(serializedObject.FindProperty("_weapon"))); - /// root.Add(SerializeReferenceEditorGUI.CreateList(serializedObject.FindProperty("_modifiers"))); - /// return root; - /// } - /// - /// public static class SerializeReferenceEditorGUI { /// - /// Builds the dropdown field for one [SerializeReference] property: a foldout whose header carries the - /// type dropdown and whose content hosts the instance's fields, with the package's usual notices. + /// Creates a UI Toolkit type picker with nested fields and managed-reference notices. /// /// A managed-reference property of the editor's . - /// Field label; the property's display name when omitted. - /// Base types narrowing the picker below the field's declared type. + /// label; uses its display name. + /// Additional picker constraints; or an empty array adds no constraints. /// The field to add to the inspector's visual tree. - /// Thrown when is . - /// Thrown when the property is not a managed reference. + /// is . + /// is not a managed reference. public static VisualElement CreateField(SerializedProperty property, string label = null, params Type[] baseTypes) { if (property is null) @@ -49,15 +35,14 @@ public static VisualElement CreateField(SerializedProperty property, string labe } /// - /// Builds the list for a [SerializeReference] array: every element renders as the dropdown field and - /// the "+" opens the type picker, appending a fresh instance instead of a rid-aliased duplicate. + /// Creates a UI Toolkit managed-reference list whose add button selects a type and appends an independent instance. /// /// An array/list property whose elements are managed references. - /// Header label; the property's display name when omitted. - /// Base types narrowing the picker below the declared element type. + /// header label; uses its display name. + /// Additional element-type constraints; or an empty array adds no constraints. /// The list to add to the inspector's visual tree. - /// Thrown when is . - /// Thrown when the property is not a managed-reference array. + /// is . + /// is not a managed-reference array. public static VisualElement CreateList(SerializedProperty property, string label = null, params Type[] baseTypes) { if (property is null) throw new ArgumentNullException(nameof(property)); @@ -72,14 +57,14 @@ public static VisualElement CreateList(SerializedProperty property, string label } /// - /// Reserves a layout rect and draws into it the same dropdown field as . + /// Draws a managed-reference type picker and its nested fields in an IMGUI layout. /// /// Lists use . /// A managed-reference property of the editor's . - /// Field label; the property's display name when omitted. - /// Base types narrowing the picker below the field's declared type. - /// Thrown when is . - /// Thrown when the property is not a managed reference. + /// label; uses its display name. + /// Additional picker constraints; or an empty array adds no constraints. + /// is . + /// is not a managed reference. public static void DrawFieldLayout(SerializedProperty property, GUIContent label = null, params Type[] baseTypes) { if (property is null) throw new ArgumentNullException(nameof(property)); diff --git a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Drawers/SerializeReferenceIMGUIList.cs b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Drawers/SerializeReferenceIMGUIList.cs index c76c9b3e..f4ee5816 100644 --- a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Drawers/SerializeReferenceIMGUIList.cs +++ b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Drawers/SerializeReferenceIMGUIList.cs @@ -9,15 +9,10 @@ namespace Aspid.FastTools.SerializeReferences.Editors { /// - /// Provides utility methods for drawing an IMGUI [SerializeReference] list whose add button opens the type - /// picker and appends a fresh instance. + /// Provides utility methods for drawing managed-reference lists with a type picker for new elements in IMGUI. /// /// - /// In IMGUI a [TypeSelector] drawer is applied to array elements and can never reach the list's own "+", so - /// an editor that overrides OnInspectorGUI gets Unity's default add — which duplicates the last element and - /// leaves it rid-aliased. Call for those lists instead. Elements still go through - /// , so the per-element drawer - /// applies exactly as it would by default. + /// The add button creates an independent instance; element fields retain their registered property drawers. /// public static class SerializeReferenceIMGUIList { @@ -40,13 +35,12 @@ public static class SerializeReferenceIMGUIList _elementRightLimits.Count > 0 ? _elementRightLimits.Peek() : float.NaN; /// - /// Draws a [SerializeReference] list with a picker-backed "+". + /// Draws a managed-reference list whose add button selects a type and appends an independent instance. /// - /// The array/list property to draw. Its elements must be managed references. - /// Header label for the list. - /// Declared element type constraining the picker; needed up front because an empty - /// list has no element to read it from. - /// Base types narrowing the candidates below . + /// The array or list of managed references; or a non-array property draws nothing. + /// The list header; displays no label. + /// The declared element type constraining the picker, supplied even when the list is empty. + /// Additional constraints below ; or an empty array adds none. public static void Draw(SerializedProperty listProperty, GUIContent label, Type elementType, params Type[] baseTypes) { if (listProperty is null || !listProperty.isArray) return; @@ -100,12 +94,9 @@ private static ReorderableList GetOrCreate(SerializedProperty listProperty, GUIC // Swept on cache misses only, which are already the slow path. EvictDeadEntries(); - // Both are stable for the field's lifetime, and Append opens its own fresh SerializedObject. var target = serializedObject.targetObject; var arrayPath = listProperty.propertyPath; - // Built before the callbacks so their lambdas can close over `list`; an object-initializer self-reference - // under `var` would not compile. var list = new ReorderableList(serializedObject, listProperty, draggable: true, displayHeader: true, displayAddButton: true, displayRemoveButton: true); @@ -133,7 +124,6 @@ private static ReorderableList GetOrCreate(SerializedProperty listProperty, GUIC rect.y += EditorGUIUtility.standardVerticalSpacing; rect.height = ElementHeight(element, depth); - // The pushed limit tells the element's drawer where this row's box ends. _elementRightLimits.Push(boxRightEdge); try { @@ -151,7 +141,6 @@ private static ReorderableList GetOrCreate(SerializedProperty listProperty, GUIC } }; - // Replaces Unity's default add, which duplicates the last element and leaves it rid-aliased. list.onAddDropdownCallback = (buttonRect, _) => { // Anchoring the picker's right edge to the button grows it leftward, so a "+" near the inspector's @@ -165,7 +154,6 @@ private static ReorderableList GetOrCreate(SerializedProperty listProperty, GUIC return list; } - // Measured the same way the row is drawn, or the reserved rect and the painted content disagree. private static float ElementHeight(SerializedProperty element, int depth) => SerializeReferenceNesting.DrawsOwnHeader(element, depth) ? SerializeReferenceIMGUIPropertyDrawer.GetHeight(element, depth + 1) diff --git a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Drawers/SerializeReferenceIMGUIPropertyDrawer.cs b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Drawers/SerializeReferenceIMGUIPropertyDrawer.cs index 0731b771..2cc16352 100644 --- a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Drawers/SerializeReferenceIMGUIPropertyDrawer.cs +++ b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Drawers/SerializeReferenceIMGUIPropertyDrawer.cs @@ -7,8 +7,6 @@ // ReSharper disable once CheckNamespace namespace Aspid.FastTools.SerializeReferences.Editors { - // IMGUI rendering for the [TypeSelector] drawer on a [SerializeReference] field: a foldout-and-dropdown header - // row, the notices, and the nested properties of the assigned instance. internal static class SerializeReferenceIMGUIPropertyDrawer { private static readonly GUIContent _measureContent = new(); @@ -38,11 +36,9 @@ internal static float GetHeight(SerializedProperty property, int depth) var spacing = EditorGUIUtility.standardVerticalSpacing; var height = EditorGUIUtility.singleLineHeight; - // Per-instance child fields cannot be merged, so mixed types get the dropdown and a one-line hint only. if (SerializeReferenceHelpers.HasMixedTypes(property)) return height + spacing + EditorGUIUtility.singleLineHeight; - // Per-asset notices are suppressed under a multi-object selection. if (SerializeReferenceHelpers.NoticesApply(property)) { if (SerializeReferenceHelpers.IsMissingType(property)) @@ -66,8 +62,6 @@ public static void Draw(Rect position, GUIContent label, SerializedProperty prop internal static void Draw(Rect position, GUIContent label, SerializedProperty property, int depth, Type[] baseTypes) { - // On a rid collision the guard queues a de-alias for the next tick, never mutating mid-draw. Cheap on the - // unchanged path, so safe to call from every repaint. SerializeReferenceDuplicateGuard.Observe(property); var spacing = EditorGUIUtility.standardVerticalSpacing; @@ -76,7 +70,6 @@ internal static void Draw(Rect position, GUIContent label, SerializedProperty pr var hasValue = currentType is not null && !mixedTypes; var fieldType = SerializeReferenceHelpers.GetFieldType(property); - // Computed up front: showing any notice decides whether the field reserves the stripe gutter. var noticesApply = !mixedTypes && SerializeReferenceHelpers.NoticesApply(property); var showMissing = noticesApply && SerializeReferenceHelpers.IsMissingType(property); var showShared = noticesApply && SerializeReferenceHelpers.HasSharedReference(property); @@ -101,7 +94,6 @@ internal static void Draw(Rect position, GUIContent label, SerializedProperty pr contextEvent.Use(); } - // Dropping a MonoScript on the header row assigns an instance of its class (when assignable). if ((contextEvent.type == EventType.DragUpdated || contextEvent.type == EventType.DragPerform) && line.Contains(contextEvent.mousePosition)) { @@ -122,7 +114,6 @@ internal static void Draw(Rect position, GUIContent label, SerializedProperty pr } } - // A type with no serialized fields is drawn flat: an arrow would promise content that never appears. var expandable = hasValue && SerializeReferenceNesting.HasVisibleChildren(property); var labelRect = new Rect(line.x, line.y, EditorGUIUtility.labelWidth, line.height); @@ -132,7 +123,6 @@ internal static void Draw(Rect position, GUIContent label, SerializedProperty pr } else { - // No foldout arrow, so pull the label left onto its spot. var labelPull = flat ? FoldoutArrowIndent : 0f; EditorGUI.LabelField(new Rect(labelRect.x - labelPull, labelRect.y, labelRect.width + labelPull, labelRect.height), label); @@ -167,7 +157,6 @@ internal static void Draw(Rect position, GUIContent label, SerializedProperty pr caption = FitCaptionFromLeft(captionStyle, caption, dropdownRect.width); } - // The caption shows the short name, so the tooltip carries the full identity. var captionTooltip = mixedTypes ? "Mixed — the selected objects hold different types." : missingTooltip ?? TypeSelectorHelpers.GetTypeSelectorTooltip(currentType); @@ -175,7 +164,6 @@ internal static void Draw(Rect position, GUIContent label, SerializedProperty pr if (EditorGUI.DropdownButton(dropdownRect, new GUIContent(caption, captionTooltip), FocusType.Passive, captionStyle)) { - // No single current type under mixed types, so the picker opens unselected. ShowSelector(property, fieldType, baseTypes, mixedTypes ? null : currentType, dropdownRect); } @@ -186,7 +174,6 @@ internal static void Draw(Rect position, GUIContent label, SerializedProperty pr var y = line.yMax + spacing; - // One dim info line stands in for the child fields, which cannot be merged. if (mixedTypes) { var hintRect = new Rect(body.x, y, body.width, EditorGUIUtility.singleLineHeight); @@ -203,7 +190,6 @@ internal static void Draw(Rect position, GUIContent label, SerializedProperty pr // foldout arrow the same at every nesting depth. var content = EditorGUI.IndentedRect(body); - // Full-height status stripe: the badge's per-index color when shared, else the warning amber. { Color? stripeColor = null; if (showShared && sharedIndex > 0) @@ -257,7 +243,6 @@ internal static void Draw(Rect position, GUIContent label, SerializedProperty pr y += EditorGUIUtility.singleLineHeight + spacing; } - // A required-but-empty reference shows a non-actionable notice; the header dropdown above is the fix. if (showRequired) { // Flat field (no arrow): pull the notice left onto the arrow's spot so it lines up with the label above. @@ -277,14 +262,12 @@ internal static void Draw(Rect position, GUIContent label, SerializedProperty pr EditorGUI.indentLevel--; } - // The only notice that coexists with children, so it alone sits under the nested properties. if (showShared) { // One color across notice and stripe, so aliased fields read as a group. No warning icon: this is // attention, not an error. Color? indexColor = sharedIndex > 0 ? SerializeReferenceRidColor.ForIndex(sharedIndex) : null; - // When this member is the one a sibling's message click just revealed, scroll the inspector to it. SerializeReferenceSharedNavigation.RevealIfPending(property, position); // Pulled left by the arrow's width so the swatch lines up under it, and widened to match so @@ -468,7 +451,6 @@ private static void ShowContextMenu(SerializedProperty property, Type fieldType, () => SerializeReferenceUsageSearchProvider.OpenSearch(usagesType)); } - // The inverse of Make Unique: point this field at an existing instance in the same object. if (SerializeReferenceHelpers.NoticesApply(property)) { foreach (var candidate in SerializeReferenceLinker.CollectLinkCandidates(property)) @@ -532,7 +514,6 @@ void Paste(SerializedProperty target) } } - // Applies a saved template to the property (an independent instance per target on a multi-object selection). private static void ApplyTemplate(SerializedProperty property, string name) { var persistent = property.Persistent(); @@ -551,7 +532,6 @@ private static void ApplyTemplate(SerializedProperty property, string name) persistent.isExpanded = true; } - // A non-null missingTooltip both feeds the hover tooltip and flags the caption for the amber treatment. private static string GetCaption(SerializedProperty property, Type currentType, out string missingTooltip) { missingTooltip = null; diff --git a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Drawers/SerializeReferenceListAddBehavior.cs b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Drawers/SerializeReferenceListAddBehavior.cs index 3c2cb0f5..37a3c983 100644 --- a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Drawers/SerializeReferenceListAddBehavior.cs +++ b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Drawers/SerializeReferenceListAddBehavior.cs @@ -10,9 +10,6 @@ // ReSharper disable once CheckNamespace namespace Aspid.FastTools.SerializeReferences.Editors { - // Replaces the default "+" on a [SerializeReference] list — which duplicates the last element and leaves it - // rid-aliased — with one that opens the type picker and appends a fresh instance. SerializeReferenceDuplicateGuard - // stays the fallback for the native add paths (Ctrl+D, paste, multi-object selections). internal static class SerializeReferenceListAddBehavior { // Installs the picker-backed add behavior once on the hosting ListView. The base types come through a @@ -26,7 +23,7 @@ public static void TryInstall(VisualElement elementField, SerializedProperty ele var path = elementProperty.propertyPath; var arrayMarker = path.IndexOf(".Array.data[", StringComparison.Ordinal); - if (arrayMarker < 0) return; // not a list/array element + if (arrayMarker < 0) return; var arrayPath = path[..arrayMarker]; var target = serializedObject.targetObject; @@ -46,24 +43,19 @@ public static void TryInstall(VisualElement elementField, SerializedProperty ele }); } - // Shared with SerializeReferenceListField, whose "+" needs the same picker anchored the same way. public static void OpenAppendPicker(Object target, string arrayPath, Type elementType, Type[] baseTypes, VisualElement anchor) { var window = anchor.GetOwnerWindow(); if (window == null) return; - // Anchor to the ListView, not the small "+", so the picker opens as a wide dropdown below the add row. var reference = anchor.GetFirstAncestorOfType() ?? anchor; - // Match TypeSelectorWindow.Show's minimum width so the clamp below reflects the picker's real footprint. var width = Mathf.Max(350f, reference.worldBound.width); - // Clamp so the picker's right edge never crosses the inspector window's. var x = Mathf.Max( window.position.x, Mathf.Min(window.position.x + reference.worldBound.xMin, window.position.xMax - width)); - // From the button's top plus its height: anchoring at yMax double-counts the height and drops a row lower. var screenRect = new Rect( x, window.position.y + anchor.worldBound.yMin, @@ -73,7 +65,6 @@ public static void OpenAppendPicker(Object target, string arrayPath, Type elemen ShowAppendPicker(target, arrayPath, elementType, baseTypes, screenRect); } - // Shared by the UIToolkit add override and the IMGUI list drawer, which differ only in the anchor rect. public static void ShowAppendPicker(Object target, string arrayPath, Type elementType, Type[] baseTypes, Rect screenRect) { TypeSelectorWindow.Show( @@ -94,7 +85,6 @@ private static void Append(Object target, string arrayPath, string assemblyQuali { if (target == null) return; - // A pick is valid: the "+" always grows the list, appending an element the user can type later. var type = string.IsNullOrEmpty(assemblyQualifiedName) ? null : Type.GetType(assemblyQualifiedName, throwOnError: false); // A fresh SerializedObject avoids a stale-binding hazard; the bound ListView refreshes on its next update. diff --git a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Editing/MissingReferenceGroup.cs b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Editing/MissingReferenceGroup.cs index 9aaee60f..06c61e47 100644 --- a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Editing/MissingReferenceGroup.cs +++ b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Editing/MissingReferenceGroup.cs @@ -6,7 +6,6 @@ // ReSharper disable once CheckNamespace namespace Aspid.FastTools.SerializeReferences.Editors { - // One broken managed-reference entry plus the asset it lives in. internal readonly struct MissingReferenceLocation { public readonly string AssetPath; @@ -19,8 +18,6 @@ public MissingReferenceLocation(string assetPath, MissingReferenceEntry entry) } } - // Every broken reference sharing one stored type across the project — the unit the audit lists and bulk-fixes. - // The picker constraint intersects the entries' declared field types, falling back to object when they disagree. internal sealed class MissingReferenceGroup { public readonly ManagedTypeName StoredType; @@ -38,8 +35,6 @@ public MissingReferenceGroup(ManagedTypeName storedType) public string DisplayName => StoredType.DisplayName; - // Groups every unresolved reference by stored type, biggest group first. Cheap once the shared usage index - // is warm, since it is an in-memory filter rather than a sweep. public static List CollectFromIndex() { var byType = new Dictionary(StringComparer.Ordinal); @@ -87,7 +82,6 @@ public bool TryGetSuggestion(Type constraint, out SerializeReferenceRepairSugges return true; } - // The type every entry's field can hold, or object when that cannot be narrowed. public Type ResolveConstraint() => ResolveConstraint(out _); // mixedFieldTypes separates a fallback caused by disagreeing field types from an unrecoverable one; the @@ -119,10 +113,6 @@ public Type ResolveConstraint(out bool mixedFieldTypes) } } - // A group's picker constraint and whether it reads as a one-click [MovedFrom] migration, resolved once so the - // audit's partition, card body and picker label can never disagree. A migration needs its rename target to fit - // the constraint too: "Migrate all" bypasses the picker's assignability guarantee, and Unity would null an - // incompatible target at load. internal readonly struct MissingReferenceMigration { public readonly Type Constraint; diff --git a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Editing/SerializeReferenceBatchEditor.cs b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Editing/SerializeReferenceBatchEditor.cs index b50421c2..d11ea5a2 100644 --- a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Editing/SerializeReferenceBatchEditor.cs +++ b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Editing/SerializeReferenceBatchEditor.cs @@ -6,13 +6,8 @@ // ReSharper disable once CheckNamespace namespace Aspid.FastTools.SerializeReferences.Editors { - // The bulk half of the repair tooling: rewriting or nulling many entries at once, batched per file so each asset - // is reimported exactly once. Pure file work — confirmations, receipts and rendering belong to the caller. - // A failed write is skipped, so every returned count is what actually changed on disk, not what was asked for. internal static class SerializeReferenceBatchEditor { - // Splits entries into those safe to rewrite on disk and those open elsewhere, which must be repaired in - // memory instead. public static void SplitWritable(IReadOnlyList source, out List onDisk, out List inMemory) { @@ -68,13 +63,10 @@ public static int Rewrite(IReadOnlyList entries, Manag RunBatch(entries, progressTitle, (path, entry) => SerializeReferenceYamlEditor.TryRewriteType(path, entry.Entry.FileId, entry.Entry.Rid, targetType)); - // Nulls every entry to the null managed-reference id and drops its payload. public static int Null(IReadOnlyList entries, string progressTitle) => RunBatch(entries, progressTitle, (path, entry) => SerializeReferenceYamlEditor.TryNullReference(path, entry.Entry.FileId, entry.Entry.Rid)); - // Nulls each open entry on its live object; the file rewrite is skipped, so these stay in the audit until - // the asset is saved. public static int ClearOpenInMemory(IReadOnlyList entries, ManagedTypeName storedType) { var cleared = 0; @@ -90,7 +82,6 @@ public static int ClearOpenInMemory(IReadOnlyList entr public static int CountFiles(IEnumerable entries) => entries.Select(entry => entry.AssetPath).Distinct(StringComparer.Ordinal).Count(); - // The shared per-file loop behind Rewrite and Null; a file is reimported only when something changed in it. private static int RunBatch(IReadOnlyList entries, string progressTitle, Func edit) { diff --git a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Editing/SerializeReferenceConstraintCache.cs b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Editing/SerializeReferenceConstraintCache.cs index a46d131d..b14cd64f 100644 --- a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Editing/SerializeReferenceConstraintCache.cs +++ b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Editing/SerializeReferenceConstraintCache.cs @@ -4,9 +4,6 @@ // ReSharper disable once CheckNamespace namespace Aspid.FastTools.SerializeReferences.Editors { - // Per-asset memo over BuildConstraintMap, so a repair surface's many lookups cost one scan per asset — building - // one map is a LoadAllAssetsAtPath plus a full SerializedObject walk. Clear it after any edit that rewrote the - // YAML, or the next lookup answers from the pre-edit file. internal sealed class SerializeReferenceConstraintCache { private readonly Dictionary> _maps = new(StringComparer.Ordinal); diff --git a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Editing/SerializeReferenceGraphEditor.cs b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Editing/SerializeReferenceGraphEditor.cs index 3da06a4c..be9a65f1 100644 --- a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Editing/SerializeReferenceGraphEditor.cs +++ b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Editing/SerializeReferenceGraphEditor.cs @@ -10,19 +10,10 @@ // ReSharper disable once CheckNamespace namespace Aspid.FastTools.SerializeReferences.Editors { - // Every single-entry repair the Asset References graph offers, without any of its UI. Two edit routes, picked by - // what the entry is rather than by the caller: a healthy or empty slot goes through managedReferenceValue, so - // Unity writes the RefIds entry exactly as the Inspector would, while a MISSING reference cannot be reassigned - // through that API at all and is edited by rewriting the YAML in place. That is why the YAML routes confirm - // first, cannot be undone through Unity's undo stack, and refuse to run against an asset with an open copy. - // - // Each entry point reports whether anything changed; re-rendering the graph is the caller's concern. internal static class SerializeReferenceGraphEditor { - // Re-points a missing reference by rewriting the stored type name in the YAML, keeping the payload. public static bool ApplyFix(string assetPath, long fileId, long rid, string assemblyQualifiedName) { - // emits an empty name, which clears the reference rather than falling through as a no-op. if (string.IsNullOrEmpty(assemblyQualifiedName)) return ClearReference(assetPath, fileId, rid); if (SerializeReferenceOpenCopyGuard.BlockedByOpenCopy(assetPath)) return false; @@ -30,7 +21,6 @@ public static bool ApplyFix(string assetPath, long fileId, long rid, string asse var type = Type.GetType(assemblyQualifiedName, throwOnError: false); if (type is null) return false; - // Only the captured document is rewritten: a rid is unique within one, but collides across them. if (!SerializeReferenceYamlEditor.TryRewriteType(assetPath, fileId, rid, ManagedTypeName.FromType(type))) return false; @@ -45,8 +35,6 @@ public static bool ClearReference(string assetPath, long fileId, long rid) { if (SerializeReferenceOpenCopyGuard.BlockedByOpenCopy(assetPath)) return false; - // Name how many fields the clear nulls, so an aliased reference does not silently take down siblings. - // A non-positive count means the pointers could not be located, so the wording drops the number. var fieldCount = SerializeReferenceYamlEditor.CountPointersTo(assetPath, fileId, rid); var pointerLine = fieldCount switch { @@ -94,7 +82,6 @@ public static bool TryClearOrphan(string assetPath, long fileId, long rid, out L if (!SerializeReferenceYamlEditor.TryRemoveEntry(assetPath, fileId, rid)) return false; - // Patched through the import invalidator rather than a full ClearCache, as in ClearReference. AssetDatabase.ImportAsset(assetPath, ImportAssetOptions.ForceUpdate); SerializeReferenceRepairSuggestions.ClearCache(); return true; @@ -129,7 +116,6 @@ public static bool ApplyLive(string assetPath, long fileId, string graphPath, st using (serializedObject) { var previous = property.managedReferenceValue; - // A null type clears to ; a concrete one carries over the previous value's matching fields. property.SetManagedReferenceAndApply(SerializeReferenceHelpers.CreateInstancePreservingData(type, previous)); property.isExpanded = type is not null; @@ -138,7 +124,6 @@ public static bool ApplyLive(string assetPath, long fileId, string graphPath, st PersistEdit(assetPath, target); } - // The save triggers the import that patches the index for this asset alone. SerializeReferenceRepairSuggestions.ClearCache(); SerializeReferenceYamlProbeCache.ClearCache(); return true; @@ -190,7 +175,6 @@ public static bool TryResolveLiveProperty(string assetPath, long fileId, string property = null; if (string.IsNullOrEmpty(graphPath)) return false; - // Scenes are not loadable through LoadAllAssetsAtPath. if (SerializeReferenceHelpers.IsScene(assetPath)) return false; return TryResolveProperty(assetPath, fileId, ToSerializedPropertyPath(graphPath), @@ -211,11 +195,9 @@ public static bool TryResolveRequiredStringProperty(GateViolation violation, SerializedPropertyType.String, out serializedObject, out property); } - // The inverse of the ".Array.data" stripping the YAML editor does when it normalizes a property path. public static string ToSerializedPropertyPath(string graphPath) => Regex.Replace(graphPath, @"\[(\d+)\]", ".Array.data[$1]"); - // Finds the sub-asset carrying fileId, then the property at propertyPath, of the expected kind. private static bool TryResolveProperty(string assetPath, long fileId, string propertyPath, SerializedPropertyType expected, out SerializedObject serializedObject, out SerializedProperty property) { @@ -236,7 +218,6 @@ private static bool TryResolveProperty(string assetPath, long fileId, string pro return true; } - // No other document shares this file id, so a wrong kind here means bail rather than scan on. serialized.Dispose(); return false; } diff --git a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Editing/SerializeReferenceOpenCopyGuard.cs b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Editing/SerializeReferenceOpenCopyGuard.cs index c908a850..64cc10d7 100644 --- a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Editing/SerializeReferenceOpenCopyGuard.cs +++ b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Editing/SerializeReferenceOpenCopyGuard.cs @@ -5,9 +5,6 @@ // ReSharper disable once CheckNamespace namespace Aspid.FastTools.SerializeReferences.Editors { - // The writability test every YAML rewrite applies first: an asset loaded as a scene or open in Prefab Mode keeps - // an in-memory copy that wins on its next save, so a file edit under it is silently clobbered. Single-asset - // callers use BlockedByOpenCopy, which explains the refusal through a dialog; a batch hoists the stage path. internal static class SerializeReferenceOpenCopyGuard { public static string CurrentPrefabStagePath() => PrefabStageUtility.GetCurrentPrefabStage()?.assetPath; diff --git a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Extensions/GenericArgumentRequirement.cs b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Extensions/GenericArgumentRequirement.cs index 1810ea99..e37c506c 100644 --- a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Extensions/GenericArgumentRequirement.cs +++ b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Extensions/GenericArgumentRequirement.cs @@ -7,14 +7,6 @@ // ReSharper disable once CheckNamespace namespace Aspid.FastTools.SerializeReferences.Editors { - // Answers whether closing a type parameter obliges its argument to be Unity-serializable — that is, whether the - // parameter reaches a field the engine writes BY VALUE. Asking "is this type serializable?" instead would cost - // real candidates: a type storing only a [SerializeReference] IConverter[] never writes T's layout, so any - // T closes it safely. - // - // The walk proves the ABSENCE of an obligation and never its presence: anything it cannot follow keeps the - // obligation. A missed rule can only leave today's behavior in place; it can never let through an argument whose - // data Unity would silently drop. internal static class GenericArgumentRequirement { // Unity's own nesting limit is not exposed. Erring high only makes the walk conservative, since hitting the diff --git a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Extensions/ReferenceGraphDocument.cs b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Extensions/ReferenceGraphDocument.cs index 34772c9f..53be6729 100644 --- a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Extensions/ReferenceGraphDocument.cs +++ b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Extensions/ReferenceGraphDocument.cs @@ -5,7 +5,6 @@ // ReSharper disable once CheckNamespace namespace Aspid.FastTools.SerializeReferences.Editors { - // The managed-reference graph of one serialized object document. internal sealed class ReferenceGraphDocument { public long FileId; @@ -20,10 +19,8 @@ internal sealed class ReferenceGraphDocument // Parent rid -> its child edges. Empty (null-sentinel) slots are kept so a cleared nested field still shows. public readonly Dictionary> Edges = new(); - // rids referenced by two or more parents — aliased managed references. public readonly HashSet Shared = new(); - // rids reachable from no root — leftover payloads no field points at. public readonly HashSet Orphans = new(); public ReferenceGraphNode? FindNode(long rid) diff --git a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Extensions/ReferenceGraphEdge.cs b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Extensions/ReferenceGraphEdge.cs index bd25706f..d2c3ccbb 100644 --- a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Extensions/ReferenceGraphEdge.cs +++ b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Extensions/ReferenceGraphEdge.cs @@ -1,9 +1,6 @@ // ReSharper disable once CheckNamespace namespace Aspid.FastTools.SerializeReferences.Editors { - // A parent -> child edge of a document's nested graph. Label is the field path relative to the PARENT's data - // block; the view joins it onto the parent's path to show where a nested reference lives. A null child slot is - // kept as an empty edge that points at no node and never recurses. internal readonly struct ReferenceGraphEdge { public readonly long Rid; diff --git a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Extensions/ReferenceGraphNode.cs b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Extensions/ReferenceGraphNode.cs index d7ea02c1..a215dcf6 100644 --- a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Extensions/ReferenceGraphNode.cs +++ b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Extensions/ReferenceGraphNode.cs @@ -1,8 +1,6 @@ // ReSharper disable once CheckNamespace namespace Aspid.FastTools.SerializeReferences.Editors { - // One managed-reference node of a document's graph. Built purely from the asset YAML, so it surfaces references - // at any nesting depth, including the orphaned ones Unity drops from the live object. internal readonly struct ReferenceGraphNode { public readonly long Rid; @@ -16,11 +14,9 @@ public ReferenceGraphNode(long rid, ManagedTypeName storedType, bool resolves) Resolves = resolves; } - // Row label. public string ShortName => string.IsNullOrEmpty(StoredType.Class) ? $"rid {Rid}" : StoredType.Class; - // Row tooltip. public string FullName => StoredType.FullName; } } diff --git a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Extensions/ReferenceGraphRoot.cs b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Extensions/ReferenceGraphRoot.cs index 571fdf98..db7d15c4 100644 --- a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Extensions/ReferenceGraphRoot.cs +++ b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Extensions/ReferenceGraphRoot.cs @@ -1,9 +1,6 @@ // ReSharper disable once CheckNamespace namespace Aspid.FastTools.SerializeReferences.Editors { - // A field pointer from a document's body into its RefIds block — a root of the reference tree. Label is the full - // field path holding it, with list elements indexed (_config._slots[2]). A field holding nothing is kept as an - // empty root, with no node behind it, so a cleared slot stays visible. internal readonly struct ReferenceGraphRoot { public readonly long Rid; diff --git a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Extensions/SerializeReferenceClipboard.cs b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Extensions/SerializeReferenceClipboard.cs index 05fc1441..a01a02fc 100644 --- a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Extensions/SerializeReferenceClipboard.cs +++ b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Extensions/SerializeReferenceClipboard.cs @@ -4,8 +4,6 @@ // ReSharper disable once CheckNamespace namespace Aspid.FastTools.SerializeReferences.Editors { - // Editor-session clipboard behind the drawer's Copy/Paste entries. The value is kept as JSON plus its concrete - // type, so a paste builds an independent instance instead of aliasing the source. internal static class SerializeReferenceClipboard { private static bool _hasContent; @@ -32,7 +30,6 @@ public static bool CanPasteInto(Type fieldType, Func filter = null) return filter is null || filter(Type); } - // Builds an instance independent of the copied object; null when an empty reference was copied. public static object CreateInstance() { if (!_hasContent || Type is null) return null; diff --git a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Extensions/SerializeReferenceDuplicateGuard.cs b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Extensions/SerializeReferenceDuplicateGuard.cs index ebd021f5..0ea75379 100644 --- a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Extensions/SerializeReferenceDuplicateGuard.cs +++ b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Extensions/SerializeReferenceDuplicateGuard.cs @@ -6,30 +6,16 @@ // ReSharper disable once CheckNamespace namespace Aspid.FastTools.SerializeReferences.Editors { - // Auto-de-aliases freshly duplicated [SerializeReference] list elements. Duplicating an element or adding one with - // the "+" copies the source's rid, so two elements end up backed by one instance and editing one edits the other. - // Per (target, array path) the guard snapshots the index -> rid layout and, when a NEW same-array alias appears - // between observations, replaces the later element with an independent clone in a single Undo step. - // - // Detection reads only live state, so it covers scene objects, Prefab Mode and saved assets alike. The fix is - // silent by product decision. After an Undo/Redo the snapshots are resynced rather than re-evaluated, so a - // restored alias is never re-fixed. - // - // Cross-FIELD sharing is out of scope — the shared-reference notice covers that — and so are aliases already - // present on the first observation of an array. A fix needs the array to have grown AND the duplicated rid's - // occurrence count to have risen with it, so a reorder or a removal only resyncs the snapshot. internal static class SerializeReferenceDuplicateGuard { // Unity reports -2 for an empty reference and -1 for a missing type; only ids >= 0 can alias. private const long FirstValidReferenceId = 0; - // The text before this marker in an element path is the parent array's path. private const string ArrayElementMarker = ".Array.data["; // On overflow the whole cache is dropped. A re-snapshot never auto-fixes, so at worst a fix is lost. private const int MaxTrackedArrays = 512; - // The last observed index -> rid layout per (target, array path). Static, so a domain reload clears it. private static readonly Dictionary _snapshots = new(); // Arrays whose fix is queued: the layout still shows the alias until it runs, so without this every @@ -38,9 +24,7 @@ internal static class SerializeReferenceDuplicateGuard private static bool _undoHooked; - // Observes one array element and returns true when it scheduled a de-alias fix for the next editor tick. - // Cheap on the unchanged path — a size and rolling-hash compare gates the map rebuild — so it is safe to call - // from IMGUI's per-frame repaint. + // Only changed array layouts need a full duplicate scan during repaint. public static bool Observe(SerializedProperty elementProperty) { if (!SerializeReferenceSettings.AutoDeAliasEnabled) return false; @@ -65,7 +49,6 @@ public static bool Observe(SerializedProperty elementProperty) var arrayProperty = serializedObject.FindProperty(arrayPath); if (arrayProperty is null || !arrayProperty.isArray) return false; - // No-change gate: size plus an order-sensitive hash of the rids, allocating nothing per observation. var size = arrayProperty.arraySize; var signature = ComputeSignature(arrayProperty, size); @@ -82,8 +65,7 @@ public static bool Observe(SerializedProperty elementProperty) return false; } - // Only a growth of exactly one element can be a fresh duplicate. A multi-element growth is a bulk restore - // — Paste Component Values, Revert to Prefab — that may legitimately bring back an intentional alias. + // Bulk restores may intentionally restore aliases; only single-element growth is auto-corrected. if (size == snapshot.Size + 1 && TryFindFreshDuplicate(snapshot.Map, current, out var duplicateIndex)) { @@ -104,13 +86,11 @@ private static void ScheduleFix(ArrayKey key, Object target, string arrayPath, i _pending.Add(key); EditorApplication.delayCall += () => { - // The fix re-verifies the alias on a fresh read, so a stale schedule is a safe no-op. _pending.Remove(key); MakeElementUnique(target, arrayPath, duplicateIndex); }; } - // A fresh instance gets a new managedReferenceId on assignment, breaking the alias; single Undo step. private static void MakeElementUnique(Object target, string arrayPath, int duplicateIndex) { if (target == null) return; @@ -126,10 +106,8 @@ private static void MakeElementUnique(Object target, string arrayPath, int dupli var current = element.managedReferenceValue; if (current is null) return; - // The layout may have changed since the schedule, so re-verify before clobbering the element. if (!SharesReferenceWithEarlierElement(arrayProperty, duplicateIndex, element.managedReferenceId)) return; - // Deep copy: a shallow clone would leave the copy's nested references aliased to the original's. element.managedReferenceValue = SerializeReferenceHelpers.CloneManagedReferenceGraph(current); serializedObject.ApplyModifiedProperties(); @@ -151,8 +129,7 @@ private static bool SharesReferenceWithEarlierElement(SerializedProperty arrayPr return false; } - // A fresh duplicate is the later element of a pair whose (index, rid) binding is new AND whose rid occurs - // more times than before; the count gate keeps a reorder of a pre-existing alias from reading as fresh. + // Require both a new index-to-ID binding and an increased occurrence count to exclude reorders. private static bool TryFindFreshDuplicate( IReadOnlyDictionary previous, IReadOnlyDictionary current, @@ -174,13 +151,10 @@ private static bool TryFindFreshDuplicate( var index = pair.Key; var rid = pair.Value; - // The earlier owner of the rid keeps its instance, so only the later element is a candidate. if (lowestIndexByRid[rid] >= index) continue; - // A binding unchanged since the previous snapshot is existing sharing, not a fresh duplicate. if (previous.TryGetValue(index, out var previousRid) && previousRid == rid) continue; - // A reorder moves an existing alias into a new binding without changing the rid's count. previousCount.TryGetValue(rid, out var before); if (currentCount[rid] <= before) continue; @@ -216,7 +190,6 @@ private static Dictionary BuildMap(SerializedProperty arrayProperty, return map; } - // Siblings are walked with a single SerializedProperty, so the gate allocates one per call, not per element. private static int ComputeSignature(SerializedProperty arrayProperty, int size) { unchecked @@ -241,8 +214,7 @@ private static int ComputeSignature(SerializedProperty arrayProperty, int size) private static void Store(ArrayKey key, int size, int signature, Dictionary map) { - // Dropped together so the two never desync. A re-snapshot never auto-fixes and a queued fix re-verifies, - // so at worst a fix is canceled, never mis-applied. + // Discard layouts and pending fixes together; the next observation establishes a fresh baseline. if (!_snapshots.ContainsKey(key) && _snapshots.Count >= MaxTrackedArrays) { _snapshots.Clear(); @@ -252,7 +224,6 @@ private static void Store(ArrayKey key, int size, int signature, Dictionary\s*)-\s+rid:\s*(?-?\d+)\s*$", RegexOptions.Compiled); private static readonly Regex _typeLine = new(@"^\s*type:\s*\{(?.*)\}\s*$", RegexOptions.Compiled); @@ -23,12 +19,9 @@ internal static class SerializeReferenceGraphScanner // matching; a match is further validated against the known RefIds set before becoming an edge. private static readonly Regex _ridPointer = new(@"(?-?\d+)", RegexOptions.Compiled); - // A mapping key on a body line, used to label a root. private static readonly Regex _mappingKey = new(@"^\s*(?:-\s+)?(?[A-Za-z_][\w\-]*)\s*:", RegexOptions.Compiled); - // The graph of every document that has a RefIds block; a read or parse failure yields an empty list. - // resolveTypeNames loads the asset and its dependencies to name each document, so a data-only caller passes - // false to keep a project-wide sweep a pure text scan. + // Resolving document labels loads assets; project sweeps pass false to keep the scan text-only. public static List Build(string assetPath, bool resolveTypeNames = true) { var result = new List(); @@ -64,7 +57,7 @@ public static List Build(string assetPath, bool resolveT return result; } - // Null when the document has no RefIds block, or when that block backs neither a node nor a field pointer. + // Documents without reference entries or field pointers contribute no graph. private static ReferenceGraphDocument BuildDocument(string[] lines, long fileId, int start, int end) { var referencesStart = FindKey(lines, _referencesKey, start, end); @@ -105,7 +98,6 @@ private static void CollectNodes(string[] lines, int refIdsStart, int end, Refer var typeMatch = _typeLine.Match(lines[j]); if (!typeMatch.Success) continue; - // On a parse failure type stays default/empty (and the node renders as just "rid N"). if (!SerializeReferenceYaml.TryParseInlineType(typeMatch.Groups["body"].Value, out type)) type = default; break; @@ -133,15 +125,13 @@ private static void CollectRoots(string[] lines, int start, int bodyEnd, HashSet continue; } - if (!knownRids.Contains(rid)) continue; // a dangling pointer, not a graphed reference + if (!knownRids.Contains(rid)) continue; document.Roots.Add(new ReferenceGraphRoot(rid, BuildRootPath(lines, i, start))); } } } - // Every "rid:" pointer inside an entry's data block is a parent -> child edge. The entry's own header line - // is skipped so an entry is never recorded as its own child. private static void CollectEdges(string[] lines, int refIdsStart, int end, HashSet knownRids, ReferenceGraphDocument document) { for (var i = refIdsStart + 1; i < end; i++) @@ -172,8 +162,6 @@ private static void CollectEdges(string[] lines, int refIdsStart, int end, HashS } } - // Shared = referenced by 2+ parents in total (root pointers + nested edges each count once). - // Orphans = nodes reachable from no root. private static void ComputeSharedAndOrphans(ReferenceGraphDocument document, HashSet knownRids) { var parentCount = new Dictionary(); @@ -222,27 +210,23 @@ private static void AddEdge(ReferenceGraphDocument document, long parent, Refere children.Add(edge); } - // A root pointer's full field path. The indent-0 document wrapper key is excluded, so a top-level field - // reads as "_weapon" rather than carrying the wrapper name. + // Omit the document wrapper so root labels remain serialized field paths. private static string BuildRootPath(string[] lines, int i, int start) { var path = BuildPath(lines, i, floor: start, stopIndent: 0); return string.IsNullOrEmpty(path) ? "reference" : path; } - // A nested edge's field path, stopped at the data block's own indent so it stays parent-relative. + // Stop at the data-block boundary to keep nested edge labels relative to their parent. private static string BuildEdgePath(string[] lines, int pointerLine, int dataStart) => BuildPath(lines, pointerLine, floor: dataStart, stopIndent: SerializeReferenceYaml.IndentOf(lines[dataStart])); - // Walks up from the rid pointer collecting mapping keys until it climbs past stopIndent or floor. Unity - // writes block-sequence dashes at the SAME column as the list key, so the owner key sits at equal indent - // above the dash and the index counts the same-indent dash siblings. + // Unity sequence dashes align with their owning key; count sibling dashes at that indentation. private static string BuildPath(string[] lines, int pointerLine, int floor, int stopIndent) { var segments = new List(); var line = pointerLine; - // The counter only guards a malformed file from looping the walk forever. for (var safety = 0; line > floor && safety < 256; safety++) { var indent = SerializeReferenceYaml.IndentOf(lines[line]); @@ -250,7 +234,6 @@ private static string BuildPath(string[] lines, int pointerLine, int floor, int if (lines[line].TrimStart().StartsWith("- ")) { - // A field key on the dash line itself is the deepest segment. var elementKey = _mappingKey.Match(lines[line]); if (elementKey.Success && !IsStructuralKey(elementKey.Groups["key"].Value)) segments.Add(elementKey.Groups["key"].Value); @@ -262,8 +245,8 @@ private static string BuildPath(string[] lines, int pointerLine, int floor, int if (lines[j].Trim().Length == 0) continue; var jIndent = SerializeReferenceYaml.IndentOf(lines[j]); - if (jIndent > indent) continue; // nested detail of an earlier sibling - if (jIndent < indent) break; // dedented out of the list + if (jIndent > indent) continue; + if (jIndent < indent) break; if (lines[j].TrimStart().StartsWith("- ")) { index++; continue; } ownerLine = j; @@ -287,7 +270,7 @@ private static string BuildPath(string[] lines, int pointerLine, int floor, int next = ParentLine(lines, line, floor, indent); } - if (next < 0 || SerializeReferenceYaml.IndentOf(lines[next]) <= stopIndent) break; // climbed out of the enclosing scope + if (next < 0 || SerializeReferenceYaml.IndentOf(lines[next]) <= stopIndent) break; line = next; } @@ -297,7 +280,6 @@ private static string BuildPath(string[] lines, int pointerLine, int floor, int return string.Join(".", segments); } - // The nearest non-empty line above with strictly shallower indent, or -1. private static int ParentLine(string[] lines, int from, int start, int indent) { for (var j = from - 1; j >= start; j--) @@ -309,7 +291,6 @@ private static int ParentLine(string[] lines, int from, int start, int indent) return -1; } - // YAML scaffolding keys that never make a meaningful root label. private static bool IsStructuralKey(string key) => key is "rid" or "data" or "type" or "version" or "references" or "RefIds"; @@ -330,7 +311,6 @@ private static bool IsStructuralKey(string key) => return headers; } - // One LoadAllAssetsAtPath pass; objects Unity cannot load fall back to the YAML class id. private static Dictionary ResolveTypeNames(string assetPath) { var map = new Dictionary(); diff --git a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Extensions/SerializeReferenceHelpers.cs b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Extensions/SerializeReferenceHelpers.cs index a4270bfe..1d59004f 100644 --- a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Extensions/SerializeReferenceHelpers.cs +++ b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Extensions/SerializeReferenceHelpers.cs @@ -16,29 +16,22 @@ // ReSharper disable once CheckNamespace namespace Aspid.FastTools.SerializeReferences.Editors { - // Shared helpers for the [TypeSelector] drawer on [SerializeReference] fields: resolving the declared field - // type, filtering candidates, instantiating the selected type and parsing Unity's managed-reference type-name - // format. The open-generic argument flow itself lives in GenericTypeResolver and TypeSelectorWindow. internal static class SerializeReferenceHelpers { - // The declared element type constraining the candidate list. managedReferenceFieldTypename already reports - // the element type for array entries. + // managedReferenceFieldTypename reports the element type for array entries. public static Type GetFieldType(SerializedProperty property) => GetTypeFromTypename(property.managedReferenceFieldTypename) ?? typeof(object); public static Type GetCurrentType(SerializedProperty property) => property.managedReferenceValue?.GetType(); - // SerializedProperty.arrayElementType of a [SerializeReference] array — the only shape whose elements are - // managed references. private const string ManagedReferenceElementPrefix = "managedReference<"; public static bool IsManagedReferenceArray(SerializedProperty property) => property is { isArray: true, propertyType: not SerializedPropertyType.String } && property.arrayElementType.StartsWith(ManagedReferenceElementPrefix, StringComparison.Ordinal); - // Constrains the add-picker on a list that may be empty; a non-empty list's elements resolve their own - // field type. Read from the reflected field's shape, falling back to the first element, then to object. + // An empty array has no element to inspect; resolve its declared type through reflection first. public static Type GetArrayElementType(SerializedProperty property) { if (property.GetFieldInfo() is { } field) @@ -53,8 +46,6 @@ public static Type GetArrayElementType(SerializedProperty property) } #region Project scan helpers - // Layers the user's excluded folders on top of the engine-level extension test, which is single-sourced so - // every scanner covers the same set. public static bool IsScanCandidate(string path) => SerializeReferenceYaml.IsCandidateAssetPath(path) && !SerializeReferenceSettings.IsExcluded(path); @@ -63,13 +54,10 @@ public static bool IsScanCandidate(string path) => public static bool IsScene(string path) => !string.IsNullOrEmpty(path) && path.EndsWith(".unity", StringComparison.OrdinalIgnoreCase); - // ManagedTypeName carries no value equality, so its three fields are joined into a key string instead. public static string StoredTypeKey(ManagedTypeName type) => $"{type.Assembly}|{type.Namespace}|{type.Class}"; - // Like StoredTypeKey but without the closed-argument expansion, so a script's open definition and every - // closed form YAML stores collapse to one key — that is how the delete guard and usage index match a - // generic type's instantiations back to its script. + // Match closed generic usages to their script by removing arguments while retaining the generic arity. public static string OpenTypeKey(ManagedTypeName type) => OpenTypeKey(StoredTypeKey(type)); @@ -88,8 +76,6 @@ public static string OpenTypeKey(string storedTypeKey) public static bool IsEditingMultipleObjects(SerializedProperty property) => property.serializedObject.isEditingMultipleObjects; - // True when the selected targets do not all hold the same managed-reference type. Always false for a single - // target. Drives the dropdown's mixed-value state and suppresses merging child fields of unlike types. public static bool HasMixedTypes(SerializedProperty property) { if (!property.serializedObject.isEditingMultipleObjects) return false; @@ -98,15 +84,12 @@ public static bool HasMixedTypes(SerializedProperty property) // stored, unloadable type names still differ. if (property.hasMultipleDifferentValues) return true; - // A non-null agreed value means the targets share the concrete type. if (property.managedReferenceValue is not null) return false; var first = property.managedReferenceFullTypename; var targets = property.serializedObject.targetObjects; if (targets.Length < 2) return false; - // The probe allocates a SerializedObject per selected object on every repaint, while what it measures is - // stable until the backing assets change. if (TryGetMixedCache(property.propertyPath, first, targets, out var cached)) return cached; var result = false; @@ -124,9 +107,7 @@ public static bool HasMixedTypes(SerializedProperty property) return result; } - // Keyed per property path, so several empty fields under one multi-selection stay memoized across a repaint - // instead of overwriting a single shared slot. Scoped to one selection snapshot and reset when it changes, - // so it stays bounded by the fields the inspector actually draws. + // Cache by property path within one selection; invalidate when the selection changes. private static Object[] _mixedTargets; private static readonly Dictionary _mixedResults = new(StringComparer.Ordinal); @@ -168,9 +149,7 @@ private static void StoreMixedCache(string path, string first, UnityEngine.Objec _mixedResults[path] = (first, result); } - // Applies a change to every selected target independently, since one multi-object assignment would alias a - // single instance across all of them. The factory receives that target's previous value, to support keeping - // data, and must return a fresh instance or null. The batch collapses into one Undo step. + // Assign per target so each object receives an independent instance; collapse the writes into one Undo step. public static void ApplyManagedReferencePerTarget(SerializedProperty property, Func factory) { var serializedObject = property.serializedObject; @@ -203,29 +182,22 @@ public static void ApplyManagedReferencePerTarget(SerializedProperty property, F serializedObject.Update(); } - // Whether the per-asset notices may be shown. They are file-level operations keyed to one backing asset, so - // under a multi-object selection they would misreport or apply to a single target while presenting as if - // they covered the selection — there the mixed/same-type hint takes their place. + // Repair notices operate on one backing asset and cannot represent a multi-object selection. public static bool NoticesApply(SerializedProperty property) => !property.serializedObject.isEditingMultipleObjects; #endregion - // True when the reference's type can no longer be loaded. Unity exposes no such state per property — the - // value reads back null and the typename is empty — so detection reads the stored reference from the asset - // YAML: a null value whose recorded type cannot be resolved is missing. + // Unity hides missing type identities in live properties; the original identity survives only in YAML. public static bool IsMissingType(SerializedProperty property) => TryGetMissingType(property, out _, out _); - // The probe runs several times per repaint and every legitimately empty field pays a full repair-location - // resolution plus a YAML parse. Repairs land on later frames; same-frame mutations drop the memo explicitly. + // Missing-reference probes run repeatedly during repaint; same-frame repairs explicitly invalidate this memo. private static int _missingProbeFrame = -1; private static readonly Dictionary<(int instanceId, string path), (bool missing, long referenceId, ManagedTypeName storedType)> _missingProbeMemo = new(); - // For mutations that must be visible to a read later in the SAME frame. public static void InvalidateMissingTypeMemo() => _missingProbeFrame = -1; - // Reads the property's stored id and type from the asset YAML; missing when the type no longer resolves. private static bool TryGetMissingType(SerializedProperty property, out long referenceId, out ManagedTypeName storedType) { referenceId = 0; @@ -268,7 +240,6 @@ private static bool ProbeMissingType(SerializedProperty property, out long refer return !storedType.IsEmpty && !StoredTypeResolves(storedType); } - // True when the YAML-recorded type identity can be loaded — i.e. the reference is intact, not missing. public static bool StoredTypeResolves(ManagedTypeName name) { if (string.IsNullOrEmpty(name.Class)) return false; @@ -280,19 +251,13 @@ public static bool StoredTypeResolves(ManagedTypeName name) return Type.GetType(assemblyQualified, throwOnError: false) is not null; } - // Types that can legally be assigned to a [SerializeReference] field: concrete reference types that are - // neither UnityEngine.Object, open generics, strings nor delegates. [Serializable] is deliberately NOT - // required — a managed reference is serialized through the asset's references registry, which records the - // concrete type and its data with no attribute involved. A generic argument lands in an ordinary field and - // does need it; that rule lives in IsValidGenericArgument. + // Managed-reference candidates do not require IsSerializable here; by-value generic arguments are checked separately. public static bool IsAssignableManagedReference(Type type) => type is { IsClass: true, IsAbstract: false, ContainsGenericParameters: false } && type != typeof(string) && !typeof(Object).IsAssignableFrom(type) && !typeof(Delegate).IsAssignableFrom(type); - // The picker's candidate predicate: the structural check, narrowed to baseTypes when they say anything — - // an empty set, or one naming only object, adds no narrowing. public static Func BuildAssignableFilter(Type[] baseTypes) { var narrowing = FilterNarrowingTypes(baseTypes); @@ -302,7 +267,7 @@ public static Func BuildAssignableFilter(Type[] baseTypes) Array.Exists(narrowing, baseType => baseType.IsAssignableFrom(type)); } - // Null when nothing meaningfully narrows the set, so the caller can skip allocating a predicate closure. + // Return null when no constraint narrows the candidates to avoid allocating a predicate. private static Type[] FilterNarrowingTypes(Type[] baseTypes) { if (baseTypes is null || baseTypes.Length == 0) return null; @@ -321,7 +286,6 @@ private static Type[] FilterNarrowingTypes(Type[] baseTypes) return result; } - // Prefers a parameterless constructor so field initializers run, falling back to an uninitialized instance. public static object CreateInstance(Type type) { if (type is null) return null; @@ -336,8 +300,6 @@ public static object CreateInstance(Type type) } } - // Carries over every field the two types share by name and shape, mirroring Unity's own type-change - // behavior: the old value is serialized to JSON and overwritten onto the new instance. public static object CreateInstancePreservingData(Type newType, object previous) { var instance = CreateInstance(newType); @@ -368,8 +330,6 @@ public static object CreateInstancePreservingData(Type newType, object previous) return instance; } - // Assigns every shared [SerializeReference] field whose value fits the target's declared type, arrays - // included. private static void CarryManagedReferences(object previous, object instance) { Dictionary targets = null; @@ -391,11 +351,7 @@ private static void CarryManagedReferences(object previous, object instance) } } - // Deep-copies a managed reference: value fields ride the same JSON round-trip, and every nested - // [SerializeReference] is replaced with its own copy. Topology is preserved — two fields aliasing one nested - // instance alias one copy, and a cyclic graph terminates because each copy registers before its children. - // This is the Make-unique copier; the type-switch flows keep CreateInstancePreservingData, where reusing the - // nested instances is correct. + // Register each clone before cloning its children to preserve aliases and terminate cycles. public static object CloneManagedReferenceGraph(object source) => CloneManagedReferenceGraph(source, new Dictionary(ReferenceComparer.Instance)); @@ -414,7 +370,6 @@ private static object CloneManagedReferenceGraph(object source, Dictionary clones) { switch (value) @@ -443,7 +398,6 @@ private static object CloneManagedReferenceValue(object value, Dictionary EnumerateManagedReferenceFields(Type type) { const BindingFlags flags = @@ -468,7 +422,6 @@ private sealed class ReferenceComparer : IEqualityComparer int IEqualityComparer.GetHashCode(object obj) => RuntimeHelpers.GetHashCode(obj); } - // Parses Unity's "AssemblyName Namespace.TypeName" format; null when empty or unloadable. public static Type GetTypeFromTypename(string typename) { if (string.IsNullOrEmpty(typename)) return null; @@ -481,13 +434,7 @@ public static Type GetTypeFromTypename(string typename) return Type.GetType($"{fullName}, {assembly}", throwOnError: false); } - // The types the engine serializes natively as a field value. They have to be named one by one because - // Type.IsSerializable answers false for all of them: the engine writes their layout itself, so none carries - // [Serializable]. This is the half of "Unity can serialize this" IsSerializable cannot see. - // - // Membership was measured on Unity 6000.4, not assumed. Value types of the same family the engine does NOT - // serialize — Ray, Ray2D, Plane, RangeInt, Keyframe, GradientColorKey — are absent for that reason and must - // stay absent. Built-ins that do carry the attribute already pass the ordinary check. + // Unity serializes these built-in field types despite Type.IsSerializable returning false. private static readonly HashSet UnityNativeSerializableTypes = new() { typeof(Vector2), typeof(Vector3), typeof(Vector4), @@ -499,7 +446,6 @@ public static Type GetTypeFromTypename(string typename) typeof(PropertyName), typeof(UnityEngine.Rendering.SphericalHarmonicsL2), }; - // Types the argument PAGE offers: concrete, non-generic types Unity can serialize as a field value. public static bool IsValidGenericArgument(Type type) { if (type is null) return false; @@ -515,11 +461,7 @@ public static bool IsValidGenericArgument(Type type) (type.IsClass && type.IsSerializable); } - // Whether an argument the FIELD already determines may close a parameter. IsValidGenericArgument demands - // serializability because its page has to stay a list a human can read; that is the wrong bar here, where - // nobody is browsing and whether the argument must be serializable at all depends on where the parameter - // lands — the question GenericArgumentRequirement answers. The structural half is not a matter of taste: - // MakeGenericType itself refuses an open definition, a pointer, a by-ref and void. + // Inferred arguments require serializability only when the parameter reaches a field serialized by value. public static bool IsAcceptableGenericArgument(Type openDefinition, Type parameter, Type argument) { if (argument is null || argument.ContainsGenericParameters) return false; @@ -536,8 +478,7 @@ public static ManagedTypeName GetMissingTypeName(SerializedProperty property) => public static string GetMissingTypeDisplayName(SerializedProperty property) => GetMissingTypeName(property).DisplayName; - // The best Smart Fix candidate for this property's missing reference, never applied automatically. The pool - // is constrained to what the picker would offer, so a suggestion can never violate the field's constraint. + // Rank only picker-compatible candidates so quick repair cannot bypass the field constraints. public static bool TryGetRepairSuggestion(SerializedProperty property, Type[] baseTypes, out SerializeReferenceRepairSuggestions.RepairCandidate suggestion) { @@ -565,12 +506,9 @@ public static bool TryGetRepairSuggestion(SerializedProperty property, Type[] ba return false; } - // Shared by both notices and the quick-apply button so the copy never drifts. The separator before it is - // decoration each notice renders itself, so it is not part of the label. public static string GetSuggestionLabel(SerializeReferenceRepairSuggestions.RepairCandidate suggestion) => $"→ {TypeSelectorHelpers.GetTypeSelectorTitle(suggestion.Type)}"; - // Shared by both notices so the two never drift. public static string GetSuggestionDetail(SerializeReferenceRepairSuggestions.RepairCandidate suggestion) => $"Suggested: {suggestion.Type.FullName}, {suggestion.Type.Assembly.GetName().Name}.\n" + $"Reason: {suggestion.Reason}.\nClick to re-point this reference to it, keeping its data."; @@ -602,10 +540,7 @@ public static bool TryGetAssetLocation(SerializedProperty property, out string a return AssetDatabase.TryGetGUIDAndLocalFileIdentifier(target, out _, out fileId); } - // The YAML document backing the stored reference, plus whether the repair must be applied in memory. A saved - // asset is repaired by rewriting its file. A Prefab Mode object has no path of its own — it comes from the - // stage, and the document id is matched back to the asset — and must be repaired in memory, since the open - // stage holds a separate copy that would overwrite a file rewrite on save. + // Prefab Mode edits must stay in memory; its open copy would overwrite a YAML repair on save. public static bool TryGetRepairLocation(SerializedProperty property, out string assetPath, out long fileId, out bool inMemory) { inMemory = false; @@ -639,9 +574,7 @@ public static bool TryGetRepairLocation(SerializedProperty property, out string return false; } - // GlobalObjectId.targetObjectId is the scene-local file id matching the YAML document anchor. Bails for a - // dirty scene, whose YAML would not match the live object, and for prefab-instance overrides, whose data - // lives in the source prefab. + // Scene YAML is usable only while the scene is clean and the reference is not a prefab override. private static bool TryGetSceneLocation(Object target, GameObject go, out string assetPath, out long fileId) { assetPath = null; @@ -686,7 +619,7 @@ private static bool TryMatchAssetFileId(PrefabStage stage, Object target, GameOb var root = stage.prefabContentsRoot.transform; while (transform != root) { - if (transform.parent is null) return false; // object is not under the stage root + if (transform.parent is null) return false; indices.Insert(0, transform.GetSiblingIndex()); transform = transform.parent; } @@ -713,14 +646,10 @@ private static bool TryMatchAssetFileId(PrefabStage stage, Object target, GameOb return AssetDatabase.TryGetGUIDAndLocalFileIdentifier(assetComponents[componentIndex], out _, out fileId); } - // Strict and per-property: only a field whose own recorded type fails to resolve counts as missing, so a - // legitimately empty field is never flagged. public static bool TryGetMissingReferenceId(SerializedProperty property, out long referenceId) => TryGetMissingType(property, out referenceId, out _); - // Opens the dropdown's own picker to choose the type a missing reference should resolve to, narrowed the - // same way so a repair cannot pick a type the attribute excludes. Unlike the authoring dropdown it does - // offer hidden types: hiding governs what may be authored, not what a broken reference may become. + // Repair permits hidden types because visibility limits authoring, not recovery of existing data. public static void ShowFixTypeSelector(SerializedProperty property, Rect screenRect, Action onFixed, Type[] baseTypes = null) { var fieldType = GetFieldType(property); @@ -748,8 +677,7 @@ public static void ShowFixTypeSelector(SerializedProperty property, Rect screenR }); } - // Re-points a missing reference at newType, keeping its stored data: a saved asset by rewriting the YAML and - // reimporting, a Prefab Mode object in memory. + // Repair saved assets through YAML and open Prefab Mode objects through their live serialized state. public static bool TryFixMissingType(SerializedProperty property, Type newType) { if (newType is null) return false; @@ -781,8 +709,7 @@ public static bool TryFixMissingType(SerializedProperty property, Type newType) return repaired; } - // Unity's object-level missing-types banner is drawn from a flag cached when the editor is built and only - // clears on a genuine reselection, so the objects are deselected and reselected across the next ticks. + // Unity caches the missing-types banner on editor creation; reselection rebuilds it after repair. private static void ScheduleInspectorRebuild() { var selection = Selection.objects; @@ -815,7 +742,6 @@ private static bool TryFixMissingTypeInMemory(SerializedProperty property, Type EditorUtility.SetDirty(target); property.serializedObject.Update(); - // Mark the owning scene dirty so the in-memory repair is offered for save. var scene = (target as Component)?.gameObject.scene ?? (target as GameObject)?.scene ?? default; if (scene.IsValid()) EditorSceneManager.MarkSceneDirty(scene); @@ -855,9 +781,7 @@ public static bool TryClearMissingReferenceInMemory(string assetPath, long rid, return false; } - // The live MonoBehaviours of an asset that is unsafe to rewrite, matched by missing-reference identity - // rather than file id, since the open stage remaps ids. Only MonoBehaviours are probed, because - // GetManagedReferencesWithMissingTypes errors on other types. + // Open stages remap file IDs, so match missing-reference identities on live MonoBehaviours. private static IEnumerable EnumerateOpenMissingTypeTargets(string assetPath) { var stage = PrefabStageUtility.GetCurrentPrefabStage(); @@ -872,16 +796,13 @@ private static IEnumerable EnumerateOpenMissingTypeTargets(string assetP if (mb != null) yield return mb; } - // Clears the fixed entry and everything it transitively referenced, which would otherwise linger as orphans - // and keep Unity's missing-types banner raised. A member referenced from OUTSIDE the subtree is kept, along - // with everything only reachable through it, since clearing it would leave that pointer unrepairable. + // Preserve any repaired-subtree member referenced from outside it, including that member's descendants. private static void ClearMissingSubtree(Object target, long rootReferenceId) { var dataByRid = new Dictionary(); foreach (var entry in SerializationUtility.GetManagedReferencesWithMissingTypes(target)) dataByRid[entry.referenceId] = entry.serializedData; - // The transitive closure of the fixed entry — the candidates for clearing. var closure = new HashSet(); var pending = new Stack(); pending.Push(rootReferenceId); @@ -935,8 +856,6 @@ private static void ClearMissingSubtree(Object target, long rootReferenceId) } } - // The rid pointers inside a missing entry's payload. The look-behind keeps a field that merely ends in "rid" - // from reading as one. private static IEnumerable EnumerateRidPointers(string data, long self) { foreach (Match match in Regex.Matches(data ?? string.Empty, @"(? Regex.IsMatch(value, @"^-?\d+(\.\d+)?$"); - // Unity single-quotes scalars containing reserved characters, doubling embedded quotes. private static string UnquoteYaml(string value) => value.Length >= 2 && value[0] == '\'' && value[^1] == '\'' ? value[1..^1].Replace("''", "'") @@ -1001,11 +918,7 @@ private static string Quote(string value) => #endregion #region Constraint map - // Maps every managed reference in the asset to the declared field type holding it, keyed by document file id - // and rid. A missing reference reads back null, but its field still reports the declared element type and - // the orphaned rid survives in the YAML, so the two together recover the constraint the picker should honor. - // References under a missing parent are unreachable here and fall back to an unconstrained picker, as do - // orphaned rids no field points at. + // Combine declared live field types with YAML IDs; missing parents and orphaned entries remain unconstrained. public static Dictionary<(long fileId, long rid), Type> BuildConstraintMap(string assetPath) { var map = new Dictionary<(long, long), Type>(); @@ -1055,8 +968,6 @@ private static string Quote(string value) => #endregion #region Cross references - // True when another field in the same object shares this property's rid, so edits to one bleed into the - // other. Happens after duplicating an array element or pasting. public static bool HasSharedReference(SerializedProperty property) { if (property.managedReferenceValue is null) return false; @@ -1068,9 +979,7 @@ public static bool HasSharedReference(SerializedProperty property) return GetReferenceIdCounts(property.serializedObject).TryGetValue(id, out var count) && count > 1; } - // The 1-based badge number of this property's shared group, or 0 when it is not shared. Numbering follows - // each rid's first appearance in document order and is shared by both notices, so two fields aliasing one - // instance always read the same number in either inspector mode. + // Number shared groups by first appearance so both inspector modes assign the same badge. public static int GetSharedReferenceIndex(SerializedProperty property) { if (property.managedReferenceValue is null) return 0; @@ -1079,12 +988,10 @@ public static int GetSharedReferenceIndex(SerializedProperty property) return GetSharedReferenceIndices(property.serializedObject).TryGetValue(id, out var index) ? index : 0; } - // How many fields carry each id, built by one full-object walk and shared across a repaint. private static int _aliasFrame = -1; private static SerializedObject _aliasSerializedObject; private static readonly Dictionary AliasCounts = new(); - // Each id's first-sighting order, so badge numbers follow document order rather than the dictionary's. private static readonly List AliasOrder = new(); private static Dictionary GetReferenceIdCounts(SerializedObject serializedObject) @@ -1101,12 +1008,11 @@ private static Dictionary GetReferenceIdCounts(SerializedObject seria var id = other.managedReferenceId; if (id < 0) return false; - if (!AliasCounts.TryGetValue(id, out var count)) AliasOrder.Add(id); // first sighting → record its order + if (!AliasCounts.TryGetValue(id, out var count)) AliasOrder.Add(id); AliasCounts[id] = count + 1; return false; }); - // The counts were rebuilt, so the maps derived from them are stale. _sharedIndicesFrame = -1; _sharedPathsFrame = -1; _aliasFrame = frame; @@ -1114,7 +1020,6 @@ private static Dictionary GetReferenceIdCounts(SerializedObject seria return AliasCounts; } - // Each shared id's badge number. Separate from the counts memo, so it is built only when a notice asks. private static int _sharedIndicesFrame = -1; private static SerializedObject _sharedIndicesObject; private static readonly Dictionary SharedIndices = new(); @@ -1164,8 +1069,7 @@ public static List GetSharedReferenceAliasPaths(SerializedProperty prope return result; } - // The whole group in document order, this property included. Both drawers cycle through this canonical - // order, so they walk the members the same way. It is a per-frame memo: read it immediately, never cache it. + // Both drawers consume this per-frame document order immediately; never retain the returned list. public static IReadOnlyList GetSharedReferenceGroupPaths(SerializedProperty property) { if (property.managedReferenceValue is null) return Array.Empty(); @@ -1176,10 +1080,8 @@ public static IReadOnlyList GetSharedReferenceGroupPaths(SerializedPrope : (IReadOnlyList)Array.Empty(); } - // How many alias paths the tooltip lists before folding the rest into "…and N more". private const int MaxDetailAliasPaths = 6; - // Built here so both notices always tell the same story. public static string BuildSharedReferenceDetail(SerializedProperty property) { var builder = new StringBuilder( @@ -1202,7 +1104,6 @@ public static string BuildSharedReferenceDetail(SerializedProperty property) return builder.ToString(); } - // The same paths recur on every repaint, so the nicified form is built once. private static readonly Dictionary DisplayPathCache = new(); // The inspector's own labels for a property path: "sidearms.Array.data[1].onHitEffect" reads as @@ -1234,7 +1135,6 @@ public static string GetPropertyDisplayPath(string propertyPath) return DisplayPathCache[propertyPath] = builder.ToString(); } - // Each shared id's member paths in document order, built only when a notice needs them. private static int _sharedPathsFrame = -1; private static SerializedObject _sharedPathsObject; private static readonly Dictionary> SharedPathsById = new(); @@ -1279,23 +1179,18 @@ public static void InvalidateSharedReferenceCache() private static void InvalidateAliasMemoOnUndoRedo() => Undo.undoRedoPerformed += InvalidateSharedReferenceCache; - // Breaks an alias by replacing the reference with an independent clone carrying the same data; a fresh - // instance gets a new rid on assignment. public static void MakeReferenceUnique(SerializedProperty property) { var persistent = property.Persistent(); var current = persistent.managedReferenceValue; if (current is null) return; - // Make unique promises independence all the way down, which a shallow clone would not give. persistent.SetManagedReferenceAndApply(CloneManagedReferenceGraph(current)); - // A repaint in this same frame would otherwise keep painting the notice on both ex-members. InvalidateSharedReferenceCache(); } - // Visits every managed-reference property, nested values included, stopping when the visitor returns true. - // A revisited rid is still reported, but its children are not re-entered, or a cyclic graph would loop. + // Report revisited IDs but do not enter their children, so cyclic reference graphs terminate. private static void TraverseManagedReferences(SerializedObject serializedObject, Func visit) { using var iterator = serializedObject.GetIterator(); diff --git a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Extensions/SerializeReferenceLinker.cs b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Extensions/SerializeReferenceLinker.cs index 6b75fe57..ef9effaa 100644 --- a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Extensions/SerializeReferenceLinker.cs +++ b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Extensions/SerializeReferenceLinker.cs @@ -5,12 +5,8 @@ // ReSharper disable once CheckNamespace namespace Aspid.FastTools.SerializeReferences.Editors { - // The inverse of Make Unique: shares one managed-reference instance across several fields of the same object. - // There is no rid setter, so sharing means assigning the SAME instance to both paths — Unity then keeps them on - // one managedReferenceId, which is exactly the aliasing the shared-reference notice detects. internal static class SerializeReferenceLinker { - // A sibling managed reference this field could be linked to. public readonly struct LinkCandidate { public readonly long Rid; @@ -66,7 +62,7 @@ public static List CollectLinkCandidates(SerializedProperty prope var type = value.GetType(); if (fieldType != null && !fieldType.IsAssignableFrom(type)) continue; - if (!seen.Add(rid)) continue; // one representative per shared instance + if (!seen.Add(rid)) continue; result.Add(new LinkCandidate(rid, type, path)); } @@ -75,7 +71,6 @@ public static List CollectLinkCandidates(SerializedProperty prope return result; } - // The rids held by every managed-reference ancestor, found by walking the property's path prefixes. private static HashSet CollectAncestorRids(SerializedProperty property) { var rids = new HashSet(); @@ -97,7 +92,6 @@ private static HashSet CollectAncestorRids(SerializedProperty property) return rids; } - // Points this field at the instance held by sourcePath, sharing its rid. public static bool LinkTo(SerializedProperty property, string sourcePath) { if (property is null || string.IsNullOrEmpty(sourcePath)) return false; diff --git a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Extensions/SerializeReferenceMissingListGuard.cs b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Extensions/SerializeReferenceMissingListGuard.cs index 292e4892..4407df27 100644 --- a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Extensions/SerializeReferenceMissingListGuard.cs +++ b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Extensions/SerializeReferenceMissingListGuard.cs @@ -4,18 +4,6 @@ // ReSharper disable once CheckNamespace namespace Aspid.FastTools.SerializeReferences.Editors { - // Preserves a [SerializeReference] list's missing-type element across a list resize. Unity keeps a missing - // managed reference only in the asset's YAML — the live object reads it back as the null id -2 — so adding an - // element makes the next SAVE re-serialize the array from that null state, collapsing the named entry into the - // anonymous sentinel and destroying the type identity and its payload on disk. - // - // The loss lands at save time, not on ApplyModifiedProperties, so the guard sits on the save boundary: the - // pre-save callback snapshots every top-level missing element from the still-pristine YAML and queues a pass that - // re-reads the written file and re-materializes anything the save collapsed. An intact element fails the - // restore's own "slot is empty" check, so a normal save is a no-op and the pass never loops. - // - // Saved assets only, and only top-level array elements — the shape a list "+" destroys. A single field or a - // nested pointer is never resized, so it is not at risk. internal sealed class SerializeReferenceMissingListGuard : AssetModificationProcessor { // Consumed once by the post-save pass and dropped, so a later save re-snapshots from the then-current file. @@ -42,7 +30,6 @@ private static string[] OnWillSaveAssets(string[] paths) return paths; } - // Captures each at-risk element with the exact RefIds entry text needed to re-materialize it after the save. private static List SnapshotMissingArrayElements(string assetPath) { var result = new List(); @@ -63,7 +50,6 @@ private static List SnapshotMissingArrayElements(string assetPath) return result; } - // Restores any snapshotted element the save collapsed, then reimports so the live object picks it up. private static void RestoreAfterSave(string assetPath) { if (!PendingByPath.TryGetValue(assetPath, out var snapshots)) return; diff --git a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Extensions/SerializeReferenceMovedFromResolver.cs b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Extensions/SerializeReferenceMovedFromResolver.cs index 149c67c1..bcd7a256 100644 --- a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Extensions/SerializeReferenceMovedFromResolver.cs +++ b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Extensions/SerializeReferenceMovedFromResolver.cs @@ -7,21 +7,14 @@ // ReSharper disable once CheckNamespace namespace Aspid.FastTools.SerializeReferences.Editors { - // Resolves a stored, no longer loadable type identity to the type declaring it as its old name via [MovedFrom]. - // Unity migrates such references in memory at load, but the YAML keeps the old name until the asset is re-saved, - // so a YAML-level scan keeps seeing the stale identity. This is what lets those entries read as a pending - // migration instead of a breakage, and backs the bulk "Migrate all" that bakes the rename into the files. internal static class SerializeReferenceMovedFromResolver { - // Stored-type key -> the single authoritative target; null means no claimant or an ambiguous pair. Negative - // results are cached too, since the breakage paths probe every unresolved entry. [MovedFrom] declarations - // only change with a recompile, which resets this with the domain, so nothing invalidates it. + // Cache unresolved and ambiguous identities too; domain reloads invalidate rename metadata. private static readonly Dictionary Cache = new(StringComparer.Ordinal); - private static readonly char[] NestedSeparators = { '/', '+' }; + private static readonly char[] _nestedSeparators = { '/', '+' }; - // True when exactly one eligible type declares a [MovedFrom] matching the stored identity. Two claimants - // make the rename non-authoritative, so the resolver refuses to pick between them. + // A rename is authoritative only when exactly one eligible type claims the stored identity. public static bool TryResolve(ManagedTypeName stored, out Type target) { target = null; @@ -60,9 +53,7 @@ private static Type ResolveUncached(ManagedTypeName stored) return found; } - // Matches the candidate's recorded old identity against the stored class and, when declared, namespace and - // assembly. storedClass must already be normalized. The attribute's data is not public API, so it is read - // reflectively and any failure counts as "no match". + // Read non-public rename metadata reflectively; failures leave the identity unmatched. public static bool MatchesOldIdentity(Type candidate, ManagedTypeName stored, string storedClass) { try @@ -107,8 +98,6 @@ public static bool MatchesOldIdentity(Type candidate, ManagedTypeName stored, st return false; } - // Strips generic-arity decoration and nesting so both sides compare on the bare simple name: - // "Modifier`1[[System.Single, mscorlib]]" and "Outer/Modifier" both reduce to "Modifier". public static string NormalizeClassName(string className) { if (string.IsNullOrEmpty(className)) return string.Empty; @@ -119,13 +108,12 @@ public static string NormalizeClassName(string className) var tick = className.IndexOf('`'); if (tick >= 0) className = className[..tick]; - var slash = className.LastIndexOfAny(NestedSeparators); + var slash = className.LastIndexOfAny(_nestedSeparators); if (slash >= 0) className = className[(slash + 1)..]; return className.Trim(); } - // Returns the recorded old value when the slot's "*HasChanged" flag is set, and the current one otherwise. private static string ReadMovedSlot(Type dataType, object data, string valueField, string changedField, string current) { var changed = dataType.GetField(changedField, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); diff --git a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Extensions/SerializeReferenceNesting.cs b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Extensions/SerializeReferenceNesting.cs index 9b5c03c5..5455ed6d 100644 --- a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Extensions/SerializeReferenceNesting.cs +++ b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Extensions/SerializeReferenceNesting.cs @@ -5,13 +5,9 @@ // ReSharper disable once CheckNamespace namespace Aspid.FastTools.SerializeReferences.Editors { - // Decides whether a child of an assigned managed reference is drawn by this package or handed back to Unity. - // Shared by both inspector modes so one asset never behaves differently between them. internal static class SerializeReferenceNesting { - // A managed-reference graph may be cyclic and each level draws the next, so an unbounded descent ends in an - // uncatchable StackOverflowException that takes the Editor and the unsaved scene with it. Past the cap a - // child falls back to Unity's drawing, which stops at the reference instead of following it. + // Stop recursive drawing at the cap so cyclic managed references cannot overflow the editor stack. internal const int MaxDepth = 8; internal static bool DrawsOwnHeader(SerializedProperty child, int depth) @@ -23,9 +19,7 @@ internal static bool DrawsOwnHeader(SerializedProperty child, int depth) return !DrawnByUnity(child); } - // True when Unity already has a body to draw, so drawing it here would discard the author's own UI. Plain - // decorators like [Header] are deliberately excluded: the caller re-emits them, so one never costs the field - // its dropdown. + // Respect custom drawers; decorators alone do not replace the managed-reference picker. internal static bool DrawnByUnity(SerializedProperty child) { var field = child.GetFieldInfo(); @@ -36,7 +30,6 @@ internal static bool DrawnByUnity(SerializedProperty child) CustomDrawerRegistry.DeclaresDrawnAttribute(field); } - // Decides whether the field offers an expand arrow at all. internal static bool HasVisibleChildren(SerializedProperty property) { var iterator = property.Copy(); diff --git a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Extensions/SerializeReferenceRepairSuggestions.cs b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Extensions/SerializeReferenceRepairSuggestions.cs index fd791bab..be705f97 100644 --- a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Extensions/SerializeReferenceRepairSuggestions.cs +++ b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Extensions/SerializeReferenceRepairSuggestions.cs @@ -9,14 +9,8 @@ // ReSharper disable once CheckNamespace namespace Aspid.FastTools.SerializeReferences.Editors { - // Ranking engine behind the missing-type Smart Fix suggestion: from the stored type identity, the field names - // recorded for it and the field's declared constraint, it orders repair candidates and surfaces the best one. - // The pool is the same set the type picker would offer, so a suggestion can never be a type the picker refuses, - // and it is never auto-applied — the user always clicks. internal static class SerializeReferenceRepairSuggestions { - // A scored repair candidate: the type the reference could be re-pointed to, its heuristic score (highest - // wins) and a short human-readable reason. public readonly struct RepairCandidate { public readonly Type Type; @@ -31,11 +25,8 @@ public RepairCandidate(Type type, float score, string reason) } } - // Below this confidence the heuristics are too weak to offer at all. public const float MinScore = 0.6f; - // How much the field-shape overlap can add, lifting a marginal name match over the threshold and breaking - // ties between equally-named candidates. private const float FieldShapeBonus = 0.2f; // IMGUI repaints every frame, so the TypeCache-scanning ranking is cached per (asset, document, rid) with a @@ -113,7 +104,6 @@ private static IEnumerable EnumerateCandidates(Type constraint) } } - // Base score before the field-shape bonus; 0 means no match. private static float ScoreCandidate(ManagedTypeName stored, string storedClass, Type candidate, out string reason) { // A matching [MovedFrom] is an authoritative rename, so it tops the ranking. @@ -125,7 +115,6 @@ private static float ScoreCandidate(ManagedTypeName stored, string storedClass, var candidateClass = SerializeReferenceMovedFromResolver.NormalizeClassName(candidate.Name); - // Same class name in another namespace or assembly: the class was moved. if (string.Equals(candidateClass, storedClass, StringComparison.Ordinal)) { reason = "same type name"; @@ -138,7 +127,6 @@ private static float ScoreCandidate(ManagedTypeName stored, string storedClass, return 0.6f; } - // A near miss, surfaced only once the field-shape bonus lifts it over the threshold. if (LevenshteinAtMost(candidateClass, storedClass, 2)) { reason = "similar name"; @@ -149,7 +137,6 @@ private static float ScoreCandidate(ManagedTypeName stored, string storedClass, return 0f; } - // Fraction of stored field names that exist on the candidate. private static float FieldShapeOverlap(HashSet storedFields, Type candidate) { var candidateFields = GetSerializedFieldNames(candidate); @@ -159,7 +146,6 @@ private static float FieldShapeOverlap(HashSet storedFields, Type candid return (float)matched / storedFields.Count; } - // Unity's rule: public instance fields plus private [SerializeField] ones, base chain included. private static HashSet GetSerializedFieldNames(Type type) { var names = new HashSet(StringComparer.Ordinal); @@ -180,7 +166,6 @@ private static HashSet GetSerializedFieldNames(Type type) return names; } - // Bounded Levenshtein with early bail-out once a row's best distance exceeds the bound. private static bool LevenshteinAtMost(string a, string b, int maxDistance) { if (a is null || b is null) return false; @@ -212,7 +197,6 @@ private static bool LevenshteinAtMost(string a, string b, int maxDistance) } #region Cached ranking - // The factory runs only on a cache miss. public static IReadOnlyList GetCached( string assetPath, long fileId, @@ -235,7 +219,6 @@ public static IReadOnlyList GetCached( return result; } - // Called after a repair, since the candidate set has changed. public static void ClearCache() { Cache.Clear(); diff --git a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Extensions/SerializeReferenceRidColor.cs b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Extensions/SerializeReferenceRidColor.cs index 80a3569f..44960ccd 100644 --- a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Extensions/SerializeReferenceRidColor.cs +++ b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Extensions/SerializeReferenceRidColor.cs @@ -3,9 +3,6 @@ // ReSharper disable once CheckNamespace namespace Aspid.FastTools.SerializeReferences.Editors { - // Deterministic colors for shared-reference visuals, both entry points sharing one green->magenta palette. - // ForRid hashes the rid, for the window's SHARED chip where there is no per-object badge; ForIndex walks the - // badge number around the band instead, so consecutive badges are maximally separated. internal static class SerializeReferenceRidColor { // Adding this fraction per step drops each next hue into the largest remaining gap. @@ -22,7 +19,6 @@ internal static class SerializeReferenceRidColor // to hit a common perceived luminance instead. private const float TargetLuminance = 0.6f; - // A hue the eye sees as dark is lifted toward mid-brightness, never blown out chasing the target. private const float MaxValue = 0.92f; // A Knuth multiplicative hash spreads the rid across the hue circle before the golden-ratio rotation. @@ -33,14 +29,12 @@ public static Color ForRid(long rid) return FromFraction(fraction); } - // The 1-based badge number, so its color and its number stay in lock-step. public static Color ForIndex(int index) { var fraction = (index * GoldenRatioConjugate) % 1f; return FromFraction(fraction); } - // Hue into the safe green→magenta band, value normalized to the common perceived luminance. private static Color FromFraction(float fraction) { var hue = SafeHueMin + fraction * (SafeHueMax - SafeHueMin); diff --git a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Extensions/SerializeReferenceSharedNavigation.cs b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Extensions/SerializeReferenceSharedNavigation.cs index d1d2e292..9bab9fcf 100644 --- a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Extensions/SerializeReferenceSharedNavigation.cs +++ b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Extensions/SerializeReferenceSharedNavigation.cs @@ -7,10 +7,6 @@ // ReSharper disable once CheckNamespace namespace Aspid.FastTools.SerializeReferences.Editors { - // IMGUI-side group navigation for shared references: clicking the "Shared reference #N" message picks the - // group's next member in document order, expands the parents hiding it, scrolls to it once painted, and pulses - // every member in the group color. The UIToolkit field navigates its element tree instead; only the pulse timings - // and the document-order cycling are kept in lock-step between the two. internal static class SerializeReferenceSharedNavigation { // Full tint for the hold fraction, then a linear fade. Mirrors the UIToolkit field so both pulses match. @@ -18,13 +14,11 @@ internal static class SerializeReferenceSharedNavigation private const double FlashSeconds = 1.6; private const float FlashHoldFraction = 0.35f; - // Scrolls the revealed member a quarter down, so a line or two of context stays above it. private const float RevealViewportFraction = 0.25f; // A revealed member only gets a rect once painted; the reveal is dropped if no repaint reports one in time. private const double RevealTimeoutSeconds = 1.0; - // The group member the next repaint should scroll to. private static int _revealTarget; private static long _revealRid; private static string _revealPath; @@ -34,7 +28,6 @@ internal static class SerializeReferenceSharedNavigation // walk the whole group. private static readonly Dictionary<(int target, long rid), string> NavigationCursor = new(); - // The active pulse: every drawn member of the group except the clicked one tints until the deadline. private static int _flashTarget; private static long _flashRid; private static string _flashExceptPath; @@ -56,7 +49,6 @@ public static void NavigateFrom(SerializedProperty property) var selfPath = property.propertyPath; - // The pulse covers every drawn member, so the whole group is revealed, not just the scroll target. foreach (var path in group) if (path != selfPath) ExpandAncestors(property.serializedObject, path); @@ -83,8 +75,6 @@ public static void NavigateFrom(SerializedProperty property) _revealUntil = EditorApplication.timeSinceStartup + RevealTimeoutSeconds; } - // Reports where a shared field was painted; call with its FULL rect, header and children. The inspector - // scrolls to it when it is the pending reveal's member. public static void RevealIfPending(SerializedProperty property, Rect fieldRect) { if (Event.current.type != EventType.Repaint) return; @@ -102,7 +92,6 @@ public static void RevealIfPending(SerializedProperty property, Rect fieldRect) EditorApplication.delayCall += () => ScrollTo(screenRect); } - // True while the pulse covers this field, with the overlay's current fade alpha. public static bool TryGetFlashAlpha(SerializedProperty property, out float alpha) { alpha = 0f; @@ -177,7 +166,6 @@ private static void ScrollTo(Rect screenRect) var viewport = scrollView.contentViewport.worldBound; var targetY = screenRect.y - window.position.y; - // Already inside the viewport, so the pulse alone is enough. if (targetY >= viewport.yMin + 4f && targetY + screenRect.height <= viewport.yMax - 4f) return; var offset = scrollView.scrollOffset; diff --git a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Extensions/SerializeReferenceTemplates.cs b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Extensions/SerializeReferenceTemplates.cs index e15b7829..db953b0e 100644 --- a/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Extensions/SerializeReferenceTemplates.cs +++ b/Aspid.FastTools/Packages/tech.aspid.fasttools/Editor/Scripts/SerializeReferences/Extensions/SerializeReferenceTemplates.cs @@ -6,9 +6,6 @@ // ReSharper disable once CheckNamespace namespace Aspid.FastTools.SerializeReferences.Editors { - // Durable, project-scoped named templates for managed-reference instances: a stored type plus its JSON payload, - // rehydrated into an independent instance on use — the persistent counterpart to the session-only clipboard. - // The JSON round-trip is JsonUtility-based, so nested [SerializeReference] children are not preserved. internal static class SerializeReferenceTemplates { private const string KeyPrefix = "Aspid.FastTools.SerializeReference.Templates."; @@ -62,7 +59,6 @@ public static void Save(string name, object value) Persist(store); } - // Returns the templates whose type still resolves, in stored order, and prunes the rest. public static List