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