Skip to content

Upgrade to Avalonia 12 - #549

Merged
erikdarlingdata merged 12 commits into
devfrom
upgrade/avalonia-12-take2
Sep 22, 2026
Merged

erikdarlingdata merged 12 commits into
devfrom
upgrade/avalonia-12-take2

Conversation

@erikdarlingdata

Copy link
Copy Markdown
Owner

Summary

Avalonia 11.3.22 → 12.1.2, with the coupled third-party releases: ScottPlot.Avalonia 5.1.59, Avalonia.AvaloniaEdit + AvaloniaEdit.TextMate 12.0.0, Avalonia.Controls.DataGrid 12.1.2, Avalonia.Headless 12.1.2. Migration only — no improvements rode along; the scope test applied throughout was "does Avalonia 12 make this wrong?"

Do not merge until the manual pass below is done. The suite proves a lot here, but three things are checkable only by hand.

App changes (the whole compile-and-runtime surface)

  • Two one-line API migrations, both breaking item Add PR branch check and version bump check #13: DataGridBehaviors.cs moves off the removed TopLevel.PlatformSettings onto grid.GetPlatformSettings() (with a new in-suite Assert.NotNull guard, since Windows Ctrl+C cannot falsify it — the fallback is also Ctrl). And QuerySessionControl.GetParentWindow() moves off VisualRoot as Window — 12 hosts the Window under a new TopLevelHost, so the old form compiled, ran, and returned the wrong thing silently, which would have broken every session-owned dialog. That one no compile probe could catch; the headless suite did.
  • 13 Watermark → PlaceholderText renames (12 XAML + 1 C#), plus the same file's parameter and prose stragglers. Visual parity is settled by resource-set identity (Fluent ships the identical placeholder resources in 11 and 12; only unreferenced PART_ names changed).
  • A vestigial CS0618 pragma deleted; the ScottPlot version-gate comment deleted; the SkiaSharp pin comment rewritten to say what the pin actually does under 12 (nothing — it is kept because the [BUG] Application Does not open on Linux #139 NoDependencies collision is unresolved and removing it is out of scope); the DataGrid AllowAutoHide comment updated for 12's template-binding mechanism (upstream PR Await ShowDialog in Format_Click (fix CS4014) #241).
  • Zero code changes for ScottPlot, AvaloniaEdit, and DataGrid usage — every API the app touches survives, verified against the shipped assemblies.

Test suite

713 tests at baseline, 713 at tip — zero deleted, zero added, no new skips, no assertion softened (both exact-collection toolbar assertions stayed exact collections). The measurement churn comes from 12 replacing the headless stub text shaper (flat 10 DIP/char) with real HarfBuzz over an embedded font: widths now scale by FontSize/10, line heights by a flat ×1.36. That cause is written once, on HeadlessUi, and every re-measured threshold points at it. The toolbar's protected-command invariant (Connect/Execute/Est never collapse) is now stated explicitly in the assertions that guard it.

Harness: kept hand-rolled (12's xunit-v3 support removes the original reason but not the conclusion — the #474 drain and the survival canary don't exist in [AvaloniaFact]). The #474 note now says precisely what 12 fixed (the scope leak) and didn't (the throw — the drain remains the only prevention). A teardown-failure precedence guard shipped after an empirical probe confirmed 12 routes teardown throws into the dispatch task, where they were replacing assertion messages.

#544 — Avalonia 12's own headless setup has a thread-affinity race (~1 run in 4, one arbitrary victim, zero repo frames on the stack, not reproduced on 11, not caused by test concurrency — parallelization-off still hit it). Mitigated with a setup-scoped single re-dispatch that structurally cannot retry a test: it fires only when the dispatched delegate provably never started AND the stack matches Avalonia's setup frames, writes to stderr on every occurrence, and documents its removal condition. Proven engaging on the real race: the marker fired 3 times across 20 test-host runs, none failed. Gate: 10 consecutive complete green dotnet test runs, plus 2 more by the reviewing orchestrator.

Verification

  • Release build 0 errors / 0 warnings; 12 consecutive complete green suite runs across two operators.
  • linux-x64 self-contained publish succeeded; published libSkiaSharp.so SHA256-matches SkiaSharp.NativeAssets.Linux 3.119.4 and differs from ScottPlot's NoDependencies copy — issue [BUG] Application Does not open on Linux #139 does not reproduce on this graph.
  • Planned and reviewed by a multi-agent pipeline: 3 planners (one ran a full compile probe against real 12.1.2), 4 adversarial reviewers over 3 rounds (18 verified Major findings folded), 2 bucket-(b) escalations ruled mid-build. Plan and rulings are in the local crew doc.

Before merge (manual, ~20 minutes, installed or clean Release build only)

The twelve-point pass in the plan doc, headlined by the three things the suite structurally cannot see: loading-spinner animation (12 newly completes style animations on detach — undocumented), the #464 scrollbar rail geometry, and AvaloniaEdit highlighting across repeated tab switches (the 12.0.0-editor-on-12.1.2-core skew). Plus Alt mnemonics (12 matches layout symbols now) and the clipboard guards.

Follow-ups filed

#544 (the race, with removal condition), #545 (drop unused Grammars package), #546 (TextMate installation leak in schema viewers), #547 (pre-existing mnemonic eating the escape-brackets label's underscore), #548 (the columns-less DataGrid test). Before the first 12-based release: confirm SignPath's 'App' artifact configuration signs by glob — Avalonia.HarfBuzz.dll is new in the publish set. After merge: dispatch nightly.yml once and eyeball the linux artifact.

macOS double-click activation remains the standing open question; it rides with the next Mac session, now alongside the Option-mnemonic check.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Sr43FouJ6RizUyoWQeR5FX

erikdarlingdata and others added 11 commits September 21, 2026 18:29
Nine package lines move together, because none of them builds without the
others: the App's Avalonia core, Desktop, Themes.Fluent, Fonts.Inter and
Controls.DataGrid to 12.1.2, AvaloniaEdit and AvaloniaEdit.TextMate to 12.0.0
(no 12.1.x exists), ScottPlot.Avalonia to 5.1.59, and the test project's
Avalonia.Headless to 12.1.2. Nothing loud enforces that atomicity: every
nuspec floor is minimum-only, so a partial bump restores and compiles green
and then dies at the first HeadlessUnitTestSession.StartNew with an
Avalonia-internals TypeLoadException. If that signature ever appears, check
the two csprojs before suspecting anything else.

TopLevel.PlatformSettings is removed in 12, and it is the only hard compile
break in the whole project. The replacement is the VisualExtensions method,
whose using was already present; it returns null when detached exactly as the
old null-conditional chain did, so the Control fallback keeps its meaning.

Baseline before the bump, for the zero-deleted gate: 713 tests total, 712
passed, 1 skipped (the pre-existing non-Windows Entra contract), 0 failed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Sr43FouJ6RizUyoWQeR5FX
TextBox.Watermark is obsolete in 12 in favour of PlaceholderText, across 12
XAML sites and one code-behind constructor. The literal Watermark= rename is
only safe because the app has no UseFloatingWatermark site; that one renames
to UseFloatingPlaceholder, so nobody should generalize this recipe. The proxy
input's parameter and the two prose comments that talk about "the watermark"
move with it, because a control named one thing and described as another is
how the next reader loses an afternoon.

The CS0618 pragma in MainWindow wrapped nothing but a string array: the
drag/drop migration moved the deprecated calls out from under it some time
ago, and in 12 the APIs its comment names do not exist at all.

Three comments stated mechanisms that 12 changed:

- The ScottPlot line carried a do-not-bump-yet gate pointing at this very
  migration. It is executed, so the gate goes rather than being reworded.
- The SkiaSharp pin's comment claimed it prevents issue #139. It never could:
  the collision is ScottPlot's NoDependencies package shipping its own
  libSkiaSharp.so, a different package id that a PackageReference on this one
  cannot constrain. Under 12 the pin is inert on top of that, since
  Avalonia.Skia already floors the same id at the same version. The comment
  now says what is true and why the pin stays anyway.
- DataGrid 12.0.1 (PR #241) moved AllowAutoHide propagation out of
  OnApplyTemplate and into a template binding on each PART_ bar. The binding
  reads the same attached property off the same object, so App.axaml's rule
  still reaches the grids, but the reason it works is no longer the one the
  comment gave.

The one-line assertion added to the DataGrid scrollbar test is the only
falsifiable guard on the GetPlatformSettings migration: a manual Ctrl+C on
Windows cannot tell a dead lookup from a live one, because the fallback the
guard uses is Control and Control is what Windows reports.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Sr43FouJ6RizUyoWQeR5FX
Three things in HeadlessUi were written against 11 and are no longer true of
the package it wraps.

The reason for hand-rolling instead of using Avalonia.Headless.XUnit has
expired without the conclusion changing: the package asked for xunit v2 when
this was written and asks for xunit.v3.extensibility.core 3.2.2 now, which
unifies upward against this project's 4.0.1. Adopting it is possible and
still wrong, because [AvaloniaFact] offers neither the #474 drain nor the
canary. Leaving the dead objection in place would have let the next reader
check it, find it false, and switch on that basis.

The #474 note claimed a mechanism 12 half-fixed. The FontManager is still
disposed one line before ResetForUnitTests runs the queue, so the throw is
exactly as available as it was and the drain is still the only thing that
prevents it. What 12 changed is the aftermath: scope disposal moved into a
finally and the failure is routed to the dispatch's task, so one test dies
instead of every test after it. Stated that way round because the original
prescription, read quickly, invites deleting the drain.

Which leads to the guard on GetResult(). 12 reports a teardown failure
through the dispatch's own task, awaited outside both catches, so it arrived
as a throw from Dispatch rather than as a returned failure. Probed under
12.1.2 rather than reasoned about: a queued job that throws during teardown
comes out of GetResult(), and when the body had already failed its assertion,
the teardown exception is what the caller sees and the assertion message is
gone. That inverts the precedence Run documents, so the wrap keeps the body's
failure first.

The metrics paragraph is new and is the one place this branch explains why
layout numbers across six test files moved: 12 drops headless's stub shaper
for real HarfBuzz over its own embedded font, which makes a character one em
wide instead of a flat 10 DIP and a line 1.0898 em tall instead of 0.8. Every
re-measured threshold carries its number and points here for the reason.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Sr43FouJ6RizUyoWQeR5FX
Two assertions and four comments, all on the width axis, all re-measured
rather than relaxed.

At a 1520px viewport the session toolbar's overflow menu now holds four
entries instead of three. The fourth is QS Overview, which is the next name
in CollapseOrder, and no protected command moved: Connect, Execute and
Execute-with-estimate are all still on the row, and every button in front of
the tail is still at the same X. That is a wider row, not a different one.

The numbers behind it, measured at 12.1.2: the row's natural width is 2116px
where it was 1910, and the uncollapsible floor -- Connect, the server label,
the database picker and the two plan verbs -- is 746px where it was 708. Both
are about 1.1x, which is what FontSize / 10 comes to at the toolbar's 11px
text. The chevron's 28px height did not move, because nothing about it is
text.

The DataGrid scrollbar comment is corrected on both halves, since both were
wrong in different directions. The mechanism half described DataGrid
assigning AllowAutoHide in code, which PR #241 replaced with a template
binding in 12.0.1 -- the app's rule still reaches the bars, for a different
reason than the comment gave. The premise half said the rows measure
zero-high for want of a font. Under 12 there is a real font with real
metrics and the rows still do not realize, so the comment now says what
actually stops them, which is that the grid generates no columns at all.
That is pre-existing and left alone here; this commit corrects the
explanation, not the test.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Sr43FouJ6RizUyoWQeR5FX
The rewritten pin comment asserted that the NoDependencies package overwrites
the right .so at publish time. That is the shape of issue #139, but it is not
what this graph does, and a comment that states a failure mode as current
when the branch's own publish check disproves it is the same defect the rest
of this commit series is removing.

Measured instead of assumed: a linux-x64 self-contained publish puts a
libSkiaSharp.so that is SHA256-identical to SkiaSharp.NativeAssets.Linux
3.119.4's copy and different from the NoDependencies 3.119.0 one, alongside
managed SkiaSharp 3.119.4. The deps file names only the 3.119.4 package as
the native provider, though NoDependencies is still in the restore graph
underneath ScottPlot. The point the comment exists to make is unchanged: the
pin is not what decides that, because it cannot reach the other package id.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Sr43FouJ6RizUyoWQeR5FX
The rewritten header said the row is narrower than either harness figure on
real fonts. Nothing here measures that. The 1536-logical story is what the
file is about and stands on its own; the two numbers are harness thresholds,
so say that instead.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Sr43FouJ6RizUyoWQeR5FX
The metrics paragraph gave 1.0898 em and then four whole-DIP measurements,
which do not multiply out unless you know layout rounds the desired height
up. Say that, so the next reader checking 1.0898 x 12 against the 14 in a
test does not conclude one of them is wrong.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Sr43FouJ6RizUyoWQeR5FX
Avalonia 12 hosts a Window inside a TopLevelHost, so a Window is no longer
the root of its own visual tree. GetParentWindow read VisualRoot and cast it,
which now always misses, and it threw "No parent window" for every dialog a
query session owns: the connection dialog, the format dialog, the plan
dialogs and the open-in-editor overwrite prompt. Three tests caught it;
opening any of those four in the app would have.

This is the second half of the same breaking change the DataGridBehaviors fix
handled, and the more dangerous half. TopLevel.PlatformSettings was deleted,
so the compiler stopped the build and the migration probe saw it. VisualRoot
still exists, still compiles, and still returns an object -- just not the one
the cast wants -- so nothing reports it until something asks for a window at
runtime. Worth remembering the next time a green build is mistaken for a
migrated one.

TopLevel.GetTopLevel is what the rest of the app already uses for this, at
thirteen sites including QueryStore.cs and QueryStoreHistoryControl, and it
still resolves to the Window under the new hosting. Measured rather than
assumed on an attached session: the visual chain now ends MainWindow <
TopLevelHost, while GetTopLevel and FindAncestorOfType<Window> both return
MainWindow and the clipboard still resolves through it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Sr43FouJ6RizUyoWQeR5FX
…ctor

Two comment strengthenings the numbers alone do not carry.

The overflow assertion now records what is invariant versus what is merely
current. How many commands sit in the menu at a given width follows the
harness's text metrics and is expected to move with them. Which commands may
leave at all does not: Connect, Execute and Execute-with-estimate are absent
from CollapseOrder and must never appear in that menu at any width. The
exact-collection assertion is what enforces it, which is the reason it stays
an exact collection rather than a count.

The metrics paragraph now says why the height factor is 1.36x and not the
1.25x someone will reach for: 1.25 is the em box alone, and this face carries
a line gap on top of it. The font manager reports ascent 819, descent 205 and
gap 92 over an em of 1024, which are the OS/2 typographic values rather than
the hhea pair that would have meant 0.854 em. So 1.36x is the tell for a
height that moved, and a height that moved by anything else has a different
cause.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Sr43FouJ6RizUyoWQeR5FX
Avalonia 12's headless bootstrap loses a race often enough to fail one
arbitrary test in roughly a quarter of full-suite runs. It throws from
Dispatcher.VerifyAccess while EnsureIsolatedApplication builds the per-
dispatch Application: AvaloniaHeadlessPlatform.Initialize constructs a
Compositor, and DefaultRenderLoop.Add finds a different thread owning the
dispatcher. No code of ours appears above HeadlessUi.Dispatch in the stack.

Measured before writing anything: 2 of 6 full-suite runs on this bump, 1 of 6
more with the two classes that were red for an unrelated reason excluded, and
1 of 6 with xunit parallelization disabled outright, which is what rules out
test concurrency as the cause. The same measurement on 11.3.22 was 0 of 6. A
different test is the victim every time. There is no newer Avalonia to take.

So the dispatch is retried once, and only when the dispatched delegate was
never entered. That is the whole safety argument: this does not re-run a
test, it starts one that never ran. The started flag is the first statement
inside the delegate, so "the body never began" is a fact rather than an
inference, and the exception match is narrow on purpose -- the type AND one
of the two upstream frames -- so a thread-affinity bug in our own code names
our own frames and gets reported rather than papered over. One retry only;
twice in a row and the run goes red. Every occurrence writes a line to
stderr, because a retry nobody can see is how a 1-in-4 flake turns into a
1-in-400 mystery.

Verified against the real race rather than a simulation of it: over twenty
consecutive full runs the marker appeared three times and no run failed. Ten
consecutive runs through dotnet test, the way CI runs it, were green at 713
tests.

Remove it when an Avalonia release fixes the race. The removal condition and
the evidence rates are in the doc comment, along with the ten-run check that
makes deleting it falsifiable.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Sr43FouJ6RizUyoWQeR5FX
The retry's stderr line now points at the opened issue (#544) instead
of a placeholder, and the harness doc's three metrics paragraphs are
three sibling paragraphs rather than one nested inside another.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Sr43FouJ6RizUyoWQeR5FX
@claude

claude Bot commented Sep 22, 2026

Copy link
Copy Markdown

Reviewed. This is a clean, well-scoped Avalonia 11→12 migration — no changes touch plan-XML parsing, T-SQL generation, or any SQL-adjacent code, so the injection/untrusted-input concerns that usually matter most here don't apply.

Checked and found no issues:

  • Watermark → PlaceholderText: every site converted (grep confirms no leftovers), and no UseFloatingWatermark site exists to be missed, matching the commit's claim.
  • GetParentWindow() (QuerySessionControl.Execution.cs): VisualRoot → TopLevel.GetTopLevel(this) is correct for 12's TopLevelHost wrapping — no other .VisualRoot casts to Window remain in src/.
  • DataGridBehaviors.AttachCopyGuard: TopLevel.GetTopLevel(grid)?.PlatformSettings → grid.GetPlatformSettings() preserves the same null-safe fallback to KeyModifiers.Control.
  • Removed #pragma warning disable/restore CS0618 in MainWindow.axaml.cs: verified no CS0618/DataFormats.Files/IDataTransfer references remain in src/, so the pragma was dead.
  • HeadlessUi.cs retry logic for the headless setup race: the bodyStarted gate correctly distinguishes "delegate never entered" from "test failed after starting," so the retry can't mask a real assertion failure — it only re-runs dispatches that never executed the test body, and logs to stderr every time it fires.
  • PlanViewer.Ssms doesn't reference Avalonia at all, so it's correctly untouched (no version file to bump here).

No warnings-suppression additions, no version bump needed for this change, no PlanViewer.Web linked-include implications (no Core files added). Nothing to flag.

Avalonia 12 completes a style-applied animation when its visual
detaches - a handler its changelog does not document, outside the
PlaybackBehavior pause guard - and re-attaching re-applies the style
without resurrecting the finished animation. Switch tabs while a
fetch or capture is running and its indeterminate bar comes back
frozen mid-track, indistinguishable from a hang. Avalonia 11 had no
detach handling at all, so the bar just kept sweeping.

ProgressBarBehaviors.RestartOnReattach deactivates and reactivates
the :indeterminate style one dispatcher hop apart on reattach, which
spawns a fresh animation instance. Applied to all seven bars whose
hosts detach mid-run: the two execution overlays, the actual-plan
loading tab, and the QS grid, wait-stats, QS history and Overview
loaders.

Proven against the live symptom in a driven run, both directions:
five window captures spanning a full animation cycle after a tab
round-trip were pixel-identical without this, and sweep again with
it. The headless suite cannot see animations, so that driven pair is
the regression evidence.

This was the plan's pre-classified bucket (b) item for the manual
pass, found exactly where the review predicted.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Sr43FouJ6RizUyoWQeR5FX
@erikdarlingdata

Copy link
Copy Markdown
Owner Author

Post-review addition, pushed as 12f64dc: the driven GUI pass executed the plan's spinner check and hit the pre-classified bucket-(b) item exactly where the regression review predicted — Avalonia 12 completes style animations on detach (undocumented, outside the PlaybackBehavior guard), so a loading bar frozen mid-track after any tab switch during a capture. Fixed with ProgressBarBehaviors.RestartOnReattach applied to all seven at-risk bars; proven both directions in driven runs (five frames across a full animation cycle: pixel-identical before, sweeping after). Suite re-run green at 713. The drive also verified live on this branch: connection dialog through the fixed GetParentWindow path, the #542 database handover, placeholders, Alt mnemonics with underlines, editor typing/colorization/completion, clipboard copy via Get-Clipboard, scratch restore, and the error path. Items still needing human hands are unchanged: scrollbar rail geometry, chart box-select vs pan, middle-click pan, grid stripe under row recycling, sorts/resize. One pre-existing (11-reproduced) find filed separately: #550-adjacent focus-after-Ctrl+N, issue #551.

@claude

claude Bot commented Sep 22, 2026

Copy link
Copy Markdown

Reviewed the diff against 87d7382 (dev tip). This is a pure Avalonia 11→12 migration touching only PlanViewer.App and its test project — no PlanViewer.Core plan-parsing/T-SQL-generation code is in scope, so the injection/untrusted-XML concerns from the review brief don't apply here.

Checked and clean:

  • No new compiler warnings introduced; the one #pragma warning disable CS0618 removed was wrapping dead code (the deprecated APIs it referenced were already gone), not a suppression that needed replacing.
  • PlanViewer.Ssms has no Avalonia dependency, so it correctly needed no coordinated version bump (and none was made to its vsixmanifest/AssemblyInfo).
  • No PlanViewer.Core files changed, so PlanViewer.Web.csproj's linked <Compile Include> list correctly needed no updates.
  • GetParentWindow()'s move from VisualRoot as Window to TopLevel.GetTopLevel(this) is the right fix for Avalonia 12's TopLevelHost wrapping — this was a silent runtime break (compiles fine, returns null) that only the headless suite caught; good that a regression test (ScrollBarVisibilityTests Assert.NotNull(grid.GetPlatformSettings())) was added for the sibling DataGridBehaviors.cs change, since that one is otherwise unfalsifiable from manual testing (the fallback and the real value produce identical behavior under Ctrl+C).
  • ProgressBarBehaviors.RestartOnReattach: traced the off→on dispatcher-hop restart trick for the rare case of rapid detach/reattach faster than one Loaded-priority tick — a second attach could see IsIndeterminate still false from the first cycle's flip and skip its own restart, but the first cycle's pending Post still lands and re-triggers the style transition while attached, so it self-corrects rather than sticking frozen. Not flagging as a defect.
  • Test re-measurements (toolbar widths, line-height factors) are traced to the documented HarfBuzz metrics change in HeadlessUi.cs, and the exact-collection assertions guarding Connect/Execute/Execute-with-estimate from ever collapsing were kept exact rather than loosened.

No correctness, security, or convention issues found.

@erikdarlingdata

Copy link
Copy Markdown
Owner Author

SignPath check from the review (regression MIN1) is resolved — no action needed. The 'App' artifact configuration signs exactly one named PE, PlanViewer.App.exe, inside the zip; the second config signs the MSI container. Neither enumerates the payload, so the new Avalonia.HarfBuzz.dll passes through unsigned exactly like every Avalonia/SkiaSharp DLL already does in every shipped release — the strongest proof being history itself: releases have always contained dozens of unlisted DLLs, so unlisted files demonstrably do not fail the request. Avalonia 12 renames neither the exe nor the MSI. Nothing on the signing side gates this merge.

@erikdarlingdata
erikdarlingdata merged commit 9923d26 into dev Sep 22, 2026
3 checks passed
@erikdarlingdata
erikdarlingdata deleted the upgrade/avalonia-12-take2 branch September 22, 2026 01:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant