Skip to content

Add schema.org JSON-LD and per-sample descriptions#17

Merged
TimPurdum merged 7 commits into
mainfrom
feature/schema-org-jsonld
May 19, 2026
Merged

Add schema.org JSON-LD and per-sample descriptions#17
TimPurdum merged 7 commits into
mainfrom
feature/schema-org-jsonld

Conversation

@magmoe
Copy link
Copy Markdown
Contributor

@magmoe magmoe commented May 1, 2026

Make sample pages legible to AI agents and screen readers without executing JavaScript.

Infrastructure:

  • MainLayout emits a schema.org JSON-LD @graph on every page with WebSite, Organization, SoftwareApplication, WebPage, and (on sample pages) SoftwareSourceCode entities. Site-level entities mirror the GeoBlazor.com marketing site so crawlers can resolve cross-site identity.
  • HeadContent emits per-page meta description plus Open Graph and Twitter Card tags so sample URLs render rich previews when shared.
  • SamplePage gains a virtual Description property. Each sample overrides it with a detailed description: what the sample shows, what's on the screen, what the user can do, and which GeoBlazor Razor component it demonstrates.
  • Description is rendered visibly as a collapsible "About this sample" details element below the article and feeds the JSON-LD description on WebPage and SoftwareSourceCode.

Content:

  • 84 Description overrides — every Core sample (54), every Pro sample (27), plus three pages migrated to SamplePage in this change (GeometryMethods, LocationMethods, GraphicsLegends).

Layout:

  • Article padding-bottom scaled by viewport so the description clears the RenderModeSelector bar at all widths (4rem ≥1400px, 14rem 1075-1399px, 16rem <1075px).
  • Sidebar adds 3.5rem bottom padding so its scroll content stays above the RenderModeSelector bar.
  • RenderModeSelector spans full viewport width at all sizes (was offset by sidebar at wide).

Make sample pages legible to AI agents and screen readers without
executing JavaScript.

Infrastructure:
- MainLayout emits a schema.org JSON-LD @graph on every page with
  WebSite, Organization, SoftwareApplication, WebPage, and (on
  sample pages) SoftwareSourceCode entities. Site-level entities
  mirror the GeoBlazor.com marketing site so crawlers can resolve
  cross-site identity.
- HeadContent emits per-page meta description plus Open Graph and
  Twitter Card tags so sample URLs render rich previews when shared.
- SamplePage gains a virtual Description property. Each sample
  overrides it with a detailed description: what the sample shows,
  what's on the screen, what the user can do, and which GeoBlazor
  Razor component it demonstrates.
- Description is rendered visibly as a collapsible "About this
  sample" details element below the article and feeds the JSON-LD
  description on WebPage and SoftwareSourceCode.

Content:
- 84 Description overrides — every Core sample (54), every Pro
  sample (27), plus three pages migrated to SamplePage in this
  change (GeometryMethods, LocationMethods, GraphicsLegends).

Layout:
- Article padding-bottom scaled by viewport so the description
  clears the RenderModeSelector bar at all widths
  (4rem ≥1400px, 14rem 1075-1399px, 16rem <1075px).
- Sidebar adds 3.5rem bottom padding so its scroll content stays
  above the RenderModeSelector bar.
- RenderModeSelector spans full viewport width at all sizes
  (was offset by sidebar at wide).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@magmoe magmoe requested review from TimPurdum and Copilot May 1, 2026 15:36
@magmoe magmoe self-assigned this May 1, 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.

Pull request overview

Adds structured, per-page metadata and human-readable descriptions to GeoBlazor sample pages so crawlers/screen readers/AI agents can understand each sample without relying on JavaScript execution, along with a few layout tweaks to accommodate the new “About this sample” section and the render-mode selector bar.

Changes:

  • Add per-page <HeadContent> meta description + Open Graph/Twitter Card tags and emit a schema.org JSON-LD @graph (including SoftwareSourceCode on sample pages).
  • Introduce SamplePage.Description (virtual) and override it across Core/Pro samples; render it visibly in MainLayout under a collapsible <details> block.
  • Adjust layout CSS (article padding/scrolling, sidebar padding, render-mode selector positioning).

Reviewed changes

Copilot reviewed 89 out of 89 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
samples/core/dymaptic.GeoBlazor.Core.Sample.Shared/Shared/MainLayout.razor Adds Head meta tags, JSON-LD emission, and renders “About this sample” from CurrentPage.Description.
samples/core/dymaptic.GeoBlazor.Core.Sample.Shared/Shared/MainLayout.razor.css Updates responsive padding/scroll behavior and styles the <details> description block.
samples/core/dymaptic.GeoBlazor.Core.Sample.Shared/Shared/NavMenu.razor.css Adds bottom padding so sidebar content clears the render-mode selector bar.
samples/core/dymaptic.GeoBlazor.Core.Sample.Shared/Shared/RenderModeSelector.razor.css Removes wide-layout left offset so selector spans full viewport width.
samples/core/dymaptic.GeoBlazor.Core.Sample.Shared/Shared/SamplePage.cs Introduces Description virtual property (default empty) for per-sample text/metadata.
samples/pro/dymaptic.GeoBlazor.Pro.Sample.Shared/Pages/WebStyleSymbols.razor Adds Description override for the sample.
samples/pro/dymaptic.GeoBlazor.Pro.Sample.Shared/Pages/WFSUtils.razor Adds Description override for the sample.
samples/pro/dymaptic.GeoBlazor.Pro.Sample.Shared/Pages/UpdateFeatureAttributes.razor Adds Description override for the sample.
samples/pro/dymaptic.GeoBlazor.Pro.Sample.Shared/Pages/TimeSlider.razor Adds Description override for the sample.
samples/pro/dymaptic.GeoBlazor.Pro.Sample.Shared/Pages/Swipe.razor Adds Description override for the sample.
samples/pro/dymaptic.GeoBlazor.Pro.Sample.Shared/Pages/StyledGeoJSONLayers.razor.cs Adds Description override for the sample.
samples/pro/dymaptic.GeoBlazor.Pro.Sample.Shared/Pages/SpatialRelationships.razor Adds Description override for the sample.
samples/pro/dymaptic.GeoBlazor.Pro.Sample.Shared/Pages/SketchQuery.razor Adds Description override for the sample.
samples/pro/dymaptic.GeoBlazor.Pro.Sample.Shared/Pages/SearchCustomSource.razor Adds Description override for the sample.
samples/pro/dymaptic.GeoBlazor.Pro.Sample.Shared/Pages/RoutesAndDirections.razor Adds Description override for the sample.
samples/pro/dymaptic.GeoBlazor.Pro.Sample.Shared/Pages/ProWidgets.razor Adds Description override for the sample.
samples/pro/dymaptic.GeoBlazor.Pro.Sample.Shared/Pages/ProBookmarks.razor Adds Description override for the sample.
samples/pro/dymaptic.GeoBlazor.Pro.Sample.Shared/Pages/PrintWidgets.razor Adds Description override for the sample.
samples/pro/dymaptic.GeoBlazor.Pro.Sample.Shared/Pages/PopupEdit.razor Adds Description override for the sample.
samples/pro/dymaptic.GeoBlazor.Pro.Sample.Shared/Pages/OGCFeatureLayers.razor Adds Description override for the sample.
samples/pro/dymaptic.GeoBlazor.Pro.Sample.Shared/Pages/LengthAndArea.razor Adds Description override for the sample.
samples/pro/dymaptic.GeoBlazor.Pro.Sample.Shared/Pages/ImageryGroupBlend.razor Adds Description override for the sample.
samples/pro/dymaptic.GeoBlazor.Pro.Sample.Shared/Pages/HighlightFeaturesByGeometry.razor Adds Description override for the sample.
samples/pro/dymaptic.GeoBlazor.Pro.Sample.Shared/Pages/GroupLayers.razor Adds Description override for the sample.
samples/pro/dymaptic.GeoBlazor.Pro.Sample.Shared/Pages/GraphicsLegends.razor Migrates to SamplePage, adds PageLinks + Description.
samples/pro/dymaptic.GeoBlazor.Pro.Sample.Shared/Pages/EditFeatureData.razor Adds Description override for the sample.
samples/pro/dymaptic.GeoBlazor.Pro.Sample.Shared/Pages/DemographicData.razor Adds Description override for the sample.
samples/pro/dymaptic.GeoBlazor.Pro.Sample.Shared/Pages/CustomPopupContents.razor.cs Adds Description override for the sample.
samples/pro/dymaptic.GeoBlazor.Pro.Sample.Shared/Pages/ClusteringPopups.razor Adds Description override for the sample.
samples/pro/dymaptic.GeoBlazor.Pro.Sample.Shared/Pages/Clustering.razor Adds Description override for the sample.
samples/pro/dymaptic.GeoBlazor.Pro.Sample.Shared/Pages/ClusterPieCharts.razor Adds Description override for the sample.
samples/pro/dymaptic.GeoBlazor.Pro.Sample.Shared/Pages/Binning.razor Adds Description override for the sample.
samples/pro/dymaptic.GeoBlazor.Pro.Sample.Shared/Pages/ApplyEdits.razor Adds Description override for the sample.
samples/core/dymaptic.GeoBlazor.Core.Sample.Shared/Pages/Widgets.razor Adds Description override for the sample.
samples/core/dymaptic.GeoBlazor.Core.Sample.Shared/Pages/WebScenes.razor Adds Description override for the sample.
samples/core/dymaptic.GeoBlazor.Core.Sample.Shared/Pages/WebMaps.razor Adds Description override for the sample.
samples/core/dymaptic.GeoBlazor.Core.Sample.Shared/Pages/WMTSLayers.razor Adds Description override for the sample.
samples/core/dymaptic.GeoBlazor.Core.Sample.Shared/Pages/WMSLayers.razor Adds Description override for the sample.
samples/core/dymaptic.GeoBlazor.Core.Sample.Shared/Pages/WFSLayers.razor Adds Description override for the sample.
samples/core/dymaptic.GeoBlazor.Core.Sample.Shared/Pages/WCSLayers.razor Adds Description override for the sample.
samples/core/dymaptic.GeoBlazor.Core.Sample.Shared/Pages/VectorLayer.razor Adds Description override for the sample.
samples/core/dymaptic.GeoBlazor.Core.Sample.Shared/Pages/UniqueValueRenderers.razor.cs Adds Description override for the sample.
samples/core/dymaptic.GeoBlazor.Core.Sample.Shared/Pages/SqlQuery.razor Adds Description override for the sample.
samples/core/dymaptic.GeoBlazor.Core.Sample.Shared/Pages/SqlFilterQuery.razor Adds Description override for the sample.
samples/core/dymaptic.GeoBlazor.Core.Sample.Shared/Pages/ServiceAreas.razor Adds Description override for the sample.
samples/core/dymaptic.GeoBlazor.Core.Sample.Shared/Pages/ServerSideQueries.razor Adds Description override for the sample.
samples/core/dymaptic.GeoBlazor.Core.Sample.Shared/Pages/SearchMultipleSources.razor Adds Description override for the sample.
samples/core/dymaptic.GeoBlazor.Core.Sample.Shared/Pages/Scene.razor Adds Description override for the sample.
samples/core/dymaptic.GeoBlazor.Core.Sample.Shared/Pages/ScaleBarWidgetPage.razor Adds Description override for the sample.
samples/core/dymaptic.GeoBlazor.Core.Sample.Shared/Pages/ReverseGeolocator.razor Adds Description override for the sample.
samples/core/dymaptic.GeoBlazor.Core.Sample.Shared/Pages/ReactiveUtils.razor Adds Description override for the sample.
samples/core/dymaptic.GeoBlazor.Core.Sample.Shared/Pages/QueryTopFeatures.razor Adds Description override for the sample.
samples/core/dymaptic.GeoBlazor.Core.Sample.Shared/Pages/QueryRelatedFeatures.razor Adds Description override for the sample.
samples/core/dymaptic.GeoBlazor.Core.Sample.Shared/Pages/ProjectionTool.razor Adds Description override for the sample.
samples/core/dymaptic.GeoBlazor.Core.Sample.Shared/Pages/Popups.razor Adds Description override for the sample.
samples/core/dymaptic.GeoBlazor.Core.Sample.Shared/Pages/PopupActions.razor Adds Description override for the sample.
samples/core/dymaptic.GeoBlazor.Core.Sample.Shared/Pages/PlaceSelector.razor Adds Description override for the sample.
samples/core/dymaptic.GeoBlazor.Core.Sample.Shared/Pages/OpenStreetMapsLayers.razor Adds Description override for the sample.
samples/core/dymaptic.GeoBlazor.Core.Sample.Shared/Pages/Navigation.razor Adds Description override for the sample.
samples/core/dymaptic.GeoBlazor.Core.Sample.Shared/Pages/MeasurementWidgets.razor Adds Description override for the sample.
samples/core/dymaptic.GeoBlazor.Core.Sample.Shared/Pages/MarkerRotation.razor Adds Description override for the sample.
samples/core/dymaptic.GeoBlazor.Core.Sample.Shared/Pages/MapImageLayers.razor Adds Description override for the sample.
samples/core/dymaptic.GeoBlazor.Core.Sample.Shared/Pages/ManyGraphics.razor Adds Description override for the sample.
samples/core/dymaptic.GeoBlazor.Core.Sample.Shared/Pages/LocationMethods.razor Migrates to SamplePage, adds PageLinks + Description.
samples/core/dymaptic.GeoBlazor.Core.Sample.Shared/Pages/LocateWidgetPage.razor Adds Description override for the sample.
samples/core/dymaptic.GeoBlazor.Core.Sample.Shared/Pages/Legends.razor Adds Description override for the sample.
samples/core/dymaptic.GeoBlazor.Core.Sample.Shared/Pages/LayerLists.razor Adds Description override for the sample.
samples/core/dymaptic.GeoBlazor.Core.Sample.Shared/Pages/Labels.razor Adds Description override for the sample.
samples/core/dymaptic.GeoBlazor.Core.Sample.Shared/Pages/KMLLayers.razor Adds Description override for the sample.
samples/core/dymaptic.GeoBlazor.Core.Sample.Shared/Pages/ImageryTileLayers.razor Adds Description override for the sample.
samples/core/dymaptic.GeoBlazor.Core.Sample.Shared/Pages/ImageryLayers.razor Adds Description override for the sample.
samples/core/dymaptic.GeoBlazor.Core.Sample.Shared/Pages/HitTests.razor Adds Description override for the sample.
samples/core/dymaptic.GeoBlazor.Core.Sample.Shared/Pages/GraphicTracking.razor Adds Description override for the sample.
samples/core/dymaptic.GeoBlazor.Core.Sample.Shared/Pages/GeometryMethods.razor Migrates to SamplePage, adds PageLinks + Description.
samples/core/dymaptic.GeoBlazor.Core.Sample.Shared/Pages/GeoRSSLayers.razor Adds Description override for the sample.
samples/core/dymaptic.GeoBlazor.Core.Sample.Shared/Pages/GeoJSONLayers.razor Adds Description override for the sample.
samples/core/dymaptic.GeoBlazor.Core.Sample.Shared/Pages/FeatureLayers.razor Adds Description override for the sample.
samples/core/dymaptic.GeoBlazor.Core.Sample.Shared/Pages/Events.razor Adds Description override for the sample.
samples/core/dymaptic.GeoBlazor.Core.Sample.Shared/Pages/Drawing.razor Adds Description override for the sample.
samples/core/dymaptic.GeoBlazor.Core.Sample.Shared/Pages/DisplayProjection.razor Adds Description override for the sample.
samples/core/dymaptic.GeoBlazor.Core.Sample.Shared/Pages/CompassWidgetPage.razor Adds Description override for the sample.
samples/core/dymaptic.GeoBlazor.Core.Sample.Shared/Pages/ClickToAdd.razor Adds Description override for the sample.
samples/core/dymaptic.GeoBlazor.Core.Sample.Shared/Pages/CalculateGeometries.razor Adds Description override for the sample.
samples/core/dymaptic.GeoBlazor.Core.Sample.Shared/Pages/CSVLayers.razor Adds Description override for the sample.
samples/core/dymaptic.GeoBlazor.Core.Sample.Shared/Pages/Bookmarks.razor Adds Description override for the sample.
samples/core/dymaptic.GeoBlazor.Core.Sample.Shared/Pages/BasemapsLayerLists.razor Adds Description override for the sample.
samples/core/dymaptic.GeoBlazor.Core.Sample.Shared/Pages/Basemaps.razor Adds Description override for the sample.
samples/core/dymaptic.GeoBlazor.Core.Sample.Shared/Pages/BasemapToggleWidgetPage.razor Adds Description override for the sample.
samples/core/dymaptic.GeoBlazor.Core.Sample.Shared/Pages/BasemapProjections.razor Adds Description override for the sample.

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

Comment thread samples/core/dymaptic.GeoBlazor.Core.Sample.Shared/Shared/MainLayout.razor Outdated
Comment on lines +60 to 63
padding-bottom: 16rem;
height: calc(100vh - 10rem);
overflow-y: auto;
}
Copy link

Copilot AI May 1, 2026

Choose a reason for hiding this comment

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

On narrow layouts (<1075px), article.content is given a fixed viewport-based height and overflow-y: auto. Since main also scrolls and contains .main-links above the article, this tends to create nested scrolling/double scrollbars. Consider making only one element scroll on small screens (e.g., remove the article’s fixed height/overflow in the base styles or adjust heights to account for main-links).

Copilot uses AI. Check for mistakes.
Comment thread samples/core/dymaptic.GeoBlazor.Core.Sample.Shared/Shared/MainLayout.razor Outdated
Comment thread samples/pro/dymaptic.GeoBlazor.Pro.Sample.Shared/Pages/WFSUtils.razor Outdated
magmoe and others added 2 commits May 1, 2026 11:48
…onld

# Conflicts:
#	samples/core/dymaptic.GeoBlazor.Core.Sample.Shared/Shared/NavMenu.razor.css
- Canonicalize page URLs used in @id, url, isPartOf, and og:url by
  stripping query string and fragment from NavigationManager.Uri.
  Without this, hitting /compass-widget?foo=bar produced an invalid
  schema.org @id of "/compass-widget?foo=bar#webpage" (two # in one
  URL) and unstable canonical URLs across query-string variations.
- Clear LayoutService.CurrentPage on every navigation by subscribing
  to NavigationManager.LocationChanged in MainLayout. SamplePage
  routes re-set CurrentPage in OnInitialized; non-SamplePage routes
  (Home, NotFound) now leave it null so the layout no longer leaks
  the previous sample's action buttons, About-this-sample block,
  meta description, or JSON-LD onto pages that aren't sample pages.
- Fix grammar in WFSUtils description: "a label OGC WFS endpoint"
  to "a labeled OGC WFS endpoint".

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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 90 out of 90 changed files in this pull request and generated 3 comments.


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

Comment on lines +130 to +138
private string MetaTitle
{
get
{
string slug = CurrentSlug;
return string.IsNullOrEmpty(slug)
? "GeoBlazor Samples"
: $"{SlugToTitle(slug)} — GeoBlazor Sample";
}
Copy link

Copilot AI May 1, 2026

Choose a reason for hiding this comment

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

MetaTitle always formats non-empty routes as "… — GeoBlazor Sample" and SlugToTitle only splits on -. This produces incorrect titles for non-sample pages (e.g., /not-found) and broken titles for multi-segment routes like /source-code/{PageUrl} (will include a / in the title). Consider basing the "Sample" suffix on LayoutService.CurrentPage != null and updating the slug-to-title logic to handle / segments (e.g., take the last path segment and replace - with spaces).

Copilot uses AI. Check for mistakes.
Comment on lines 270 to +274
public void Dispose()
{
HttpClient.Dispose();
LayoutService.OnPageChanged -= StateHasChanged;
NavigationManager.LocationChanged -= OnLocationChanged;
Copy link

Copilot AI May 1, 2026

Choose a reason for hiding this comment

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

Dispose() calls HttpClient.Dispose(), but this HttpClient is injected from DI (likely scoped/singleton-managed). Disposing it in the layout can break subsequent HTTP calls elsewhere with ObjectDisposedException. Remove the explicit dispose here; let the DI container manage the lifetime (or use IHttpClientFactory/a dedicated disposable client if you truly need per-component ownership).

Copilot uses AI. Check for mistakes.
Comment on lines +123 to +137
.sample-description summary {
cursor: pointer;
font-weight: 500;
color: var(--text-emphasis);
user-select: none;
display: flex;
align-items: center;
gap: 0.6rem;
list-style: none;
padding-left: 0.25rem;
}

.sample-description summary::-webkit-details-marker {
display: none;
}
Copy link

Copilot AI May 1, 2026

Choose a reason for hiding this comment

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

The default disclosure marker is only removed for WebKit (summary::-webkit-details-marker). In Firefox/other browsers the marker will still render, resulting in a duplicated indicator alongside the custom summary::before arrow. Consider also clearing summary::marker (and/or list-style) for cross-browser consistency.

Copilot uses AI. Check for mistakes.
magmoe and others added 2 commits May 1, 2026 15:11
- MetaTitle only adds the "— GeoBlazor Sample" suffix when on an
  actual sample page (LayoutService.CurrentPage is not null).
  Routes like /not-found previously advertised themselves as
  "Not Found — GeoBlazor Sample".
- SlugToTitle takes the last path segment before kebab-splitting,
  so multi-segment routes like /source-code/{slug} produce a clean
  leaf-only title instead of leaking a / into the rendered title.
- Remove HttpClient.Dispose() from MainLayout.Dispose. Disposing
  the DI-injected HttpClient broke any later component using the
  same instance with ObjectDisposedException; DI manages lifetime.
- Clear the disclosure marker via summary::marker in addition to
  the WebKit pseudo. Without this, Firefox showed both the native
  marker and the custom CSS triangle.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@TimPurdum TimPurdum left a comment

Choose a reason for hiding this comment

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

Testing issues:

  • "About this page" never shows on first navigation from the navbar, only after a page refresh
  • The Schema.org LD-JSON is too static for the site, each page should have somewhat different content, espectially the description and url.

TimPurdum added 2 commits May 19, 2026 18:04
The "About this sample" details block, per-page meta tags, and JSON-LD
SoftwareSourceCode entry were missing on the first navigation from the
navbar - they only appeared after a page refresh. The cause was a
render-order race in MainLayout: the LocationChanged handler cleared
LayoutService.CurrentPage, but on first navigation the layout could
re-render with the new URL before the new SamplePage's OnInitialized
had a chance to re-register itself.

Replace the clear-on-navigation approach with a URI key on
LayoutService - SamplePage records the URI it set CurrentPage for, and
MainLayout's new EffectiveCurrentPage getter only treats CurrentPage as
live when that URI matches NavigationManager.Uri. Stale references
after navigating to a non-SamplePage route are silently ignored with
no timing dependency.

While there, enrich the per-page JSON-LD with keywords and
mainEntity/mainEntityOfPage cross-references so each sample page emits
visibly distinct schema.org content, and fix the unresolved double
scrollbar on narrow screens by moving article.content's fixed height
and overflow-y into the >=1075px media query (where <main> is set to
overflow:hidden).
ProWidgets.razor redeclared [Inject] NavigationManager, hiding the
one I added to SamplePage in the previous commit and failing the
build. Drop the redundant declaration; the base class injection is
inherited.

While there, remove the description field from the SoftwareSourceCode
JSON-LD entity. It duplicated the WebPage description verbatim on
every sample page. SoftwareSourceCode now relies on mainEntityOfPage
to cross-link to the WebPage where the canonical description lives.
@TimPurdum TimPurdum merged commit ff56fbc into main May 19, 2026
1 check passed
@TimPurdum TimPurdum deleted the feature/schema-org-jsonld branch May 19, 2026 22:31
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.

3 participants