diff --git a/frontend/app_flowy/packages/flowy_editor/lib/infra/html_converter.dart b/frontend/app_flowy/packages/flowy_editor/lib/infra/html_converter.dart
index ece4f6b9f4..6895643744 100644
--- a/frontend/app_flowy/packages/flowy_editor/lib/infra/html_converter.dart
+++ b/frontend/app_flowy/packages/flowy_editor/lib/infra/html_converter.dart
@@ -1,5 +1,6 @@
 import 'dart:collection';
 
+import 'package:flowy_editor/document/attributes.dart';
 import 'package:flowy_editor/document/node.dart';
 import 'package:flowy_editor/document/text_delta.dart';
 import 'package:flutter/foundation.dart';
@@ -20,7 +21,8 @@ class HTMLConverter {
       if (child is html.Element) {
         if (child.localName == "a" ||
             child.localName == "span" ||
-            child.localName == "strong") {
+            child.localName == "strong" ||
+            child.localName == "b") {
           _handleRichTextElement(delta, child);
         } else {
           _handleElement(result, child);
@@ -63,9 +65,33 @@ class HTMLConverter {
     _handleRichText(nodes, element);
   }
 
+  Attributes? _getDeltaAttributesFromHtmlAttributes(
+      LinkedHashMap<Object, String> htmlAttributes) {
+    final attrs = <String, dynamic>{};
+    final styleString = htmlAttributes["style"];
+    if (styleString != null) {
+      final entries = styleString.split(";");
+      for (final entry in entries) {
+        final tuples = entry.split(":");
+        if (tuples.length < 2) {
+          continue;
+        }
+        if (tuples[0] == "font-weight") {
+          int? weight = int.tryParse(tuples[1]);
+          if (weight != null && weight > 500) {
+            attrs["bold"] = true;
+          }
+        }
+      }
+    }
+
+    return attrs.isEmpty ? null : attrs;
+  }
+
   _handleRichTextElement(Delta delta, html.Element element) {
     if (element.localName == "span") {
-      delta.insert(element.text);
+      delta.insert(element.text,
+          _getDeltaAttributesFromHtmlAttributes(element.attributes));
     } else if (element.localName == "a") {
       final hyperLink = element.attributes["href"];
       Map<String, dynamic>? attributes;
@@ -73,8 +99,10 @@ class HTMLConverter {
         attributes = {"href": hyperLink};
       }
       delta.insert(element.text, attributes);
-    } else if (element.localName == "strong") {
+    } else if (element.localName == "strong" || element.localName == "b") {
       delta.insert(element.text, {"bold": true});
+    } else {
+      delta.insert(element.text);
     }
   }
 
@@ -89,13 +117,7 @@ class HTMLConverter {
 
     for (final child in element.nodes.toList()) {
       if (child is html.Element) {
-        if (child.localName == "a" ||
-            child.localName == "span" ||
-            child.localName == "strong") {
-          _handleRichTextElement(delta, element);
-        } else {
-          delta.insert(child.text);
-        }
+        _handleRichTextElement(delta, child);
       } else {
         delta.insert(child.text ?? "");
       }
@@ -147,3 +169,21 @@ class HTMLConverter {
     }
   }
 }
+
+String deltaToHtml(Delta delta) {
+  var result = "<p>";
+
+  for (final op in delta.operations) {
+    if (op is TextInsert) {
+      final attributes = op.attributes;
+      if (attributes != null && attributes["bold"] == true) {
+        result += '<strong>${op.content}</strong>';
+      } else {
+        result += op.content;
+      }
+    }
+  }
+
+  result += "</p>";
+  return result;
+}
diff --git a/frontend/app_flowy/packages/flowy_editor/lib/service/internal_key_event_handlers/copy_paste_handler.dart b/frontend/app_flowy/packages/flowy_editor/lib/service/internal_key_event_handlers/copy_paste_handler.dart
index 6e6e30dbf4..f413801771 100644
--- a/frontend/app_flowy/packages/flowy_editor/lib/service/internal_key_event_handlers/copy_paste_handler.dart
+++ b/frontend/app_flowy/packages/flowy_editor/lib/service/internal_key_event_handlers/copy_paste_handler.dart
@@ -5,7 +5,26 @@ import 'package:flutter/material.dart';
 import 'package:flutter/services.dart';
 import 'package:rich_clipboard/rich_clipboard.dart';
 
-_handleCopy() async {
+_handleCopy(EditorState editorState) async {
+  final selection = editorState.cursorSelection;
+  if (selection == null || selection.isCollapsed) {
+    return;
+  }
+  if (pathEquals(selection.start.path, selection.end.path)) {
+    final nodeAtPath = editorState.document.nodeAtPath(selection.end.path)!;
+    if (nodeAtPath.type == "text") {
+      final textNode = nodeAtPath as TextNode;
+      final delta =
+          textNode.delta.slice(selection.start.offset, selection.end.offset);
+
+      final htmlString = deltaToHtml(delta);
+      debugPrint('copy html: $htmlString');
+      RichClipboard.setData(RichClipboardData(html: htmlString));
+    } else {
+      debugPrint("unimplemented: copy non-text");
+    }
+    return;
+  }
   debugPrint('copy');
 }
 
@@ -20,6 +39,7 @@ _pasteHTML(EditorState editorState, String html) {
     return;
   }
 
+  debugPrint('paste html: $html');
   final converter = HTMLConverter(html);
   final nodes = converter.toNodes();
 
@@ -38,6 +58,7 @@ _pasteHTML(EditorState editorState, String html) {
       tb.setAfterSelection(Selection.collapsed(Position(
           path: path, offset: startOffset + firstTextNode.delta.length)));
       tb.commit();
+      return;
     }
   }
 
@@ -64,12 +85,16 @@ _pasteMultipleLinesInText(
             .delete(remain.length)
             .concat(firstTextNode.delta));
 
-    path[path.length - 1]++;
     final tailNodes = nodes.sublist(1);
-    if (tailNodes.last.type == "text") {
-      final tailTextNode = tailNodes.last as TextNode;
-      tailTextNode.delta = tailTextNode.delta.concat(remain);
-    } else if (remain.length > 0) {
+    path[path.length - 1]++;
+    if (tailNodes.isNotEmpty) {
+      if (tailNodes.last.type == "text") {
+        final tailTextNode = tailNodes.last as TextNode;
+        tailTextNode.delta = tailTextNode.delta.concat(remain);
+      } else if (remain.length > 0) {
+        tailNodes.add(TextNode(type: "text", delta: remain));
+      }
+    } else {
       tailNodes.add(TextNode(type: "text", delta: remain));
     }
 
@@ -165,7 +190,7 @@ _handleCut() {
 
 FlowyKeyEventHandler copyPasteKeysHandler = (editorState, event) {
   if (event.isMetaPressed && event.logicalKey == LogicalKeyboardKey.keyC) {
-    _handleCopy();
+    _handleCopy(editorState);
     return KeyEventResult.handled;
   }
   if (event.isMetaPressed && event.logicalKey == LogicalKeyboardKey.keyV) {
diff --git a/frontend/app_flowy/packages/flowy_editor/lib/service/selection_service.dart b/frontend/app_flowy/packages/flowy_editor/lib/service/selection_service.dart
index 59632773e5..a304272846 100644
--- a/frontend/app_flowy/packages/flowy_editor/lib/service/selection_service.dart
+++ b/frontend/app_flowy/packages/flowy_editor/lib/service/selection_service.dart
@@ -437,7 +437,7 @@ class _FlowySelectionState extends State<FlowySelection>
       final selection = Selection(
           start: isDownward ? start : end, end: isDownward ? end : start);
       debugPrint('[_onPanUpdate] isDownward = $isDownward, $selection');
-      editorState.service.selectionService.updateSelection(selection);
+      editorState.updateCursorSelection(selection);
     }
   }