diff --git a/Resources/Plugins/emoji-keyboard/.meta b/Resources/Plugins/emoji-keyboard/.meta
index 7ebd72ee..ce497f98 100644
--- a/Resources/Plugins/emoji-keyboard/.meta
+++ b/Resources/Plugins/emoji-keyboard/.meta
@@ -9,7 +9,7 @@ Emoji keyboard
 chylex
 
 [version]
-1.2.2
+1.3
 
 [website]
 https://tweetduck.chylex.com
diff --git a/Resources/Plugins/emoji-keyboard/browser.js b/Resources/Plugins/emoji-keyboard/browser.js
index ec93abe0..b5a748f8 100644
--- a/Resources/Plugins/emoji-keyboard/browser.js
+++ b/Resources/Plugins/emoji-keyboard/browser.js
@@ -39,6 +39,10 @@ enabled(){
   this.css.insert(".emoji-keyboard-skintones div { width: 0.8em; height: 0.8em; margin: 0.25em 0.1em; border-radius: 50%; display: inline-block; box-sizing: border-box; cursor: pointer }");
   this.css.insert(".emoji-keyboard-skintones .sel { border: 2px solid rgba(0, 0, 0, 0.35); box-shadow: 0 0 2px 0 rgba(255, 255, 255, 0.65), 0 0 1px 0 rgba(255, 255, 255, 0.4) inset }");
   this.css.insert(".emoji-keyboard-skintones :hover { border: 2px solid rgba(0, 0, 0, 0.25); box-shadow: 0 0 1px 0 rgba(255, 255, 255, 0.65), 0 0 1px 0 rgba(255, 255, 255, 0.25) inset }");
+  this.css.insert("#emoji-keyboard-tweet-input { padding: 2px 0 !important; line-height: 18px }");
+  this.css.insert("#emoji-keyboard-tweet-input img { padding: 0.1em !important; width: 1em; height: 1em; vertical-align: -0.25em }");
+  this.css.insert("#emoji-keyboard-tweet-input:empty::before { content: \"What's happening?\"; display: inline-block; color: #ced8de }");
+  this.css.insert(".js-docked-compose .js-compose-text:not(.debug) { position: absolute; z-index: -9999; left: 0; opacity: 0 }");
   
   // layout
   
@@ -124,7 +128,7 @@ enabled(){
     updateFilters();
   };
   
-  this.generateKeyboard = (input, left, top) => {
+  this.generateKeyboard = (left, top) => {
     var outer = document.createElement("div");
     outer.classList.add("emoji-keyboard");
     outer.style.left = left+"px";
@@ -134,18 +138,10 @@ enabled(){
     keyboard.classList.add("emoji-keyboard-list");
     
     keyboard.addEventListener("click", function(e){
-      if (e.target.tagName === "IMG"){
-        var val = input.val();
-        var inserted = e.target.getAttribute("alt");
-        var posStart = input[0].selectionStart;
-        var posEnd = input[0].selectionEnd;
-        
-        input.val(val.slice(0, posStart)+inserted+val.slice(posStart));
-        input.trigger("change");
-        input.focus();
-        
-        input[0].selectionStart = posStart+inserted.length;
-        input[0].selectionEnd = posEnd+inserted.length;
+      let ele = e.target;
+      
+      if (ele.tagName === "IMG"){
+        insertEmoji(ele.getAttribute("src"), ele.getAttribute("alt"));
       }
       
       e.stopPropagation();
@@ -204,6 +200,26 @@ enabled(){
     return button.offset().top+button.outerHeight()+me.composePanelScroller.scrollTop()+8;
   };
   
+  var focusWithCaretAtEnd = () => {
+    let range = document.createRange();
+    range.selectNodeContents(me.composeInputNew[0]);
+    range.collapse(false);
+    
+    let sel = window.getSelection();
+    sel.removeAllRanges();
+    sel.addRange(range);
+  };
+  
+  var insertEmoji = (src, alt) => {
+    let sel = window.getSelection();
+    
+    if (!sel.anchorNode || $(sel.anchorNode).closest("#emoji-keyboard-tweet-input").length === 0){
+      focusWithCaretAtEnd();
+    }
+    
+    document.execCommand("insertHTML", false, `<img src="${src}" alt="${alt}">`);
+  };
+  
   // event handlers
   
   this.emojiKeyboardButtonClickEvent = function(e){
@@ -212,7 +228,7 @@ enabled(){
       $(this).blur();
     }
     else{
-      me.generateKeyboard($(".js-compose-text").first(), $(this).offset().left, getKeyboardTop());
+      me.generateKeyboard($(this).offset().left, getKeyboardTop());
       $(this).addClass("is-selected");
     }
     
@@ -230,7 +246,7 @@ enabled(){
   };
   
   this.documentClickEvent = function(e){
-    if (me.currentKeyboard && !e.target.classList.contains("js-compose-text")){
+    if (me.currentKeyboard && !e.target.classList.contains("compose-text")){
       hideKeyboard();
     }
   };
@@ -246,7 +262,77 @@ enabled(){
     if (me.currentKeyboard){
       me.currentKeyboard.style.top = getKeyboardTop()+"px";
     }
-  }
+  };
+  
+  var prevOldInputVal = "";
+  
+  this.composeOldInputFocusEvent = function(){
+    let ele = $(this);
+    let val = ele.val();
+    
+    if (val != prevOldInputVal){
+      prevOldInputVal = val;
+      
+      setTimeout(function(){
+        let selStart = ele[0].selectionStart;
+        let selEnd = ele[0].selectionEnd;
+        
+        me.composeInputNew.text(val);
+        me.composeInputNew.focus();
+        
+        let sel = window.getSelection();
+        let range = document.createRange();
+        range.setStart(me.composeInputNew[0].firstChild, selStart);
+        range.setEnd(me.composeInputNew[0].firstChild, selEnd);
+        
+        sel.removeAllRanges();
+        sel.addRange(range);
+      }, 1);
+    }
+    else{
+      focusWithCaretAtEnd();
+    }
+  };
+  
+  var allowedShortcuts = [ 65 /* A */, 67 /* C */, 86 /* V */, 89 /* Y */, 90 /* Z */ ];
+  
+  this.composeInputKeyEvent = function(e){
+    if (e.type === "keydown" && (e.ctrlKey || e.metaKey)){
+      if (e.keyCode === 13){
+        $(".js-send-button", ".js-docked-compose").click();
+      }
+      else if (e.keyCode >= 48 && !allowedShortcuts.includes(e.keyCode)){
+        e.preventDefault();
+        return;
+      }
+    }
+    
+    e.stopPropagation();
+  };
+  
+  this.composeInputUpdateEvent = function(){
+    let clone = $(this).clone();
+    
+    clone.children("div").each(function(){
+      let ele = $(this)[0];
+      ele.outerHTML = "\n"+ele.innerHTML;
+    });
+    
+    clone.children("img").each(function(){
+      let ele = $(this)[0];
+      ele.outerHTML = ele.getAttribute("alt");
+    });
+    
+    me.composeInputOrig.val(prevOldInputVal = clone.text());
+    me.composeInputOrig.trigger("change");
+  };
+  
+  this.composeInputPasteEvent = function(e){
+    e.preventDefault();
+    
+    let text = e.originalEvent.clipboardData.getData("text/plain");
+    text && document.execCommand("insertText", false, text);
+  };
 }
 
 ready(){
@@ -261,6 +347,17 @@ ready(){
   $(document).on("uiComposeImageAdded", this.uploadFilesEvent);
   this.composeDrawer.on("uiComposeTweetSending", this.composerSendingEvent);
   
+  // Editor
+  
+  this.composeInputOrig = $(".js-compose-text", ".js-docked-compose").first();
+  this.composeInputNew = $('<div id="emoji-keyboard-tweet-input" contenteditable="true" class="compose-text txt-size--14 scroll-v scroll-styled-v scroll-styled-h scroll-alt td-detect-image-paste"></div>').insertAfter(this.composeInputOrig);
+  
+  this.composeInputOrig.on("focus", this.composeOldInputFocusEvent);
+  
+  this.composeInputNew.on("keydown keypress keyup", this.composeInputKeyEvent);
+  this.composeInputNew.on("input", this.composeInputUpdateEvent);
+  this.composeInputNew.on("paste", this.composeInputPasteEvent);
+  
   // HTML generation
   
   var convUnicode = function(codePt){
@@ -280,12 +377,14 @@ ready(){
     
     // declaration inserters
     
+    let mapUnicode = pt => convUnicode(parseInt(pt, 16));
+    
     let addDeclaration1 = decl => {
-      this.emojiData1.push(decl.split(" ").map(pt => convUnicode(parseInt(pt, 16))).join(""));
+      this.emojiData1.push(decl.split(" ").map(mapUnicode).join(""));
     };
     
     let addDeclaration2 = (tone, decl) => {
-      let gen = decl.split(" ").map(pt => convUnicode(parseInt(pt, 16))).join("");
+      let gen = decl.split(" ").map(mapUnicode).join("");
       
       if (tone === null){
         for(let skinTone of this.skinToneList){
@@ -298,7 +397,7 @@ ready(){
     };
     
     let addDeclaration3 = decl => {
-      this.emojiData3.push(decl.split(" ").map(pt => convUnicode(parseInt(pt, 16))).join(""));
+      this.emojiData3.push(decl.split(" ").map(mapUnicode).join(""));
     };
     
     // line reading
@@ -372,6 +471,9 @@ disabled(){
     $(this.currentSpanner).remove();
   }
   
+  this.composeInputNew.remove();
+  
+  this.composeInputOrig.off("focus", this.composeOldInputFocusEvent);
   this.composePanelScroller.off("scroll", this.composerScrollEvent);
   
   $(".emoji-keyboard-popup-btn").off("click", this.emojiKeyboardButtonClickEvent);
diff --git a/Resources/Scripts/code.js b/Resources/Scripts/code.js
index 2a5ce9ff..8bc93fbc 100644
--- a/Resources/Scripts/code.js
+++ b/Resources/Scripts/code.js
@@ -526,7 +526,7 @@
   onAppReady.push(function(){
     var uploader = $._data(document, "events")["uiComposeAddImageClick"][0].handler.context;
     
-    app.delegate(".js-compose-text,.js-reply-tweetbox", "paste", function(e){
+    app.delegate(".js-compose-text,.js-reply-tweetbox,.td-detect-image-paste", "paste", function(e){
       for(let item of e.originalEvent.clipboardData.items){
         if (item.type.startsWith("image/")){
           if (!$(this).closest(".rpl").find(".js-reply-popout").click().length){ // popout direct messages