Dev/skiafiddle ux improvements#3790
Merged
mattleibow merged 17 commits intoApr 28, 2026
Merged
Conversation
After bumping SkiaSharp to 4.147.0, the generated samples flow
(`dotnet cake --target=samples-generate`) fails restore on
`output/samples/Gallery/Uno/SkiaSharpSample.Uno.csproj` with:
Error NU1605: Detected package downgrade: SkiaSharp.Skottie from
4.147.0 to 3.119.2.
SkiaSharpSample.Uno -> SkiaSharpSample.Shared -> SkiaSharp.Skottie (>= 4.147.0)
SkiaSharpSample.Uno -> SkiaSharp.Skottie (>= 3.119.2)
`SkiaSharpSample.Shared` brings in Skottie 4.147.0 (its in-tree
ProjectReference is rewritten to a PackageReference 4.147.0 by
samples-generate). The Uno gallery csproj has no direct Skottie ref;
the only "near" Skottie in its closure comes from Uno.WinUI.Lottie at
3.119.2, which NuGet's resolver flags as a downgrade against the
deeper 4.147.0 ask from Shared.
The targets file is the right place to declare the Skottie
ProjectReference (matching the SkiaSharp / HarfBuzzSharp pattern), but
samples-generate strips imports of `samples/_UnoPlatformSamples.targets`
along with the file itself — so a PackageReference there alone would
not propagate to the generated csproj. The direct PackageReference in
the gallery csproj is what actually fixes the generated build:
samples-generate's version-substitution bumps it to whatever's in
VERSIONS.txt, putting the floor at 4.x and shadowing Lottie's
transitive 3.x.
Two changes:
- `samples/_UnoPlatformSamples.targets`: add the in-tree
`SkiaSharp.Skottie` ProjectReference next to the existing
SkiaSharp / HarfBuzzSharp ones.
- `samples/Gallery/Uno/SkiaSharpSample.Uno.csproj`: pin Skottie with
`ExcludeAssets="all"` so the package only constrains the version
floor; the actual Skottie code keeps coming from Shared (in the
generated build) or the in-tree project ref via the targets file
(in the local build). A duplicate PackageReference here AND in the
targets file trips NU1504 (and Uno.Implicit.Packages chokes on it),
so the targets file holds only the ProjectReference.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The shared gallery's FillPathSample built its shapes with mutating
SKPath methods (MoveTo/LineTo/AddRoundRect/AddCircle/Close), all of
which were marked [Obsolete("Use SKPathBuilder instead.")] when
SkiaSharp landed at 4.147.0. SkiaSharpSample.Shared has
TreatWarningsAsErrors=true, so the gallery and SkiaFiddle Uno builds
fail with CS0618 in CI:
error CS0618: 'SKPath.MoveTo(float, float)' is obsolete: 'Use
SKPathBuilder instead.'
error CS0618: 'SKPath.AddRoundRect(SKRoundRect, SKPathDirection)'
is obsolete: 'Use SKPathBuilder instead.'
...
Rewrite the five Create*Path helpers to construct an SKPathBuilder,
configure it via the same MoveTo/LineTo/AddCircle/AddRoundRect/Close
calls (those methods are still supported on the builder), and call
Detach() to materialize the SKPath returned to the caller. The
builder is `using`-disposed; the caller owns the detached path
exactly as before.
FillPathSample is the only sample that still uses the obsolete
mutating SKPath methods directly — every other sample in
samples/Gallery/Shared/Samples/ already uses SKPathBuilder.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The Uno wasm build was linking three copies of libSkiaSharp.a (in-tree st, NuGet st, NuGet st,simd), producing duplicate-symbol errors across libpng, libwebp and friends. SkiaSharp.NativeAssets.WebAssembly's targets file adds both st and st,simd variants to the wasm-ld command unconditionally. Listing the package as a direct PackageReference with ExcludeAssets="all" mutes both — the in-tree output/native/wasm/libSkiaSharp.a Content glob in IncludeNativeAssets.SkiaSharp.targets remains the sole source. Same treatment for HarfBuzzSharp.NativeAssets.WebAssembly. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The in-tree Uno glob was libSkiaSharp.a/**/*.a, which would link both st and st,simd if the native build ever produced both. Mirror the Blazor block's _SkiaSharpNativeBinaryType computation (driven by WasmEnableThreads and WasmEnableSIMD) and pin the variant directory in the Content glob so WasmEnableSIMD=false actually selects what the SkiaFiddle README claims it selects. Same change in IncludeNativeAssets.HarfBuzzSharp.targets. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace the stacked Setup/Draw editor split with a tabbed pane that defaults to the Draw tab, since the Draw block is what users edit most. Both editors stay mounted (visibility-toggled) so Monaco doesn't lose state when switching. Add Platforms/WebAssembly/WasmScripts/MonacoFocusGuard.js to stop Monaco's key/composition events from bubbling up to Uno's document-level listener — Monaco renders into a native HTML overlay on top of Uno's Skia canvas, and without the guard typed input could trigger Uno-side accelerators or focus changes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CodeEditor.Text doesn't reliably reflect what the user typed: Monaco's
onDidChangeContent → Accessor.setValue("Text", …) round-trip into managed
code lags behind keystrokes under Uno WASM, so RunAsync was reading the
seeded sample text and Roslyn was happily recompiling the original
snippet. Typing nonsense into the Draw box "compiled" successfully and
the animation never changed.
skiaFiddleGetMonacoValues (in MonacoFocusGuard.js) reads each editor's
live model value via EditorContext, and RunAsync pulls from there with a
.Text fallback for the early-load case. The order is DOM order, which
matches XAML declaration order (Draw first, Setup second).
While here, switch the inactive tab from Visibility.Collapsed to Opacity
+ IsHitTestVisible + IsTabStop. Collapsing skips Monaco's Loaded event,
so a never-shown editor would never initialize Monaco and our JS lookup
would return only one value.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The Orbits animation is a better landing showcase than the static gradient, so default the dropdown to it. Picker is selected by name so list reorders don't break the default. Drop the header's three-column grid in favor of a HorizontalAlignment overlay: brand on the left, sample picker centered, no right-column filler. The two children share the same row but lay out independently. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Contributor
📦 Try the packages from this PRWarning Do not run these scripts without first reviewing the code in this PR. Step 1 — Download the packages bash / macOS / Linux: curl -fsSL https://raw.githubusercontent.com/mono/SkiaSharp/main/scripts/get-skiasharp-pr.sh | bash -s -- 3790PowerShell / Windows: iex "& { $(irm https://raw.githubusercontent.com/mono/SkiaSharp/main/scripts/get-skiasharp-pr.ps1) } 3790"Step 2 — Add the local NuGet source dotnet nuget add source ~/.skiasharp/hives/pr-3790/packages --name skiasharp-pr-3790More options
Or download manually from Azure Pipelines — look for the Remove the source when you're done: dotnet nuget remove source skiasharp-pr-3790 |
The stopPropagation guard on .monaco-editor was an attempt to keep typing inside the editor from leaking into Uno's document-level event routing. In practice it broke typing entirely — Monaco stopped receiving keystrokes — so drop it and keep only the skiaFiddleGetMonacoValues JS helper, which is what actually fixes the recompile bug. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Re-add a keyboard guard but at the document level in capture phase, filtering for events whose target is inside .monaco-editor. This runs before Uno's own document listener, calls stopImmediatePropagation, and leaves Monaco's textarea-level listeners untouched (they fire later in target/bubble). The previous version listened on .monaco-editor itself, which sat in the wrong phase to interpose on Uno's routing. Tab strip is now drawn on top of the editor stack in the same Grid cell instead of sitting in a separate row above it. The editor reserves space with Margin-top, but its layout box still extends under the tab strip, so any sub-pixel clip overflow from Monaco's native HTML overlay lands behind the tab strip rather than next to it. Eliminates the white rectangle that was poking out where the tab buttons should be. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
collapse the inactive tab The previous guard called stopImmediatePropagation at document/capture, which also aborts the dispatch to the target — so Monaco's textarea-level listeners never fired and Ctrl+C/V, backspace at line boundaries, etc. silently broke. (Letters and space still made it through because Monaco relies on `input` events for those, which we weren't intercepting.) Replace it with a document.addEventListener monkey-patch that wraps any keyboard listener Uno installs at document scope and makes it skip events whose target is inside .monaco-editor. The wrapper only short- circuits Uno's own handler — Monaco's listeners are on the textarea inside .monaco-editor, not on document, so they're untouched and every keystroke reaches Monaco normally. For the white rectangle on the tab strip: switch the inactive editor's hide mechanism from Opacity=0 / IsHitTestVisible=False to Visibility=Collapsed. Opacity-based hiding kept the inactive Monaco's native HTML overlay in the rendering pipeline, where its sub-pixel clip overflow added on top of the active editor's. Visibility=Collapsed is display:none — the overlay is dropped from rendering, leaving only the active editor's clip to deal with (and that one sits behind the tab strip thanks to the z-stack from the previous commit). Pre-collapse, snapshot the live editor texts via GetEditorTexts and re-assign them to the managed Text properties so Monaco can rehydrate from the cache if it loses state when its host control is hidden. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Contributor
|
/azp run |
|
No pipelines are associated with this pull request. |
Increase the editor stack's top Margin from 34 to 48 so Monaco's native HTML overlay starts ~14px below the tab strip's bottom edge. The sub-pixel clip overflow that was bleeding into the tab area now lands inside the EditorBackground gap below the tab strip instead, leaving the buttons visible. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The clip-path Uno generated for Monaco's native overlay in the previous layout (single-cell Grid with the inner editor Grid using Margin=48 and the tab strip drawn on top via VerticalAlignment=Top) didn't honor the inner Grid's margin — it hugged the tab strip's bottom edge regardless, producing an overlay that punched a hole through Skia's render across the tab buttons. Switching to a 2-row Grid (tabs in row 0, editors in row 1) gives the editor a real layout cell with a clean boundary, so Uno emits a clip-path that actually starts at the row 1 top edge. This mirrors the structure we had before tabs were introduced (where Uno generated correct per-editor clip-paths for the two stacked editors). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Uno's WASM Skia compositor appears to punch a transparent hole for each native HTML overlay sized to the element's bounding box rather than its clip-path, so when Monaco's box is larger than the clip-path the unclipped portion of the hole exposes the body's default white background. Force body and .monaco-editor to the panel color so whatever leaks through blends in. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The tabbed UI exposed an Uno WASM Skia clip-path bug — Monaco's native HTML overlay would punch a hole through Skia across the tab buttons regardless of margin or row layout, leaving a white rectangle where the tabs should be. Reverting to the original stacked layout (Setup above Draw, each in its own row with a header) since that's the structure Uno generates a correct per-editor clip-path for. Preserved from the tab-era work: * Recompile fix — GetEditorTexts pulls live values from Monaco's models via skiaFiddleGetMonacoValues, so RunAsync no longer sends the seeded sample text to Roslyn even when the Monaco→managed Text round-trip lags. * Keyboard guard — document.addEventListener wrapper makes any keyboard listener Uno installs at document scope skip events whose target is inside .monaco-editor, stopping 's'/space/etc. from leaking into the ComboBox or focused button. * Default sample — picker defaults to Animated · Orbits. * Centered sample picker in the page header. Removed: tab strip XAML, SelectTab / OnDrawTabClicked / OnSetupTabClicked handlers, the body/Monaco background-paint CSS shim. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Orbits is now the default sample, so the "Default" prefix was misleading. The new name follows the same `<category> · <topic>` pattern as the other samples (Animated · Orbits, Shader · Plasma, …). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
GetEditorTexts mapped values[0] to Draw because the tabbed XAML put DrawEditor first in DOM order. Restoring the stacked layout flipped that — SetupEditor is now declared first (top), DrawEditor second (bottom) — so values[0] is the Setup block now. The previous mapping was sending the Draw block to Roslyn as Setup (and vice versa), which made every fresh run fail with class-scope-illegal expressions like `canvas.Clear(...)` at the start of Setup. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
No pipelines are associated with this pull request. |
|
No pipelines are associated with this pull request. |
This was referenced Apr 28, 2026
Closed
github-actions Bot
added a commit
that referenced
this pull request
Apr 29, 2026
Add docs landing page improvement (#3794), note CI and workflow automation improvements (#3755, #3787, #3802, #3805, #3808, #3811, #3812, #3813), and expand highlights paragraph. Community sample fixes by @ramezgerges (#3785, #3790) were already present. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This was referenced Apr 29, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description of Change
Bugs Fixed
API Changes
None.
Behavioral Changes
None.
Required skia PR
None.
PR Checklist