Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .github/workflows/llm-lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,25 @@ jobs:
- name: Enforce assembly warning policy
run: pwsh -NoProfile -File scripts/lint-assembly-warnings.ps1

- name: Resolve the harness self-test scope
id: self-test-scope
shell: bash
env:
BASE_SHA: ${{ github.event.pull_request.base.sha || github.event.before }}
run: |
set -euo pipefail
if [ -n "${BASE_SHA}" ] && [ "${BASE_SHA}" != "0000000000000000000000000000000000000000" ] && ! git cat-file -e "${BASE_SHA}^{commit}" 2>/dev/null; then
git fetch --depth=1 origin "${BASE_SHA}" || true
fi
verdict="$(pwsh -NoProfile -File scripts/should-run-self-tests.ps1 -BaseSha "${BASE_SHA:-}")"
echo "self-tests=${verdict}" >> "${GITHUB_OUTPUT}"

# The self-tests only verify the harness surface (scripts, .llm, CI and
# devcontainer wiring, package manifests). Skip them when that surface
# is untouched; every surface change still runs the full suite on both
# operating systems.
- name: Run harness self-tests
if: steps.self-test-scope.outputs.self-tests != 'skip'
run: pwsh -NoProfile -File scripts/tests/run-all.ps1

- name: Enforce .llm file length limit
Expand Down
3 changes: 3 additions & 0 deletions .llm/context.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,9 @@ See the generated [Skills Index](./skills/index.md). Regenerate it after adding

### Validation Ladder (Run After Each Change)

Per-iteration speed: run `npm run check:fast` first (formats and lints only the files changed since `origin/main`, and skips harness self-tests when their surface is unchanged).
The full ladder below gates review.

```bash
dotnet tool restore
dotnet tool run csharpier -- format Editor Runtime Tests
Expand Down
1 change: 1 addition & 0 deletions .llm/references/forbidden-patterns.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,6 @@ Patterns that must not appear in this codebase, with the compliant alternative.
| Static mutable state with no documented reason or teardown reset | document why it is static, weak-key by lifecycle owner, unregister on every removal path, and reset it in the owning window's `Cleanup()` (`AssetGuidTypeIndex.Shared` suspension reset and the theme-selection ownership registry are the precedents) | static editor state outlives windows and strands cross-window state (PR #122 review finding) |
| `using` directives above the `namespace` declaration | place them inside the namespace block; file-level usings are sanctioned only for namespaceless assembly-attribute files and `[assembly: ...]` preambles like `InternalsVisibleTo` (`npm run lint:csharp-usings` enforces this, `:fix` moves the simple cases) | one namespace-scoped convention keeps the file header stable and the rule mechanically checkable (#124) |
| A guard that reads a machine-readable config resolves only part of a construct, such as the first `{a,b}` alternative, a nested group, or one level of a nested mapping | resolve every alternative and nesting level, or refuse the pattern with the file and line that uses it | a partially-read rule still matches its own representative path, so the guard passes while the real configuration disagrees; refusing beats guessing (PR #131 review finding in `scripts/lint-line-endings.js`) |
| Passing a changed-path list (diff, status, hook payload) straight into a file-argument tool — formatter, linter, packager | keep group triggers on the full changed list, but filter every file-argument invocation to paths that still exist on disk (`Test-Path` against the repo root) | deletions and rename old-paths appear in `git diff --name-only` but not on disk, so one ordinary delete or rename makes the tool fail and the whole check useless afterwards (PR #164 Bugbot finding in `scripts/fast-check.ps1`) |
| A guard that names the argument forms it rejects and passes everything it does not name | require the exact arguments the policy permits, and report the file with every argument found | a switch the reader does not recognize turns off the policy the guard claims to enforce, so `-nowarn:1701` or `-warnaserror-` in a `csc.rsp` passed the guard that owns warnings-as-errors (`scripts/lint-assembly-warnings.ps1`, #132) |
| Pixel or region validation that records UI Toolkit subtrees without checking whether they render | filter candidates through a rendered check before measuring — `display:none` ancestor chain, resolved per-element visibility, and positive area (`DocsImageCapture.IsRenderedForCapture` precedent) | a `display:none` subtree reports laid-out children but paints nothing, so its clamped one-pixel sample always fails and rejects a faithful capture; persisted namespace collapse makes hidden rows a normal host state (PR #140 review finding) |
1 change: 1 addition & 0 deletions .llm/skills/ship-changes/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ Run the narrowest sufficient layer; escalate on failure:

| Layer | Scope | Command |
| --- | --- | --- |
| Fast check | changed files | `npm run check:fast` |
| Pre-commit hook | staged files | automatic (`pre-commit` + `.pre-commit-config.yaml`) |
| C# member layout | all C# types | `npm run lint:csharp-member-order` |
| Markdown formatting | tracked Markdown | `npm run format:md:check` |
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),

### Fixed

- Fix the docs copy to match the window: a processor without an `Accepts` list is never offered, pane widths always save in editor preferences, and persistence docs use the
on-screen **Persist State in Settings Asset** label. The settings tooltip now names the correct save location. (#153, #149)
- `BaseDataObject` asset ids no longer change on validation: an asset keeps its authored id, and only an empty id fills from the asset GUID. Created and cloned assets still get a
fresh id (#137).

Expand Down
4 changes: 3 additions & 1 deletion Editor/DataVisualizer/Data/DataVisualizerSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ public sealed class DataVisualizerSettings : ScriptableObject

public string DataFolderPath => _dataFolderPath;

[Tooltip("If true, window state (selection, order, collapse) is saved in a special ScriptableObject. If false, state is saved within this settings asset file.")]
[Tooltip(
"If true, window state (selection, order, collapse) is saved within this settings asset file. If false, state is saved as JSON to a per-user file in Unity's persistent data path."
)]
public bool persistStateInSettingsAsset;

[Tooltip("If true, when selecting an Object, it will be selected in the Inspector.")]
Expand Down
40 changes: 20 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
# Data Visualizer
# DxVisualizer

> **🤖 AI Assistance Disclosure**
>
> The early versions of Data Visualizer were heavily human-authored. More recent development has been a mix of human effort and AI assistance: human authors lead design,
> architecture, and review, while AI tools assist with feature development, bug detection, performance optimization, and documentation.

Data Visualizer streamlines working with ScriptableObject-heavy systems by centralizing asset management, inspection, and batch operations in a single window. Instead of hunting
DxVisualizer streamlines working with ScriptableObject-heavy systems by centralizing asset management, inspection, and batch operations in a single window. Instead of hunting
through the Project panel and repeatedly switching contexts, you get a namespace-organized view of all your data types with inline editing, batch operations, and workflow
automation.

Data Visualizer is free forever: no subscriptions, no paid upgrades, no feature-gated tiers. The full source is MIT-licensed, and every capability documented here ships in the free
DxVisualizer is free forever: no subscriptions, no paid upgrades, no feature-gated tiers. The full source is MIT-licensed, and every capability documented here ships in the free
package.

This guide captures the key points from the companion [video walkthrough](https://youtu.be/3oUxUSKNyhw) while keeping the instructions project-agnostic. A browsable version of this
documentation, with per-topic pages, is published at <https://wallstop.github.io/DataVisualizer/>.

## Getting Started

Open **Tools → Wallstop Studios → Data Visualizer** and dock it alongside the Inspector. The tool persists your layout, selection, and tracked types between sessions, so you can
jump back into your workflow immediately.
Open **Tools → Wallstop Studios → DxVisualizer** and dock it alongside the Inspector. The tool persists your layout, selection, and tracked types between sessions, so you can jump
back into your workflow immediately.

## Window Layout

Expand Down Expand Up @@ -72,12 +72,12 @@ Three controls above the Namespace panel populate your catalog:

**Search Types** queries Unity's known ScriptableObject types. Add individual types or entire namespaces in one operation.

**Scan Asset Folder** crawls a folder recursively, discovering all ScriptableObject types and wiring up existing instances. Ideal for bootstrapping Data Visualizer on established
**Scan Asset Folder** crawls a folder recursively, discovering all ScriptableObject types and wiring up existing instances. Ideal for bootstrapping DxVisualizer on established
projects.

**Scan Scripts Folder** targets source folders containing ScriptableObject classes. Use this when you've written new types but haven't created any assets yet.

Removing types or namespaces is non-destructive—it only stops Data Visualizer from tracking them. Your assets remain untouched on disk.
Removing types or namespaces is non-destructive—it only stops DxVisualizer from tracking them. Your assets remain untouched on disk.

Organize the catalog to match your team's mental model. The structure persists across sessions, so everyone can navigate consistently.

Expand All @@ -102,14 +102,14 @@ removes its labels, and the filter re-applies immediately.
![Settings popover with persistence toggles and data folder field](https://raw.githubusercontent.com/wallstop/DataVisualizer/main/docs/images/data-visualizer-settings.png)
_Settings popover. Regenerated from the live window by the docs capture pipeline._

**Persist State in UserState** (the default) stores the selected namespace and type, the selected object per type, namespace, type, and object ordering, collapse state, tracked
types, per-type label filters, and the per-type processor scope in a per-user JSON file instead of a shared project asset. Each developer keeps a private arrangement and version
control stays free of layout churn.
With **Persist State in Settings Asset** off (the default), the window stores the selected namespace and type, the selected object per type, namespace, type, and object ordering,
collapse state, tracked types, per-type label filters, and the per-type processor scope in a per-user JSON file instead of a shared project asset. Each developer keeps a private
arrangement and version control stays free of layout churn.

Turning the setting off stores the same state inside a `DataVisualizerSettings` asset in the project, which suits a team that wants one shared arrangement. Data Visualizer creates
one at `Assets/Editor/DataVisualizerSettings.asset` on first use if no such asset exists. Switching between the two copies the current state across.
Turning **Persist State in Settings Asset** on stores the same state inside a `DataVisualizerSettings` asset in the project, which suits a team that wants one shared arrangement.
DxVisualizer creates one at `Assets/Editor/DataVisualizerSettings.asset` on first use if no such asset exists. Switching between the two copies the current state across.

**Select Active Object** syncs selection between Data Visualizer and Unity's Inspector window. Useful for cross-referencing assets in other editor windows.
**Select Active Object** syncs selection between DxVisualizer and Unity's Inspector window. Useful for cross-referencing assets in other editor windows.

**Data Folder** defines where new assets land, each in a per-type folder named after its full namespace. Click the path to ping the current folder, or browse to set a new default.

Expand All @@ -126,8 +126,8 @@ toggles, scrollers, and inspector surfaces. Action colors remain distinct: dange
for cancel/move, and emphasis for alternate toggle modes. **Reset Theme** keeps the compact Data Folder button sizing, clears the saved selection, and restores Classic. Selecting
the Classic asset gives the same appearance but keeps an explicit selection.

Create a theme with **Assets → Create → Wallstop Studios → DataVisualizer → Data Visualizer Theme**. Assign a `.uss` asset to its **Style Sheet** field, then choose the theme in
the window's **Settings → Theme** field. Keep your theme and stylesheet under an `Editor` folder; they are editor-only assets.
Create a theme with **Assets → Create → Wallstop Studios → DxVisualizer → DxVisualizer Theme**. Assign a `.uss` asset to its **Style Sheet** field, then choose the theme in the
window's **Settings → Theme** field. Keep your theme and stylesheet under an `Editor` folder; they are editor-only assets.

The selected theme follows the existing project/user persistence setting. Switching that setting copies the current selection. Choose **Classic (Default / Reset)** or use **Reset
Theme** to restore the package style. A missing theme falls back to the package style without discarding its saved GUID; reset clears that reference too.
Expand All @@ -145,17 +145,17 @@ For example, this stylesheet changes the accent, standard button states, window
}
```

The override stylesheet is applied after the package stylesheet. Standard control rules are scoped to `.dataviz-root` inside the Data Visualizer window. Normal USS selector
precedence still applies, and explicit inline styles take priority. Data color swatches and label colors remain data-driven; IMGUI and custom third-party inspector styling are not
replaced. Use `Nord.uss` or `Dracula.uss` as palette references; copy them under your project's `Editor` folder before customizing rather than editing installed package files. The
The override stylesheet is applied after the package stylesheet. Standard control rules are scoped to `.dataviz-root` inside the DxVisualizer window. Normal USS selector precedence
still applies, and explicit inline styles take priority. Data color swatches and label colors remain data-driven; IMGUI and custom third-party inspector styling are not replaced.
Use `Nord.uss` or `Dracula.uss` as palette references; copy them under your project's `Editor` folder before customizing rather than editing installed package files. The
`--dataviz-background` and `--dataviz-input` tokens control the window and input surfaces. Semantic `--dataviz-danger`, `--dataviz-positive`, `--dataviz-secondary`,
`--dataviz-warning`, and `--dataviz-emphasis` tokens each have a matching `--dataviz-on-*` foreground token for filled states. Theme changes apply to the open window without
reselecting or reopening: editing the applied theme's stylesheet, changing its Style Sheet reference, or renaming/moving the assets updates immediately, and deleting the applied
theme falls back to the package style while keeping the saved GUID semantics.

## Extensibility

Data Visualizer exposes several extension points for custom workflows:
DxVisualizer exposes several extension points for custom workflows:

**Attributes** let you override display namespace or friendly names on ScriptableObject classes. `[CustomDataVisualization(Namespace = "...", TypeName = "...")]` replaces the
namespace group and the display name the window shows. Without it the window files a type under the last segment of its C# namespace. Useful when code organization doesn't match
Expand All @@ -173,7 +173,7 @@ Derived classes override only what they need—GUID generation, cache resets, co
without writing per-asset editor scripts.

**UI Toolkit Extensions** render custom UI alongside the default inspector. Return a `VisualElement` tree—graphs, thumbnails, validation badges, or any UI Toolkit component—from
`IGUIProvider.BuildGUI` (or `BaseDataObject.BuildGUI`) and Data Visualizer slots it in below the inspector. The `DataVisualizerGUIContext` argument carries the selected asset's
`IGUIProvider.BuildGUI` (or `BaseDataObject.BuildGUI`) and DxVisualizer slots it in below the inspector. The `DataVisualizerGUIContext` argument carries the selected asset's
`SerializedObject` for writing changes back. Because the entire window runs on UI Toolkit, this approach scales to complex dashboards without leaving the unified workflow.

**Processors** are plain `IDataProcessor` classes that the window discovers with no registration. `Name` labels the button, `Description` is its tooltip, `Accepts` lists the types
Expand Down
4 changes: 2 additions & 2 deletions docs/extending.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Extending the window

The runtime assembly is part of the package and carries the extension surface, so your types compile against it in both the editor and a player build. Everything on this page is
public API.
public API. Code identifiers keep the `DataVisualizer` name — namespaces, types, and assemblies are unchanged by the rebrand, so your code needs no edits.

## Display attributes

Expand Down Expand Up @@ -169,7 +169,7 @@ Odin integration is optional. The package has no dependency on Odin and compiles
## Removing a type from the catalog

Types deriving from `BaseDataObject` and types carrying `[CustomDataVisualization]` are managed for you and have no remove button. Other tracked types can be removed with the **X**
on their row or their namespace header. Removing a type stops Data Visualizer from tracking it; the assets stay on disk.
on their row or their namespace header. Removing a type stops DxVisualizer from tracking it; the assets stay on disk.

## Next steps

Expand Down
Loading
Loading