Skip to content

feat: Custom editor#510

Merged
linkdotnet merged 1 commit intomasterfrom
custom-markdown-edtior
Mar 28, 2026
Merged

feat: Custom editor#510
linkdotnet merged 1 commit intomasterfrom
custom-markdown-edtior

Conversation

@linkdotnet
Copy link
Copy Markdown
Owner

No description provided.

Copilot AI review requested due to automatic review settings March 27, 2026 13:51
Copy link
Copy Markdown
Contributor

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

This PR replaces the Blazorise-based markdown editor with a custom MarkdownTextArea implementation (toolbar, preview, image upload) and wires up the required static assets (CSS/JS) and dependencies.

Changes:

  • Replaced the Blazorise markdown component with a custom toolbar-driven <textarea> + preview mode using JS interop.
  • Added new editor static assets (markdown-editor.js and markdown-editor.css) and included them in App.razor.
  • Removed Blazorise package references and the related service registration, and added unit tests covering the new editor UI/behaviors.

Reviewed changes

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

Show a summary per file
File Description
tests/LinkDotNet.Blog.UnitTests/Web/Features/Components/MarkdownTextAreaTests.cs Adds bUnit coverage for rendering, toolbar buttons, preview toggling, and insert fallbacks.
src/LinkDotNet.Blog.Web/wwwroot/js/markdown-editor.js Implements JS helpers for insert operations, undo/redo, highlighting, and keyboard shortcuts.
src/LinkDotNet.Blog.Web/wwwroot/css/markdown-editor.css Provides styling for the new editor container, toolbar, preview, and table popover.
src/LinkDotNet.Blog.Web/Features/Components/MarkdownTextArea.razor Replaces Blazorise editor with custom markup + JS interop for editing actions and uploads.
src/LinkDotNet.Blog.Web/App.razor Includes new editor CSS/JS and Bootstrap Icons CDN.
src/LinkDotNet.Blog.Web/ServiceExtensions.cs Removes Blazorise service registration extension.
src/LinkDotNet.Blog.Web/Program.cs Removes the Blazorise registration call from service setup.
src/LinkDotNet.Blog.Web/LinkDotNet.Blog.Web.csproj Drops Blazorise package references.
Directory.Packages.props Removes centralized Blazorise package version entries.

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

Comment on lines +369 to +371
using var memoryStream = new MemoryStream();
await file.OpenReadStream().CopyToAsync(memoryStream);
memoryStream.Position = 0;
Copy link

Copilot AI Mar 27, 2026

Choose a reason for hiding this comment

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

file.OpenReadStream() uses a small default max size (and will throw for larger files). For image uploads this can easily break in production. Pass an explicit maxAllowedSize (ideally from configuration) and handle the oversize exception to show a friendly error.

Copilot uses AI. Check for mistakes.
</div>
<script src="js/markdown-editor.js"></script>
<script src="_framework/blazor.web.js"></script>
<script async src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/highlight.min.js" integrity="sha512-EBLzUL8XLl+va/zAsmXwS7Z2B1F9HUHkZwyS/VKwh3S7T/U0nF4BaU29EP/ZSf6zgiIxYAnKLu6bJ8dqpmX5uw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
Copy link

Copilot AI Mar 27, 2026

Choose a reason for hiding this comment

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

highlight.js is loaded with async, but preview rendering triggers highlighting immediately via JS interop. If hljs hasn’t loaded yet, markdownEditor.highlightCodeBlocks() will no-op and code blocks won’t be highlighted for that preview session. Consider loading highlight.js with defer (or without async), or implement a retry/ready mechanism before attempting to highlight.

Suggested change
<script async src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/highlight.min.js" integrity="sha512-EBLzUL8XLl+va/zAsmXwS7Z2B1F9HUHkZwyS/VKwh3S7T/U0nF4BaU29EP/ZSf6zgiIxYAnKLu6bJ8dqpmX5uw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/highlight.min.js" integrity="sha512-EBLzUL8XLl+va/zAsmXwS7Z2B1F9HUHkZwyS/VKwh3S7T/U0nF4BaU29EP/ZSf6zgiIxYAnKLu6bJ8dqpmX5uw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>

Copilot uses AI. Check for mistakes.
Comment on lines +242 to +247
previewHtml = await PreviewFunction(Value);
StateHasChanged();
await InvokeAsync(async () =>
{
await JSRuntime.InvokeVoidAsync("markdownEditor.highlightCodeBlocks");
});
Copy link

Copilot AI Mar 27, 2026

Choose a reason for hiding this comment

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

UpdatePreview() calls StateHasChanged() and then immediately invokes markdownEditor.highlightCodeBlocks. In Blazor, the DOM update from StateHasChanged() is not guaranteed to have completed before the JS call runs, so highlighting may miss newly rendered code blocks. A more reliable approach is to set a flag and perform the highlighting from OnAfterRenderAsync after the preview render completes.

Copilot uses AI. Check for mistakes.
Comment on lines +72 to +74
return {
canUndo: textareaElement.value && textareaElement.value.length > 0,
canRedo: false
Copy link

Copilot AI Mar 27, 2026

Choose a reason for hiding this comment

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

getUndoRedoState always returns canRedo: false, which means the Redo toolbar button will remain disabled even if document.execCommand('redo') could work. Consider deriving these values from document.queryCommandEnabled('undo'/'redo') (or maintaining your own undo stack) so the UI state matches actual behavior.

Suggested change
return {
canUndo: textareaElement.value && textareaElement.value.length > 0,
canRedo: false
if (!textareaElement) {
return {
canUndo: false,
canRedo: false
};
}
// Ensure the correct element is the active editing target
textareaElement.focus();
let canUndo = false;
let canRedo = false;
if (typeof document.queryCommandEnabled === 'function') {
try {
canUndo = document.queryCommandEnabled('undo');
canRedo = document.queryCommandEnabled('redo');
} catch (e) {
// If the command query fails, fall back to the value-based check below.
}
}
// Preserve existing behavior as a fallback for undo if the command query is unavailable
if (!canUndo && textareaElement.value && textareaElement.value.length > 0) {
canUndo = true;
}
return {
canUndo: canUndo,
canRedo: canRedo

Copilot uses AI. Check for mistakes.
Comment on lines +180 to +183
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
Copy link

Copilot AI Mar 27, 2026

Choose a reason for hiding this comment

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

OnAfterRenderAsync only calls markdownEditor.setupKeyboardShortcuts on firstRender. Because the <textarea> is conditionally rendered (removed in preview mode and re-created when switching back), keyboard shortcuts won’t be re-attached after toggling Preview → Editor. Consider either keeping the textarea in the DOM (hide/show), or re-running the JS setup whenever returning to editor / when textAreaRef changes.

Copilot uses AI. Check for mistakes.
@linkdotnet linkdotnet force-pushed the custom-markdown-edtior branch 4 times, most recently from 72f6ccb to a1a630a Compare March 27, 2026 16:01
fix: Add aria-labels

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

fix: Check for mac properly

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

fix: undo/redo

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

fix: proper handling

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@linkdotnet linkdotnet force-pushed the custom-markdown-edtior branch from a1a630a to a53eb93 Compare March 27, 2026 16:32
@linkdotnet
Copy link
Copy Markdown
Owner Author

Closes #508

@linkdotnet linkdotnet merged commit 34a656b into master Mar 28, 2026
3 checks passed
@linkdotnet linkdotnet deleted the custom-markdown-edtior branch March 28, 2026 18:43
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