notlmn / copy-as-markdown

🖱 Browser extension to copy hyperlinks, images, and selected text as Markdown with GFM support
MIT License
323 stars 48 forks source link

Multiple line codeblocks with triple backticks #34

Closed gwpl closed 1 year ago

gwpl commented 1 year ago

When copying conversation from chat.openai.com ChatGPT / GPT4 , I believe multiple improvements could be made ( e.g. #33 ),

but one that I believe also would help on many other sides with codeblocks that are easy to copy and paste:

Current behaviour:

> after copy&paste of codeblock
> 
> `multi
> line
> code
> is surrounded by single backticks`
> unfortunately

Expected behaviour:

> after copy-as-markdown
> 
> ```
> multi line
> line code/text
> is surrounded by
>  triple backticks
> ```

P.S. I used > indentation because otherwise I don't know how to put triple backticks inside triple backticks in Markdown

notlmn commented 1 year ago

Could you paste the markup of this response here, if it's just <code></code> that would be considered single-line code.

It has to be <pre></pre> or <pre><code></code></pre> for it to be considered multi-line codeblock if I'm not wrong according to Turndown.

gwpl commented 1 year ago

Sure, , just ask GPT for few lines of code, here it is random sample:

Screenshot_20230421_174354

raw html:

<div class="relative flex w-[calc(100%-50px)] flex-col gap-1 md:gap-3 lg:w-[calc(100%-115px)]"><div class="flex flex-grow flex-col gap-3"><div class="min-h-[20px] flex flex-col items-start gap-4 whitespace-pre-wrap"><div class="markdown prose w-full break-words dark:prose-invert dark"><p>Sure! Here's an example of 5 lines of Rust code that utilize a few advanced language features:</p><pre><div class="bg-black rounded-md mb-4"><div class="flex items-center relative text-gray-200 bg-gray-800 px-4 py-2 text-xs font-sans justify-between rounded-t-md"><span>rust</span><button class="flex ml-auto gap-2"><svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" class="h-4 w-4" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"></path><rect x="8" y="2" width="8" height="4" rx="1" ry="1"></rect></svg>Copy code</button></div><div class="p-4 overflow-y-auto"><code class="!whitespace-pre hljs language-rust"><span class="hljs-keyword">let</span> <span class="hljs-keyword"></span><span class="hljs-variable">arr</span> = [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>];
<span class="hljs-keyword">let</span> <span class="hljs-keyword"></span><span class="hljs-variable">sum_of_squares</span>: <span class="hljs-type">i32</span> = arr.<span class="hljs-title function_ invoke__">iter</span>().<span class="hljs-title function_ invoke__">map</span>(|&amp;x| x * x).<span class="hljs-title function_ invoke__">sum</span>();
<span class="hljs-built_in">println!</span>(<span class="hljs-string">"Sum of squares of {:?} is {}"</span>, arr, sum_of_squares);
</code></div></div></pre><p>This code creates an array of integers, squares each element using an iterator's <code>map</code> method and then computes the sum of the squared values using the <code>sum</code> method. Finally, it prints out the sum of squares using string formatting.</p><p>This code uses Rust's array types, iterators, closures, and string formatting all in just 5 lines of code!</p></div></div></div><div class="flex justify-between lg:block"><div class="text-xs flex items-center justify-center gap-1 self-center pt-2 !invisible"><button disabled="" class="dark:text-white disabled:text-gray-300 dark:disabled:text-gray-400"><svg stroke="currentColor" fill="none" stroke-width="1.5" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" class="h-3 w-3" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><polyline points="15 18 9 12 15 6"></polyline></svg></button><span class="flex-grow flex-shrink-0">1 / 1</span><button disabled="" class="dark:text-white disabled:text-gray-300 dark:disabled:text-gray-400"><svg stroke="currentColor" fill="none" stroke-width="1.5" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" class="h-3 w-3" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><polyline points="9 18 15 12 9 6"></polyline></svg></button></div><div class="text-gray-400 flex self-end lg:self-center justify-center mt-2 gap-2 md:gap-3 lg:gap-1 lg:absolute lg:top-0 lg:translate-x-full lg:right-0 lg:mt-0 lg:pl-2 visible"><button class="flex ml-auto gap-2 h-full w-full rounded-md p-1 hover:bg-gray-100 hover:text-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-gray-200 disabled:dark:hover:text-gray-400"><svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" class="h-4 w-4" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"></path><rect x="8" y="2" width="8" height="4" rx="1" ry="1"></rect></svg></button><button class="p-1 rounded-md hover:bg-gray-100 hover:text-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-gray-200 disabled:dark:hover:text-gray-400"><svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" class="h-4 w-4" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M14 9V5a3 3 0 0 0-3-3l-4 9v11h11.28a2 2 0 0 0 2-1.7l1.38-9a2 2 0 0 0-2-2.3zM7 22H4a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2h3"></path></svg></button><button class="p-1 rounded-md hover:bg-gray-100 hover:text-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-gray-200 disabled:dark:hover:text-gray-400"><svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" class="h-4 w-4" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M10 15v4a3 3 0 0 0 3 3l4-9V2H5.72a2 2 0 0 0-2 1.7l-1.38 9a2 2 0 0 0 2 2.3zm7-13h2.67A2.31 2.31 0 0 1 22 4v7a2.31 2.31 0 0 1-2.33 2H17"></path></svg></button></div></div></div>

pretty formatted html:

<div
  class="relative flex w-[calc(100%-50px)] flex-col gap-1 md:gap-3 lg:w-[calc(100%-115px)]"
>
  <div class="flex flex-grow flex-col gap-3">
    <div
      class="min-h-[20px] flex flex-col items-start gap-4 whitespace-pre-wrap"
    >
      <div class="markdown prose w-full break-words dark:prose-invert dark">
        <p>
          Sure! Here's an example of 5 lines of Rust code that utilize a few
          advanced language features:
        </p>
        <pre><div class="bg-black rounded-md mb-4"><div class="flex items-center relative text-gray-200 bg-gray-800 px-4 py-2 text-xs font-sans justify-between rounded-t-md"><span>rust</span><button class="flex ml-auto gap-2"><svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" class="h-4 w-4" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"></path><rect x="8" y="2" width="8" height="4" rx="1" ry="1"></rect></svg>Copy code</button></div><div class="p-4 overflow-y-auto"><code class="!whitespace-pre hljs language-rust"><span class="hljs-keyword">let</span> <span class="hljs-keyword"></span><span class="hljs-variable">arr</span> = [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>];
<span class="hljs-keyword">let</span> <span class="hljs-keyword"></span><span class="hljs-variable">sum_of_squares</span>: <span class="hljs-type">i32</span> = arr.<span class="hljs-title function_ invoke__">iter</span>().<span class="hljs-title function_ invoke__">map</span>(|&amp;x| x * x).<span class="hljs-title function_ invoke__">sum</span>();
<span class="hljs-built_in">println!</span>(<span class="hljs-string">"Sum of squares of {:?} is {}"</span>, arr, sum_of_squares);
</code></div></div></pre>
        <p>
          This code creates an array of integers, squares each element using an
          iterator's <code>map</code> method and then computes the sum of the
          squared values using the <code>sum</code> method. Finally, it prints
          out the sum of squares using string formatting.
        </p>
        <p>
          This code uses Rust's array types, iterators, closures, and string
          formatting all in just 5 lines of code!
        </p>
      </div>
    </div>
  </div>
  <div class="flex justify-between lg:block">
    <div
      class="text-xs flex items-center justify-center gap-1 self-center pt-2 !invisible"
    >
      <button
        disabled=""
        class="dark:text-white disabled:text-gray-300 dark:disabled:text-gray-400"
      >
        <svg
          stroke="currentColor"
          fill="none"
          stroke-width="1.5"
          viewBox="0 0 24 24"
          stroke-linecap="round"
          stroke-linejoin="round"
          class="h-3 w-3"
          height="1em"
          width="1em"
          xmlns="http://www.w3.org/2000/svg"
        >
          <polyline points="15 18 9 12 15 6"></polyline>
        </svg></button
      ><span class="flex-grow flex-shrink-0">1 / 1</span
      ><button
        disabled=""
        class="dark:text-white disabled:text-gray-300 dark:disabled:text-gray-400"
      >
        <svg
          stroke="currentColor"
          fill="none"
          stroke-width="1.5"
          viewBox="0 0 24 24"
          stroke-linecap="round"
          stroke-linejoin="round"
          class="h-3 w-3"
          height="1em"
          width="1em"
          xmlns="http://www.w3.org/2000/svg"
        >
          <polyline points="9 18 15 12 9 6"></polyline>
        </svg>
      </button>
    </div>
    <div
      class="text-gray-400 flex self-end lg:self-center justify-center mt-2 gap-2 md:gap-3 lg:gap-1 lg:absolute lg:top-0 lg:translate-x-full lg:right-0 lg:mt-0 lg:pl-2 visible"
    >
      <button
        class="flex ml-auto gap-2 h-full w-full rounded-md p-1 hover:bg-gray-100 hover:text-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-gray-200 disabled:dark:hover:text-gray-400"
      >
        <svg
          stroke="currentColor"
          fill="none"
          stroke-width="2"
          viewBox="0 0 24 24"
          stroke-linecap="round"
          stroke-linejoin="round"
          class="h-4 w-4"
          height="1em"
          width="1em"
          xmlns="http://www.w3.org/2000/svg"
        >
          <path
            d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"
          ></path>
          <rect x="8" y="2" width="8" height="4" rx="1" ry="1"></rect>
        </svg></button
      ><button
        class="p-1 rounded-md hover:bg-gray-100 hover:text-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-gray-200 disabled:dark:hover:text-gray-400"
      >
        <svg
          stroke="currentColor"
          fill="none"
          stroke-width="2"
          viewBox="0 0 24 24"
          stroke-linecap="round"
          stroke-linejoin="round"
          class="h-4 w-4"
          height="1em"
          width="1em"
          xmlns="http://www.w3.org/2000/svg"
        >
          <path
            d="M14 9V5a3 3 0 0 0-3-3l-4 9v11h11.28a2 2 0 0 0 2-1.7l1.38-9a2 2 0 0 0-2-2.3zM7 22H4a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2h3"
          ></path>
        </svg></button
      ><button
        class="p-1 rounded-md hover:bg-gray-100 hover:text-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-gray-200 disabled:dark:hover:text-gray-400"
      >
        <svg
          stroke="currentColor"
          fill="none"
          stroke-width="2"
          viewBox="0 0 24 24"
          stroke-linecap="round"
          stroke-linejoin="round"
          class="h-4 w-4"
          height="1em"
          width="1em"
          xmlns="http://www.w3.org/2000/svg"
        >
          <path
            d="M10 15v4a3 3 0 0 0 3 3l4-9V2H5.72a2 2 0 0 0-2 1.7l-1.38 9a2 2 0 0 0 2 2.3zm7-13h2.67A2.31 2.31 0 0 1 22 4v7a2.31 2.31 0 0 1-2.33 2H17"
          ></path>
        </svg>
      </button>
    </div>
  </div>
</div>

And result when I copy as markdown:

language features:

rustCopy code

`let arr = [1, 2, 3, 4, 5];
let sum_of_squares: i32 = arr.iter().map(|&x| x * x).sum();
println!("Sum of squares of {:?} is {}", arr, sum_of_squares);` 

This code creates

Yes, they made bigger blog

 but inner block with multi line code is  , so maybe worth to make a check if code between  is multiline and depending on it produce either in line fragment or 

multi
line

?

notlmn commented 1 year ago

<code> has to be an immediate child of <pre> for this to work.

See https://github.com/mixmark-io/turndown/blob/97e4535ca76bb2e70d9caa2aa4d4686956b06d44/src/commonmark-rules.js#L102-L109

We can't try and fix this from our side as there be an infinite number of combinations that we have to account for.

gwpl commented 1 year ago

Thank you for feedback. I will see if I have a moment to look into your code, as I am not JS expert, or if I know someone who could make Pull Request, if you would be open for such Pull Request?

notlmn commented 1 year ago

A fix like that would not be accepted here for the same reason mentioned in https://github.com/notlmn/copy-as-markdown/issues/33#issuecomment-1517990262

ChatGPT's HTML output in this case doesn't have the right markup that you would in in Markdown to HTML conversion for it to be parsable from HTML back to Markdown.

gwpl commented 1 year ago

Sure! I just thought that this case is general, i.e. that if user puts multi line code inside blocks, then it can be assumed to be inside ``` triple backtics. However I am not html or Markdown expert, so I am sorry if I get confused with your decision making. I just found copy-as-markdown very helpful for me and thought about sharing how to make it even more awesome! :) Thank you!