Skip to content

feat: Desktop Skia support, STJ migration, and CI modernization#38

Draft
clairernovotny wants to merge 323 commits intomainfrom
dev/cnov/desktop-head
Draft

feat: Desktop Skia support, STJ migration, and CI modernization#38
clairernovotny wants to merge 323 commits intomainfrom
dev/cnov/desktop-head

Conversation

@clairernovotny
Copy link
Copy Markdown

@clairernovotny clairernovotny commented Feb 11, 2026

Summary

This PR delivers three major initiatives for the Uno Monaco Editor:

Desktop Skia Support (fn-1)

  • Add net10.0-desktop target framework to MonacoEditorComponent and test app
  • Implement CDP-based (Chrome DevTools Protocol) bridge for desktop JS interop
  • Add desktop-specific content files (editor.html, helpers) alongside existing WASM embedded resources
  • Desktop CDP tests with Playwright automation

System.Text.Json Migration (fn-2)

  • Remove Newtonsoft.Json dependency entirely
  • Migrate all Monaco model types from [JsonProperty] to [JsonPropertyName]
  • Rewrite domain converters (enum converters, interface serializers) for STJ with AOT support
  • Add MonacoJsonContext source generator for trimming/AOT compatibility
  • Migrate all call-site serialization from JsonConvert to JsonSerializer
  • Modernize type generation pipeline to emit STJ attributes

CI Modernization (fn-3)

  • Add macOS ARM (macos-15) build job for Apple Silicon validation
  • Add npm install && npm run build step to generate Monaco bundles in CI
  • Add desktop test job (Windows) with CDP test category filtering
  • Structured artifact uploads and Playwright browser caching
  • Code coverage and test reporting infrastructure

Test plan

  • build job (ubuntu) passes
  • desktop-tests job (windows) passes
  • build-macos job (macos-15) passes
  • All three CI jobs green on PR

🤖 Generated with Claude Code

Copilot AI review requested due to automatic review settings February 11, 2026 09:22
@clairernovotny clairernovotny marked this pull request as draft February 11, 2026 09:22
@clairernovotny clairernovotny changed the title Remove unused editor helper classes and resources Add support for Skia Desktop Feb 11, 2026
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

clairernovotny and others added 25 commits February 11, 2026 11:22
Task: fn-1-implement-desktop-skia-target-for.7

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Update task JSON files to match their actual completion state.
Both tasks had stale "todo" status in their on-disk JSON while
the internal state tracking had them as completed.

Task: fn-1-implement-desktop-skia-target-for.7

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Sync task JSON metadata status to done for tasks 1-6
- Add Windows CI job for desktop CDP Playwright tests
- Add WASM lifecycle exactly-once integration test
- Add per-platform evidence matrix to task 7 spec

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…n tests

Add desktop CDP tests for markers, undo/redo keyboard operations, and
all four language service APIs (Completion, Hover, CodeLens, Color).
Update per-platform evidence matrix with detailed Windows test coverage.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…test

Replace API-existence checks with output-verifying tests for all four
language services (Completion returns "foreach", Hover returns "Hit"
content, CodeLens returns "Second Line Command", Color detects hex
values). Add multi-instance test creating a second editor with
independent state verification.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Linux validation is deferred due to no Linux runner/dev machine being
available during this epic. The architecture fully supports Linux
(single TFM, runtime detection, no preprocessor directives), but
manual validation requires a Linux environment. Updated acceptance
criteria and Known Gaps section accordingly.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Create MonacoJsonContext source-gen context with CamelCase naming,
  WhenWritingNull ignore, and UnsafeRelaxedJsonEscaping for Monaco interop
- Register all ~25 cross-boundary types via [JsonSerializable] including
  Position, Range, Selection, CompletionItem, CodeAction, Hover, etc.
- Add JsonSerializerIsReflectionEnabledByDefault=false to .csproj
- Suppress SYSLIB1031 for Monaco.Uri / System.Uri name collision
- Add 20 serialization contract tests: golden Newtonsoft baselines,
  STJ round-trip per type category, camelCase verification, null
  handling, and relaxed encoder verification
- Numeric enums (MarkerSeverity, CompletionItemKind) stay as integers

Task: fn-2-type-generation-pipeline-and.1
- Convert partial golden baseline assertions to full exact-match JSON
  comparisons (CompletionItem, CodeAction, Hover, MarkerData, ColorInformation)
- Add detailed SYSLIB1031 rationale comment in .csproj since SG
  diagnostics cannot be suppressed via pragma in user code
- Document SYSLIB1031 suppression in MonacoJsonContext remarks
- Add UriCollision_BothTypesSerializeCorrectly test proving Monaco.Uri
  and System.Uri both serialize correctly despite SG name collision

Task: fn-2-type-generation-pipeline-and.1
Replace all 30 custom Newtonsoft JsonConverter classes for string-backed
enums with [JsonStringEnumMemberName] attributes and per-enum
[JsonConverter(typeof(JsonStringEnumConverter<T>))]. Remove property-level
converter references from StandaloneEditorConstructionOptions. Numeric
enums remain untouched. Add 7 contract tests verifying round-trip for
representative enums including hyphenated values and camelCase wire format.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The active Newtonsoft serialization path (WebViewExtensions.cs) still
uses JsonConvert.SerializeObject for StandaloneEditorConstructionOptions.
Without [EnumMember] attributes and a Newtonsoft StringEnumConverter,
Newtonsoft would serialize string enums as integers after deleting the
custom converters.

Add [EnumMember(Value = "wireValue")] on each member and
[Newtonsoft.Json.JsonConverter(typeof(StringEnumConverter))] on each
enum type for all 30 string-backed enums. Add 7 Newtonsoft dual-stack
contract tests covering hyphenated, camelCase, and simple values.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Rewrite InterfaceToClassConverter, CssStyleConverter, and ColorConverter
as STJ JsonConverter<T> implementations while preserving Newtonsoft
dual-stack converters for compatibility until Newtonsoft removal.

- InterfaceToClassConverter<TInterface, TClass>: STJ JsonConverter<TInterface>
  that delegates Read to Deserialize<TClass> and Write to Serialize(typeof(TClass))
- CssStyleConverter: per-type STJ write-only converters (CssLineStyleConverter,
  CssGlyphStyleConverter, CssInlineStyleConverter) that serialize ICssStyle.Name
- ColorConverter: STJ JsonConverter<Color> using Utf8JsonReader/Utf8JsonWriter
  for Monaco 0-1 float RGBA format
- Migrate [JsonProperty] -> [JsonPropertyName] in ColorInformation.cs
- Add dual-stack [JsonConverter] attributes on all usage sites
- Add 10 contract tests covering round-trip, golden parity, and write-only behavior
- Update rd.xml for NewtonsoftInterfaceToClassConverter reflection directive

Task: fn-2-type-generation-pipeline-and.4

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- NewtonsoftCssStyleConverter.ReadJson now throws NotSupportedException
  instead of returning an exception object as data
- NewtonsoftInterfaceToClassConverter uses Deserialize<TClass> instead
  of Populate(), removing last serializer.Populate() call
- ColorConverter.Read clamps and rounds float values to prevent
  out-of-range payloads from silently corrupting color bytes

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace [JsonProperty] with CamelCase naming policy across ~64 model files
- Add [JsonPropertyName] only where wire name diverges from CamelCase convention
- Remove per-property NullValueHandling.Ignore (handled globally by WhenWritingNull)
- Replace using Newtonsoft.Json with using System.Text.Json.Serialization
- Add [JsonInclude] on properties with non-public setters for STJ source gen
- Change private set to internal set on Position, Range, Selection, WordAtPosition,
  ContextKey, IModelDeltaDecoration for STJ source generator visibility (SYSLIB1038)
- Add dual-stack [Newtonsoft.Json.JsonIgnore] on Selection.Direction for compat
- Update golden baseline test to use CamelCasePropertyNamesContractResolver
- Add round-trip tests for Range, Selection, and interface-typed properties (IRange)

Task: fn-2-type-generation-pipeline-and.3
- Add [JsonIgnore(Condition = JsonIgnoreCondition.Never)] on Model property
  to allow explicit null serialization for Monaco's "model: null" contract
- Complete IModelDeltaDecoration round-trip test with deserialization
  and value verification via InterfaceToClassConverter
- Add IPosition round-trip test verifying [JsonInclude] on internal setters

Task: fn-2-type-generation-pipeline-and.3
Task: fn-2-type-generation-pipeline-and.3
CamelCase naming policy already produces "uris" from "Uris".

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…essor for AOT

- Replace all JsonConvert.SerializeObject/DeserializeObject/ToString in
  WebViewExtensions, CodeEditor.Methods, LanguagesHelper, ModelHelper
  with JsonSerializer using MonacoJsonContext.Default (deserialization)
  and MonacoJsonContext.Relaxed (JS interop serialization)
- Delete _settings field from WebViewExtensions (CamelCase + NullIgnore
  now on MonacoJsonContext)
- Redesign ParentAccessor.SetValue to use FQN-keyed Dictionary<string,
  JsonTypeInfo> lookup instead of AOT-hostile Type.GetType() + assembly
  scanning
- Redesign ParentAccessor.GetJsonValue to use STJ with fail-fast
  InvalidOperationException for unregistered types
- Delete LookForTypeByName from both ParentAccessor and
  ParentAccessorDesktop
- Add RegisterTypeInfo extensibility API on IParentAccessor
- Mark AddAssemblyForTypeLookup as [Obsolete] (no-op, retained for compat)
- Add BuildTypeInfoMap() to MonacoJsonContext for AOT-safe type registry
- Register additional types in MonacoJsonContext: ContextKey, FindMatch,
  WordAtPosition, ILanguageExtensionPoint, object, string, bool, and
  collection variants
- Add callback round-trip contract tests for Completion, CodeAction,
  Hover, Color, Markers
- Add ParentAccessor type registry tests for known/unknown type handling
- Remove dead AddAssemblyForTypeLookup call from CodeEditor.Events

Task: fn-2-type-generation-pipeline-and.5
- Return "null" instead of "{}" from GetJsonValue when property is null
  to preserve JSON contract for JS callers expecting nullable values
- Harden unregistered-type exception handling in GetJsonValue to catch
  both InvalidOperationException and NotSupportedException (STJ may
  throw either depending on the code path)
- Add direct accessor behavior tests: RegisterTypeInfo extensibility,
  SetValue unknown-type fail-fast, GetJsonValue null contract,
  GetJsonValue unregistered-type exception wrapping, SetValue
  known-type deserialization via type info map

Task: fn-2-type-generation-pipeline-and.5
Task: fn-2-type-generation-pipeline-and.5
…ment bugs

- Use Position.Lift/Range.Lift for IPosition/IRange before STJ serialization
  to ensure concrete type metadata is available in AOT/source-gen mode
- Materialize IEnumerable<ColorInformation/ColorPresentation> to arrays
  with typed overloads (ColorInformationArray/ColorPresentationArray)
- Fix FindNextMatchAsync/FindPreviousMatchAsync passing searchString
  instead of searchStart as second argument
- Let STJ metadata exceptions (InvalidOperationException, NotSupportedException)
  propagate through WebViewExtensions catch-all instead of being silently hidden

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Use ConcurrentDictionary<string, JsonTypeInfo> for _typeInfoMap in
  ParentAccessor and ParentAccessorDesktop to prevent race conditions
  between RegisterTypeInfo writes and SetValue reads
- Update MonacoJsonContext.BuildTypeInfoMap() to return ConcurrentDictionary
- Stop swallowing STJ metadata/AOT failures in WebViewExtensions by
  excluding InvalidOperationException and NotSupportedException from the
  catch-all handler so unregistered types surface clearly
- Add List<T>-to-array materialization tests for ColorPresentation and
  ColorInformation matching the LanguagesHelper serialization pattern

Task: fn-2-type-generation-pipeline-and.5

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Task: fn-2-type-generation-pipeline-and.5

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
ParentAccessor.SetValue(string, object) was calling
BridgeEncoding.Desanitize on browser, but ManagedSetValue in
ParentAccessor.wasm.cs already desanitizes at the JSExport boundary
before calling SetValue. This double-decode could corrupt legitimate
%NN text patterns in editor content.

Remove the redundant desanitize from SetValue and document that
JSExport entrypoints are the canonical decode boundary.

Task: fn-2-type-generation-pipeline-and.5

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove Newtonsoft.Json PackageVersion from Directory.Packages.props
- Remove Newtonsoft.Json PackageReference from MonacoEditorComponent.csproj
- Remove Newtonsoft type directives from MonacoEditorComponent.rd.xml
- Remove dual-stack Newtonsoft converter classes (NewtonsoftInterfaceToClassConverter,
  NewtonsoftCssStyleConverter, NewtonsoftColorConverter)
- Remove all [Newtonsoft.Json.JsonConverter] attributes from ~30 string-backed enums
- Remove [Newtonsoft.Json.JsonConverter] attributes from CssGlyphStyle, CssLineStyle,
  CssInlineStyle, ColorInformation
- Remove [Newtonsoft.Json.JsonIgnore] from Selection, CssGlyphStyle, CssLineStyle
- Remove using Newtonsoft.Json from all affected .cs files
- Update serialization contract tests to use STJ golden baselines instead
  of Newtonsoft parity comparisons
- Add changelog.md entry documenting the breaking change with migration guidance

Task: fn-2-type-generation-pipeline-and.6
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 207 out of 646 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copilot AI review requested due to automatic review settings February 16, 2026 20:21
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 207 out of 646 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Decode JSON-RPC array string elements as strings in ParentAccessorDesktop so hover provider position payloads are deserialized as Monaco.Position instead of quoted JSON text.

Also harden hover callback deserialization to log JsonException without faulting the RPC request and keep the host visually hidden (opacity 0) during forced init visibility to reduce white-frame flicker.
- Implemented project scaffold and build infrastructure for HybridWebViewComponent.
- Defined core interfaces, control class, and XAML template for HybridWebView.
- Created JavaScript bridge script and async task manager for interop.
- Developed Desktop WebView2 presenter for C#↔JS communication.
- Implemented WASM iframe presenter for hosting local content.
- Added sample page and end-to-end validation for interop paths in MonacoEditorTestApp.
- Initiated PoC for composition-mode WebView2 with DirectComposition in Uno Win32.
- Validated flickering scenarios and airspace issues in both HWND and DComp modes.
Copilot AI review requested due to automatic review settings February 18, 2026 20:05
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 226 out of 666 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

The Microsoft.Playwright package is now referenced without the
ExcludeAssets="build;buildTransitive" attribute. This change allows
all Playwright build assets to be included, removing previous
suppression that avoided build target conflicts on some platforms.
Copilot AI review requested due to automatic review settings April 11, 2026 02:43
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 227 out of 667 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

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.

2 participants