Skip to content

Release 1.0: Aspid.FastTools rename, ID System, UIToolkit API#8

Open
VPDPersonal wants to merge 199 commits into
mainfrom
Develop
Open

Release 1.0: Aspid.FastTools rename, ID System, UIToolkit API#8
VPDPersonal wants to merge 199 commits into
mainfrom
Develop

Conversation

@VPDPersonal
Copy link
Copy Markdown
Owner

@VPDPersonal VPDPersonal commented Mar 4, 2026

First public release of Aspid.FastTools (com.aspid.fasttools 1.0.0). The package was renamed from Aspid.UnityFastTools, restructured into a Unity package + standalone generators solution, and got a large feature pass.

Source generators

ProfilerMarkersGenerator emits a ProfilerMarker per this.Marker() call site, keyed by enclosing type / member / line — supports .WithName(literal) and $"...". IdStructGenerator emits IId boilerplate for partial structs marked with [UniqueId], and reports AFID001 / AFID002 when the struct isn't partial or already declares the generated members. Pipeline data is value-equatable to keep Roslyn's incremental cache stable (regression-tested).

ID System (beta)

A single IdRegistry (ScriptableObject) maps names ↔ stable integer IDs for a given IId struct. IdRegistryResolver lazily indexes Type AQN → registry and updates incrementally via AssetPostprocessor. The inspector (RegistryEditorCore) does C#-identifier validation, full Undo, an explicit Review / Clean-up flow, Sort/Group toolbar, and a manual Next ID with a backward-step warning. There's also an IdField VisualElement and an Open-Registry shortcut on the IdStruct drawer.

Types & Enums

SerializableType / SerializableType<T> serialize System.Type via assembly-qualified names with lazy resolution; TypeSelectorWindow is the shared IMGUI/UIToolkit picker. ComponentTypeSelectorAttribute and the TypeAllow enum tighten what can be picked. EnumValues<TValue> is a serializable enum→value dictionary with [Flags] support and a populate-missing context menu.

UIToolkit

Large fluent API over VisualElement covering layout, sizing, IStyle, USS, child management, callbacks, IBindable, and per-element extensions (Image, Button, Foldout, HelpBox, List, ProgressBar, Slider, TextElement, Field, IMGUIContainer, INotifyValueChanged, manipulators, PropertyField). An optional Aspid.FastTools.Unity.VisualElements.Math satellite assembly adds INotifyValueChanged extensions for float2/3/4 and int2/3/4, gated by com.unity.mathematics through versionDefines.

Internal editor components

AspidLabel, AspidBox, AspidGradientButton, AspidHelpBox, AspidInspectorHeader, AspidAnimatedLogo, AspidAnimatedTitle, AspidAnimatedDotsBackground, AspidDividingLine, AspidHoverGradientOverlay — each with a fluent Preset and USS-driven custom style properties. Shared helpers (AspidStyles, StatusStyle, ThemeStyle, InlineStyle<T>, ICustomStyleExtensions) live next to them.

Editor utilities

Fluent SerializedProperty extensions, VerticalScope / HorizontalScope / ScrollViewScope, MonoScript.GetScriptName(WithIndex), and a WelcomeWindow (Tools → Aspid FastTools → Welcome) that reads package.json to install samples.

Design system

BRANDBOOK.html documents the palette, H1–H7 type scale and the 10 internal components. USS classes follow BEM (aspid-fasttools-{block}__{element}--{modifier}); custom properties follow a positional grammar (--{prefix}-{group}-{role}[-{state}][-{tone}]).

Samples

Under Samples~/ (UPM convention): Types, EnumValues, Ids, ProfilerMarkers, VisualElements.

Layout & rename

Folders renamed from Aspid.UnityFastTools(.Generators) to Aspid.FastTools(.Generators). Public API moved into Aspid.FastTools.{Types,Enums,Ids,UIElements,Editors}. Assemblies split into Aspid.FastTools (pure C#), Aspid.FastTools.Unity (runtime), the Mathematics satellite, and Aspid.FastTools.Unity.Editor.

Docs & tooling

Synced README.md / README_RU.md (root + Documentation/), DESIGN.md, CLAUDE.md. The Aspid.FastTools.Claude plugin is added as a git submodule, and there's local Claude Code automation — a PostToolUse hook that rebuilds the generators DLL on edits, plus the build-generator / sync-readmes skills and code-reviewer / uss-bem-checker subagents.

Closes #9
Closes #11

@VPDPersonal VPDPersonal linked an issue May 10, 2026 that may be closed by this pull request
This was referenced May 10, 2026
@VPDPersonal VPDPersonal added type: feature New feature or capability area: runtime Runtime / player code area: editor Editor-only code area: generator Roslyn source generator area: samples Sample projects status: needs-review Ready for review labels May 10, 2026
@VPDPersonal VPDPersonal added this to the v1.0 milestone May 10, 2026
@VPDPersonal VPDPersonal moved this to In review in Aspid.FastTools May 10, 2026
@VPDPersonal
Copy link
Copy Markdown
Owner Author

VPDPersonal commented May 10, 2026

@claude one-time review of this release PR. Focus on:

  • correctness of the source generators (ProfilerMarkersGenerator, IdStructGenerator) and their incremental-cache safety
  • Unity / Editor assembly boundary — Unity/Runtime/ must not depend on UnityEditor
  • public API of the ID System (beta) — naming, generic handling, registry-resolver invariants
  • USS class names follow the BEM grammar and custom properties follow the positional grammar described in CLAUDE.md
  • anything that looks broken, surprising, or likely to bite a consumer of the package

Do not push commits — just leave a single review comment summary.

@claude
Copy link
Copy Markdown

claude Bot commented May 10, 2026

Claude finished @VPDPersonal's task in 6m 31s —— View job


PR Review — Release 1.0

  • Read CLAUDE.md and repo guidelines
  • Review source generators (ProfilerMarkers, IdStruct) and incremental-cache safety
  • Review Unity/Editor assembly boundary
  • Review ID System public API
  • Review USS/BEM naming conventions
  • Identify any broken or surprising issues
  • Post review summary

Source Generators — Incremental Cache

Overall: solid. Both generators follow the three-stage predicate → transform → emit pattern correctly. No ISymbol or SyntaxNode is stored in pipeline data; every data struct is a readonly struct with explicit IEquatable<T> covering all fields. IncrementalCacheTests provides a proper regression guard.

Specific things confirmed correct:

  • DiagnosticInfo stores file path + TextSpan + LinePositionSpan (primitives), not Location (which holds a SyntaxTree reference). Rebuilt in ToDiagnostic(). ✓
  • CancellationToken is passed through to GetDeclaredSymbol and GetEnclosingSymbol in both Transform methods. ✓
  • IdStructGenerator resolves IId via GetTypeByMetadataName per transform, which is acceptable — the same Compilation instance is shared within a run and Roslyn caches the lookup internally.
  • The filter .Where(static r => !r.IsEmpty) on the IdStruct provider prevents empty results from reaching RegisterSourceOutput. ✓

Minor — GetKeyword throws instead of emitting a diagnostic:

IdStructData.cs:58–63:

return symbol.TypeKind switch
{
    TypeKind.Class => "class",
    TypeKind.Struct => "struct",
    TypeKind.Interface => "interface",
    _ => throw new InvalidOperationException($"Unsupported containing type kind: {symbol.TypeKind}"),
};

An unrecognised TypeKind in a containing type (e.g. a future C# version or a malformed partial) throws an unhandled exception inside the generator pipeline, which surfaces as an opaque CS8785 generator error rather than a clean AFID diagnostic. Returning "class" as a safe fallback—or an early return from Transform—would be more resilient. Fix this →

Informational — ProfilerMarkersGenerator uses .Collect():

var collected = callsProvider.Collect();
context.RegisterSourceOutput(collected, GenerateCode);

Any single marker change forces re-generation of all marker extensions across the project. This is the right choice for now (all markers need to be in one file per type, so grouping is needed), but it's a consideration if this runs over very large projects. No action needed for 1.0.


Unity / Editor Assembly Boundary

CLEAN. Aspid.FastTools.Unity.asmdef has no UnityEditor reference. The two grep hits in VisualElementExtensions.Style.cs and IStyleExtensions.cs are false positives — SetUnityEditorTextRenderingMode is a UIToolkit IStyle property name, not a using UnityEditor; import. IdRegistry.Editor.cs is correctly gated with #if UNITY_EDITOR.


ID System — Public API

🔴 Critical: IdRegistry cache is permanently empty in player builds

IdRegistry.cs:36–45, 73–79:

[field: NonSerialized]
public bool IsCacheDirty { get; private set; }   // starts false

protected virtual void OnValidate() =>
    IsCacheDirty = true;                          // Editor-only lifecycle

public void EnsureCache()
{
    if (!IsCacheDirty) return;   // short-circuits — cache never built at runtime
    RebuildCache();
    IsCacheDirty = false;
}

OnValidate is Editor-only (Unity does not call it in player builds). IsCacheDirty is [NonSerialized] so it deserializes as false. There is no OnEnable or Awake override. In a built player, every call to TryGetId, TryGetName, and Contains short-circuits in EnsureCache and returns false/empty — the registry appears empty even though _ids/_names were deserialized correctly.

Fix: Add an OnEnable override (called in both Editor and player):

protected virtual void OnEnable() => IsCacheDirty = true;

Fix this →

IdRegistry<T> — typed wrapper discoverability

IdRegistry<T> (typed TryGetName(T, ...) and Contains(T)) exists and is documented in the README, but it has no [CreateAssetMenu] (correct — Unity can't instantiate generic SOs from the menu). What's non-obvious: a consumer wanting the typed API must declare [SerializeField] private IdRegistry<EnemyId> _registry; in their MonoBehaviour and drag in the (untyped) IdRegistry asset. Unity accepts this because IdRegistry<T> : IdRegistry : ScriptableObject. Worth a one-liner callout in the samples or README since the mismatch between "typed in code, untyped asset" surprises first-time consumers.

IdRegistry.Count vs. iteration

Count returns _ids.Length; GetEnumerator uses Math.Min(_ids.Length, _names.Length). The editor mutation path (RegistryEditorCore) guarantees the arrays stay in sync, so in practice this is fine. But exposing Count and IEnumerable with different bounds from different array lengths is a latent inconsistency that will silently produce wrong results if the arrays ever diverge (e.g., a corrupt asset). Low priority, but worth noting.

IdRegistryResolver.RegistryChanged — memory leak risk

IdRegistryResolver.RegistryChanged is a static event. Any editor window or drawer that subscribes to it must unsubscribe on Dispose/OnDisable. If a consumer window is closed without unsubscribing, the static delegate holds the window alive indefinitely. Not a correctness issue in Editor use, but worth documenting in the API.


USS / BEM Naming

BEM class names: correct. All observed class names follow aspid-fasttools-{block}[__{element}][--{modifier}].

Custom property grammar: two violations:

In Aspid-FastTools-AspidGradientButton.uss:14–15:

--aspid-fasttools-colors-gradient-button-bg:     ...
--aspid-fasttools-colors-gradient-button-accent: ...

Per the documented grammar, the role slot must use _ for compound words (gradient_button), not -. The dash separator is reserved for slot boundaries (prefix-group-role-state-tone). As written, the parser of the convention would read gradient as role, button as state, and bg/accent as tone — none of which is semantically accurate.

Correct forms: --aspid-fasttools-colors-gradient_button-bg and --aspid-fasttools-colors-gradient_button-accent. Fix this →

Informational — 6-slot status text/shade variables:

--aspid-colors-status-success-text-darkness has six dash-separated segments, exceeding the documented five-slot maximum (prefix-group-role-state-tone). The convention is applied consistently across all status text/shade variants so it reads clearly in practice, but it's an undocumented extension. Worth a note in CLAUDE.md or BRANDBOOK.html if you want future contributors to follow the same pattern without guessing.


Summary

Area Status
Generator correctness ✅ Solid — cache-safe, no symbol leakage, diagnostics clean
Assembly boundary ✅ Clean — no UnityEditor references in Runtime
ID System API ⚠️ One critical bug (IsCacheDirty / player builds) + minor discoverability notes
USS/BEM ⚠️ Two property name violations in gradient button USS

The critical IsCacheDirty bug should be fixed before release — any consumer trying to use IdRegistry in a built player will get silently empty results. Everything else is minor or informational.

Repository owner locked and limited conversation to collaborators May 10, 2026
Repository owner unlocked this conversation May 10, 2026
* Add Claude Code GitHub Actions workflows

* Add GitHub issue and pull request templates (#15)

* Mark IdRegistry cache dirty on OnEnable for player builds

---------

Co-authored-by: Claude <noreply@anthropic.com>
VPDPersonal and others added 10 commits May 10, 2026 23:24
* Fix IdStruct generator crash on unrecognised TypeKind values
* Redeploy Aspid.FastTools.Generators DLL
…tor (#18)

Rename --aspid-fasttools-colors-gradient-button-bg to
--aspid-fasttools-colors-gradient_button-bg and -accent likewise, in
both the USS file and the C# CustomStyleProperty registrations.

Co-authored-by: Claude <noreply@anthropic.com>
Rename URP Global Settings.asset to UniversalRenderPipelineGlobalSettings.asset
and refresh related ProjectSettings/GraphicsSettings for the current URP
version. Tighten the package.json description to highlight the actual
feature set.
Reorganises the package Documentation/ tree: README, SerializedPropertyExtensions
and VisualElementExtensions are split into Documentation/EN/ and Documentation/RU/
subfolders, dropping the _RU suffix (now expressed by the folder). Images/ stays
at the Documentation root; inner READMEs point at it via ../Images/. Root README.md
and README_RU.md links to per-feature references are retargeted to the new EN/RU
paths. CLAUDE.md README-sync note updated to describe the new layout.
Replace raw Profiler crop with an HTML-mocked card render and rename
the asset (plus its .meta) to aspid_fasttools_profiler_markers.png to
match the snake_case file-naming policy. Updates all four README copies
to point at the new path.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: editor Editor-only code area: generator Roslyn source generator area: runtime Runtime / player code area: samples Sample projects status: needs-review Ready for review type: feature New feature or capability

Projects

Status: In review

Development

Successfully merging this pull request may close these issues.

EnumValues IDRegistry

1 participant