DocsPage.razor 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. @page "/docs/{Page}"
  2. @using Markdig
  3. @inject HttpClient Http
  4. @inject NavigationManager Nav
  5. @inject IJSRuntime JS
  6. @implements IDisposable
  7. <PageTitle>Docs: @Page</PageTitle>
  8. <div class="min-h-screen bg-zinc-950 text-zinc-200 font-mono p-6 space-y-6">
  9. <!-- Header -->
  10. <div class="space-y-2">
  11. <h1 class="text-lg text-zinc-100">
  12. Docs:
  13. <span class="text-emerald-400">@Page</span>
  14. </h1>
  15. </div>
  16. @if (_isLoading)
  17. {
  18. <div class="text-zinc-500">loading documentation…</div>
  19. }
  20. else if (_notFound)
  21. {
  22. <div class="text-zinc-500">document not found</div>
  23. }
  24. else
  25. {
  26. <div class="markdown">
  27. @((MarkupString)_htmlContent!)
  28. </div>
  29. }
  30. </div>
  31. @code {
  32. [Parameter]
  33. public string Page { get; set; } = string.Empty;
  34. private string? _htmlContent;
  35. private bool _isLoading = true;
  36. private bool _notFound;
  37. private static readonly MarkdownPipeline Pipeline =
  38. new MarkdownPipelineBuilder()
  39. .UseAdvancedExtensions()
  40. .UseAutoIdentifiers()
  41. .Build();
  42. private bool _pendingScroll;
  43. protected override void OnInitialized()
  44. {
  45. Nav.LocationChanged += HandleLocationChanged;
  46. }
  47. private void HandleLocationChanged(object? sender, LocationChangedEventArgs e)
  48. {
  49. // Trigger re-render so OnAfterRenderAsync runs again
  50. _pendingScroll = true;
  51. InvokeAsync(StateHasChanged);
  52. }
  53. protected override async Task OnParametersSetAsync()
  54. {
  55. _isLoading = true;
  56. _notFound = false;
  57. _pendingScroll = true;
  58. try
  59. {
  60. var decoded = Uri.UnescapeDataString(Page);
  61. if (!decoded.EndsWith(".md", StringComparison.OrdinalIgnoreCase))
  62. decoded += ".md";
  63. var url = $"_content/Shared.Rcl/raw_docs/{decoded}";
  64. var markdown = await Http.GetStringAsync(url);
  65. _htmlContent = Markdown.ToHtml(markdown, Pipeline);
  66. }
  67. catch
  68. {
  69. _notFound = true;
  70. _htmlContent = null;
  71. }
  72. finally
  73. {
  74. _isLoading = false;
  75. }
  76. }
  77. protected override async Task OnAfterRenderAsync(bool firstRender)
  78. {
  79. if (_pendingScroll && !_isLoading && !_notFound)
  80. {
  81. _pendingScroll = false;
  82. await ScrollToFragmentAsync();
  83. }
  84. }
  85. private async Task ScrollToFragmentAsync()
  86. {
  87. var uri = new Uri(Nav.Uri);
  88. var fragment = uri.Fragment;
  89. if (!string.IsNullOrWhiteSpace(fragment))
  90. {
  91. var anchor = fragment.TrimStart('#');
  92. await JS.InvokeVoidAsync("scrollToAnchor", anchor);
  93. }
  94. }
  95. public void Dispose()
  96. {
  97. Nav.LocationChanged -= HandleLocationChanged;
  98. }
  99. }