diff --git a/OliverBooth/Markdown/Renderers/EditorJs/JsonRenderer.cs b/OliverBooth/Markdown/Renderers/EditorJs/JsonRenderer.cs
new file mode 100644
index 0000000..ee0d3c0
--- /dev/null
+++ b/OliverBooth/Markdown/Renderers/EditorJs/JsonRenderer.cs
@@ -0,0 +1,14 @@
+using System.Text.Json;
+using Markdig.Renderers;
+using Markdig.Syntax;
+
+namespace OliverBooth.Markdown.Renderers.EditorJs;
+
+public class JsonRenderer : RendererBase
+{
+ ///
+ public override object Render(MarkdownObject markdownObject)
+ {
+ return JsonDocument.Parse("""{"blocks": []}""");
+ }
+}
diff --git a/OliverBooth/Markdown/Renderers/EditorJs/ObjectRenderers/HeadingObjectRenderer.cs b/OliverBooth/Markdown/Renderers/EditorJs/ObjectRenderers/HeadingObjectRenderer.cs
new file mode 100644
index 0000000..099015d
--- /dev/null
+++ b/OliverBooth/Markdown/Renderers/EditorJs/ObjectRenderers/HeadingObjectRenderer.cs
@@ -0,0 +1,17 @@
+using Markdig.Renderers;
+using Markdig.Syntax;
+
+namespace OliverBooth.Markdown.Renderers.EditorJs.ObjectRenderers;
+
+public class HeadingObjectRenderer : IMarkdownObjectRenderer
+{
+ public bool Accept(RendererBase renderer, Type objectType)
+ {
+ return renderer.GetType() == typeof(JsonRenderer) && objectType == typeof(HeadingBlock);
+ }
+
+ public void Write(RendererBase renderer, MarkdownObject objectToRender)
+ {
+
+ }
+}
diff --git a/OliverBooth/Markdown/Renderers/JsonConverters/HeadingBlockConverter.cs b/OliverBooth/Markdown/Renderers/JsonConverters/HeadingBlockConverter.cs
new file mode 100644
index 0000000..4d201ad
--- /dev/null
+++ b/OliverBooth/Markdown/Renderers/JsonConverters/HeadingBlockConverter.cs
@@ -0,0 +1,53 @@
+using System.Text.Json;
+using System.Text.Json.Nodes;
+using System.Text.Json.Serialization;
+using Markdig.Helpers;
+using Markdig.Syntax;
+using Markdig.Syntax.Inlines;
+
+namespace OliverBooth.Markdown.Renderers.JsonConverters;
+
+public sealed class HeadingBlockConverter : JsonConverter
+{
+ ///
+ public override HeadingBlock? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ var node = JsonNode.Parse(reader.ValueSpan);
+ if (node is not JsonObject jsonObject)
+ {
+ return null;
+ }
+
+ return new HeadingBlock(null!)
+ {
+ Level = jsonObject["level"]?.GetValue() ?? 1,
+ Lines = new StringLineGroup(jsonObject["text"]?.GetValue() ?? string.Empty)
+ };
+ }
+
+ ///
+ public override void Write(Utf8JsonWriter writer, HeadingBlock value, JsonSerializerOptions options)
+ {
+ if (value.Inline is not { } containerInline)
+ {
+ return;
+ }
+
+ writer.WriteStartObject();
+ writer.WriteString("type", "header");
+ writer.WriteNumber("level", value.Level);
+ writer.WritePropertyName("text");
+
+ foreach (Inline inline in containerInline)
+ {
+ if (inline is LiteralInline literal)
+ {
+ var converter = (JsonConverter)options.GetConverter(typeof(LiteralInline));
+ writer.WriteStringValue(literal.Content.Text);
+ converter.Write(writer, literal, options);
+ }
+ }
+
+ writer.WriteEndObject();
+ }
+}
diff --git a/OliverBooth/Markdown/Renderers/JsonConverters/LiteralInlineConverter.cs b/OliverBooth/Markdown/Renderers/JsonConverters/LiteralInlineConverter.cs
new file mode 100644
index 0000000..2ca9554
--- /dev/null
+++ b/OliverBooth/Markdown/Renderers/JsonConverters/LiteralInlineConverter.cs
@@ -0,0 +1,22 @@
+using System.Text;
+using System.Text.Json;
+using System.Text.Json.Serialization;
+using Markdig.Syntax.Inlines;
+
+namespace OliverBooth.Markdown.Renderers.JsonConverters;
+
+public class LiteralInlineConverter : JsonConverter
+{
+ public override LiteralInline? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ ReadOnlySpan bytes = reader.ValueSpan;
+ Span chars = stackalloc char[bytes.Length];
+ Encoding.UTF8.GetChars(bytes, chars);
+ return new LiteralInline(chars.ToString());
+ }
+
+ public override void Write(Utf8JsonWriter writer, LiteralInline value, JsonSerializerOptions options)
+ {
+ writer.WriteStringValue(value.Content.Text);
+ }
+}
diff --git a/OliverBooth/Markdown/Renderers/JsonConverters/MarkdownDocumentConverter.cs b/OliverBooth/Markdown/Renderers/JsonConverters/MarkdownDocumentConverter.cs
new file mode 100644
index 0000000..eacc3a1
--- /dev/null
+++ b/OliverBooth/Markdown/Renderers/JsonConverters/MarkdownDocumentConverter.cs
@@ -0,0 +1,57 @@
+using System.Text.Json;
+using System.Text.Json.Serialization;
+using Markdig.Syntax;
+
+namespace OliverBooth.Markdown.Renderers.JsonConverters;
+
+public sealed class MarkdownDocumentConverter : JsonConverter
+{
+ ///
+ public override MarkdownDocument Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ var document = new MarkdownDocument();
+ var blocks = JsonSerializer.Deserialize(ref reader, options) ?? Array.Empty();
+
+ foreach (Block block in blocks)
+ {
+ document.Add(block);
+ }
+
+ return document;
+ }
+
+ ///
+ public override void Write(Utf8JsonWriter writer, MarkdownDocument value, JsonSerializerOptions options)
+ {
+ writer.WriteStartObject();
+ writer.WriteStartArray("blocks");
+
+ foreach (Block block in value)
+ {
+ WriteBlock(writer, options, block);
+ }
+
+ writer.WriteEndArray();
+ writer.WriteEndObject();
+ }
+
+ private static void WriteBlock(Utf8JsonWriter writer, JsonSerializerOptions options, Block block)
+ {
+ switch (block)
+ {
+ case ParagraphBlock paragraphBlock:
+ {
+ var converter = (JsonConverter)options.GetConverter(typeof(ParagraphBlock));
+ converter.Write(writer, paragraphBlock, options);
+ break;
+ }
+
+ case HeadingBlock headingBlock:
+ {
+ var converter = (JsonConverter)options.GetConverter(typeof(HeadingBlock));
+ converter.Write(writer, headingBlock, options);
+ break;
+ }
+ }
+ }
+}
diff --git a/OliverBooth/Markdown/Renderers/JsonConverters/ParagraphBlockConverter.cs b/OliverBooth/Markdown/Renderers/JsonConverters/ParagraphBlockConverter.cs
new file mode 100644
index 0000000..5d7e996
--- /dev/null
+++ b/OliverBooth/Markdown/Renderers/JsonConverters/ParagraphBlockConverter.cs
@@ -0,0 +1,35 @@
+using System.Text.Json;
+using System.Text.Json.Nodes;
+using System.Text.Json.Serialization;
+using Markdig.Helpers;
+using Markdig.Syntax;
+
+namespace OliverBooth.Markdown.Renderers.JsonConverters;
+
+public sealed class ParagraphBlockConverter : JsonConverter
+{
+ ///
+ public override ParagraphBlock? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+ {
+ var node = JsonNode.Parse(reader.ValueSpan);
+ if (node is not JsonObject jsonObject)
+ {
+ return null;
+ }
+
+ return new ParagraphBlock
+ {
+ Lines = new StringLineGroup(jsonObject["text"]?.GetValue() ?? string.Empty)
+ };
+ }
+
+ ///
+ public override void Write(Utf8JsonWriter writer, ParagraphBlock value, JsonSerializerOptions options)
+ {
+ string text = string.Join('\n', value.Inline);
+ writer.WriteStartObject();
+ writer.WriteString("type", "paragraph");
+ writer.WriteString("text", text);
+ writer.WriteEndObject();
+ }
+}
diff --git a/OliverBooth/Pages/Components/MarkdownEditor.razor b/OliverBooth/Pages/Components/MarkdownEditor.razor
index 97b85bf..34e8bdd 100644
--- a/OliverBooth/Pages/Components/MarkdownEditor.razor
+++ b/OliverBooth/Pages/Components/MarkdownEditor.razor
@@ -1,3 +1,4 @@
+@using System.Text.Json
@using OliverBooth.Common.Data.Blog
@using OliverBooth.Common.Services
@implements IDisposable
@@ -7,6 +8,17 @@
@code {
private DotNetObjectReference? _dotNetHelper;
+ [JSInvokable]
+ public string GetEditorObject(Guid id)
+ {
+ if (!BlogPostService.TryGetPost(id, out IBlogPost? post))
+ {
+ return JsonSerializer.Serialize(new { blocks = Array.Empty