comrak
Docusaurus
comrak | Docusaurus | |
---|---|---|
5 | 282 | |
1,083 | 53,091 | |
- | 1.5% | |
8.7 | 9.5 | |
9 days ago | 1 day ago | |
Rust | TypeScript | |
GNU General Public License v3.0 or later | MIT License |
Stars - the number of stars that a project has on GitHub. Growth - month over month growth in stars.
Activity is a relative number indicating how actively a project is being developed. Recent commits have higher weight than older ones.
For example, an activity of 9.0 indicates that a project is amongst the top 10% of the most actively developed projects that we are tracking.
comrak
-
Create a Markdown Editor with Rust and React
I opened up the src/App.tsx and added a and synced any text changes to React state.
import { useState } from "react"; import reactLogo from "./assets/react.svg"; import { invoke } from "@tauri-apps/api/tauri"; import "./App.css"; function App() { const [markdown, setMarkdown] = useState(""); return (
className="container">Welcome to Tauri!
className="row"> href="https://vitejs.dev" target="_blank"> src="/vite.svg" className="logo vite" alt="Vite logo" /> href="https://tauri.app" target="_blank"> src="/tauri.svg" className="logo tauri" alt="Tauri logo" /> href="https://reactjs.org" target="_blank"> src={reactLogo} className="logo react" alt="React logo" />Click on the Tauri, Vite, and React logos to learn more.
className="row">id</span><span class="o">=</span><span class="s2">"greet-input"</span> <span class="nv">onChange</span><span class="o">={(</span>e<span class="o">)</span> <span class="o">=></span> setMarkdown<span class="o">(</span>e.currentTarget.value<span class="o">)}</span> /> </div> </div> </div> <span class="o">)</span><span class="p">;</span> <span class="o">}</span> <span class="nb">export </span>default App<span class="p">;</span> </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>Now that we have a textbox we can write Markdown inside, let’s convert that to HTML to preview.</p> <h2> <a name="creating-a-tauri-command" href="#creating-a-tauri-command"> </a> Creating a Tauri command </h2> <p>We’ll need to use <a href="https://tauri.app/v1/guides/features/command/">Tauri commands</a> to communicate between the frontend and backend - and vice versa (similar to <a href="https://www.electronjs.org/docs/latest/tutorial/ipc">the IPC layer in Electron</a>).</p> <p>Tauri comes with an example Tauri command called <code>greet()</code> already setup, so let’s use that and change it to parse Markdown. Open up <code>src-tauri/src/main.rs</code> and change it to the following:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight rust"><code><span class="nd">#![cfg_attr(</span> <span class="nd">all(not(debug_assertions),</span> <span class="nd">target_os</span> <span class="nd">=</span> <span class="s">"windows"</span><span class="nd">),</span> <span class="nd">windows_subsystem</span> <span class="nd">=</span> <span class="s">"windows"</span> <span class="nd">)]</span> <span class="c1">// Import comrak</span> <span class="k">use</span> <span class="nn">comrak</span><span class="p">::{</span><span class="n">markdown_to_html</span><span class="p">,</span> <span class="n">ComrakOptions</span><span class="p">};</span> <span class="c1">// Learn more about Tauri commands at https://tauri.app/v1/guides/features/command</span> <span class="nd">#[tauri::command]</span> <span class="k">fn</span> <span class="nf">greet</span><span class="p">(</span><span class="n">markdown</span><span class="p">:</span> <span class="o">&</span><span class="nb">str</span><span class="p">)</span> <span class="k">-></span> <span class="nb">String</span> <span class="p">{</span> <span class="c1">// format!("Hello, {}! You've been greeted from Rust!", name)</span> <span class="c1">// Parse the markdown text to HTML</span> <span class="k">let</span> <span class="n">html</span> <span class="o">=</span> <span class="nf">markdown_to_html</span><span class="p">(</span><span class="o">&</span><span class="n">markdown</span><span class="p">,</span> <span class="o">&</span><span class="nn">ComrakOptions</span><span class="p">::</span><span class="nf">default</span><span class="p">());</span> <span class="nd">println!</span><span class="p">(</span><span class="s">"Markdown parsed into HTML </span><span class="se">\n</span><span class="s">"</span><span class="p">);</span> <span class="nd">println!</span><span class="p">(</span><span class="s">"{html}"</span><span class="p">);</span> <span class="c1">// We return the HTML to the frontend (in Rust, we return by omitting the `;`)</span> <span class="n">html</span> <span class="p">}</span> <span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> <span class="nn">tauri</span><span class="p">::</span><span class="nn">Builder</span><span class="p">::</span><span class="nf">default</span><span class="p">()</span> <span class="c1">// Here is where we add our Tauri commands to our app</span> <span class="nf">.invoke_handler</span><span class="p">(</span><span class="nn">tauri</span><span class="p">::</span><span class="nd">generate_handler!</span><span class="p">[</span><span class="n">greet</span><span class="p">])</span> <span class="nf">.run</span><span class="p">(</span><span class="nn">tauri</span><span class="p">::</span><span class="nd">generate_context!</span><span class="p">())</span> <span class="nf">.expect</span><span class="p">(</span><span class="s">"error while running tauri application"</span><span class="p">);</span> <span class="p">}</span> </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>And in our frontend, let’s create a <code><button></code> we can press to run the Tauri command. We use the <code>invoke</code> function provided by Tauri that’ll call a Tauri command with the same name.<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight jsx"><code><span class="k">import</span> <span class="p">{</span> <span class="nx">useState</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">react</span><span class="dl">"</span><span class="p">;</span> <span class="k">import</span> <span class="nx">reactLogo</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">./assets/react.svg</span><span class="dl">"</span><span class="p">;</span> <span class="k">import</span> <span class="p">{</span> <span class="nx">invoke</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">@tauri-apps/api/tauri</span><span class="dl">"</span><span class="p">;</span> <span class="k">import</span> <span class="dl">"</span><span class="s2">./App.css</span><span class="dl">"</span><span class="p">;</span> <span class="kd">function</span> <span class="nx">App</span><span class="p">()</span> <span class="p">{</span> <span class="kd">const</span> <span class="p">[</span><span class="nx">html</span><span class="p">,</span> <span class="nx">setHtml</span><span class="p">]</span> <span class="o">=</span> <span class="nx">useState</span><span class="p">(</span><span class="dl">""</span><span class="p">);</span> <span class="kd">const</span> <span class="p">[</span><span class="nx">markdown</span><span class="p">,</span> <span class="nx">setMarkdown</span><span class="p">]</span> <span class="o">=</span> <span class="nx">useState</span><span class="p">(</span><span class="dl">""</span><span class="p">);</span> <span class="k">async</span> <span class="kd">function</span> <span class="nx">greet</span><span class="p">()</span> <span class="p">{</span> <span class="c1">// Learn more about Tauri commands at https://tauri.app/v1/guides/features/command</span> <span class="nx">setHtml</span><span class="p">(</span><span class="k">await</span> <span class="nx">invoke</span><span class="p">(</span><span class="dl">"</span><span class="s2">greet</span><span class="dl">"</span><span class="p">,</span> <span class="p">{</span> <span class="nx">markdown</span> <span class="p">}));</span> <span class="p">}</span> <span class="k">return</span> <span class="p">(</span> <span class="p"><</span><span class="nt">div</span> <span class="na">className</span><span class="p">=</span><span class="s">"container"</span><span class="p">></span> <span class="p"><</span><span class="nt">h1</span><span class="p">></span>Welcome to Tauri!<span class="p"></</span><span class="nt">h1</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">className</span><span class="p">=</span><span class="s">"row"</span><span class="p">></span> <span class="p"><</span><span class="nt">a</span> <span class="na">href</span><span class="p">=</span><span class="s">"https://vitejs.dev"</span> <span class="na">target</span><span class="p">=</span><span class="s">"_blank"</span><span class="p">></span> <span class="p"><</span><span class="nt">img</span> <span class="na">src</span><span class="p">=</span><span class="s">"/vite.svg"</span> <span class="na">className</span><span class="p">=</span><span class="s">"logo vite"</span> <span class="na">alt</span><span class="p">=</span><span class="s">"Vite logo"</span> <span class="p">/></span> <span class="p"></</span><span class="nt">a</span><span class="p">></span> <span class="p"><</span><span class="nt">a</span> <span class="na">href</span><span class="p">=</span><span class="s">"https://tauri.app"</span> <span class="na">target</span><span class="p">=</span><span class="s">"_blank"</span><span class="p">></span> <span class="p"><</span><span class="nt">img</span> <span class="na">src</span><span class="p">=</span><span class="s">"/tauri.svg"</span> <span class="na">className</span><span class="p">=</span><span class="s">"logo tauri"</span> <span class="na">alt</span><span class="p">=</span><span class="s">"Tauri logo"</span> <span class="p">/></span> <span class="p"></</span><span class="nt">a</span><span class="p">></span> <span class="p"><</span><span class="nt">a</span> <span class="na">href</span><span class="p">=</span><span class="s">"https://reactjs.org"</span> <span class="na">target</span><span class="p">=</span><span class="s">"_blank"</span><span class="p">></span> <span class="p"><</span><span class="nt">img</span> <span class="na">src</span><span class="p">=</span><span class="si">{</span><span class="nx">reactLogo</span><span class="si">}</span> <span class="na">className</span><span class="p">=</span><span class="s">"logo react"</span> <span class="na">alt</span><span class="p">=</span><span class="s">"React logo"</span> <span class="p">/></span> <span class="p"></</span><span class="nt">a</span><span class="p">></span> <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"><</span><span class="nt">p</span><span class="p">></span>Click on the Tauri, Vite, and React logos to learn more.<span class="p"></</span><span class="nt">p</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">className</span><span class="p">=</span><span class="s">"row"</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span><span class="p">></span> <span class="p"><</span><span class="nt">textarea</span> <span class="na">id</span><span class="p">=</span><span class="s">"greet-input"</span> <span class="na">onChange</span><span class="p">=</span><span class="si">{</span><span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="o">=></span> <span class="nx">setMarkdown</span><span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">currentTarget</span><span class="p">.</span><span class="nx">value</span><span class="p">)</span><span class="si">}</span> <span class="p">/></span> <span class="p"><</span><span class="nt">button</span> <span class="na">type</span><span class="p">=</span><span class="s">"button"</span> <span class="na">onClick</span><span class="p">=</span><span class="si">{</span><span class="p">()</span> <span class="o">=></span> <span class="nx">greet</span><span class="p">()</span><span class="si">}</span><span class="p">></span> Convert to HTML <span class="p"></</span><span class="nt">button</span><span class="p">></span> <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"><</span><span class="nt">p</span><span class="p">></span><span class="si">{</span><span class="nx">html</span><span class="si">}</span><span class="p"></</span><span class="nt">p</span><span class="p">></span> <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p">);</span> <span class="p">}</span> <span class="k">export</span> <span class="k">default</span> <span class="nx">App</span><span class="p">;</span> </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>If you type some Markdown into the text box and click the button, it should print out raw HTML representing your Markdown. Here’s <a href="https://github.com/whoisryosuke/tauri-markdown-editor/commit/321f5bcfb1c0e370c27b8781d6f70fd74e6b67df">the commit on Github for reference.</a></p> <h3> <a name="but-how-do-we-preview-the-html" href="#but-how-do-we-preview-the-html"> </a> But how do we preview the HTML? </h3> <p>If we wanted to preview the HTML, we could use React’s <code>dangerouslySetInnerHTML</code> to inject the HTML inside our app:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight jsx"><code><span class="kd">const</span> <span class="nx">createMarkdownMarkup</span> <span class="o">=</span> <span class="p">()</span> <span class="o">=></span> <span class="p">({</span> <span class="na">__html</span><span class="p">:</span> <span class="nx">html</span> <span class="p">})</span> <span class="o"><</span><span class="nx">div</span> <span class="nx">dangerouslySetInnerHTML</span><span class="o">=</span><span class="p">{</span><span class="nx">createMarkdownMarkup</span><span class="p">()}</span> <span class="sr">/</span><span class="err">> </span></code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>And ideally, this isn’t as “dangerous” as it sounds, because <a href="https://github.com/kivikakk/comrak#security">Comrak sanitizes the Markdown output for any malicious code</a> before converting to HTML 👍</p> <p>We could also update the <code>onChange</code> function to run our conversion process, instead of waiting for a button press, so we “instantly” see a live preview. In my case, I do it in a useEffect with a refresh boolean — but it’s all good. <em>Note, this is a little risky, since the user might be able to break the app, so in production it’d be best to create a cache of the last “working” preview just in case one fails.</em><br> </p> <div class="highlight js-code-highlight"> <pre class="highlight jsx"><code><span class="k">import</span> <span class="p">{</span> <span class="nx">useEffect</span><span class="p">,</span> <span class="nx">useRef</span><span class="p">,</span> <span class="nx">useState</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">react</span><span class="dl">"</span><span class="p">;</span> <span class="k">import</span> <span class="nx">reactLogo</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">./assets/react.svg</span><span class="dl">"</span><span class="p">;</span> <span class="k">import</span> <span class="p">{</span> <span class="nx">invoke</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">"</span><span class="s2">@tauri-apps/api/tauri</span><span class="dl">"</span><span class="p">;</span> <span class="k">import</span> <span class="dl">"</span><span class="s2">./App.css</span><span class="dl">"</span><span class="p">;</span> <span class="kd">function</span> <span class="nx">App</span><span class="p">()</span> <span class="p">{</span> <span class="kd">const</span> <span class="p">[</span><span class="nx">html</span><span class="p">,</span> <span class="nx">setHtml</span><span class="p">]</span> <span class="o">=</span> <span class="nx">useState</span><span class="p">(</span><span class="dl">""</span><span class="p">);</span> <span class="kd">const</span> <span class="p">[</span><span class="nx">markdown</span><span class="p">,</span> <span class="nx">setMarkdown</span><span class="p">]</span> <span class="o">=</span> <span class="nx">useState</span><span class="p">(</span><span class="dl">""</span><span class="p">);</span> <span class="kd">const</span> <span class="p">[</span><span class="nx">refreshCheck</span><span class="p">,</span> <span class="nx">setRefreshCheck</span><span class="p">]</span> <span class="o">=</span> <span class="nx">useState</span><span class="p">(</span><span class="kc">false</span><span class="p">);</span> <span class="k">async</span> <span class="kd">function</span> <span class="nx">greet</span><span class="p">()</span> <span class="p">{</span> <span class="nx">setRefreshCheck</span><span class="p">(</span><span class="kc">true</span><span class="p">);</span> <span class="p">}</span> <span class="nx">useEffect</span><span class="p">(()</span> <span class="o">=></span> <span class="p">{</span> <span class="kd">const</span> <span class="nx">parseMarkdown</span> <span class="o">=</span> <span class="k">async</span> <span class="p">()</span> <span class="o">=></span> <span class="p">{</span> <span class="nx">setHtml</span><span class="p">(</span><span class="k">await</span> <span class="nx">invoke</span><span class="p">(</span><span class="dl">"</span><span class="s2">greet</span><span class="dl">"</span><span class="p">,</span> <span class="p">{</span> <span class="nx">markdown</span> <span class="p">}));</span> <span class="nx">setRefreshCheck</span><span class="p">(</span><span class="kc">false</span><span class="p">);</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">new markdown</span><span class="dl">"</span><span class="p">);</span> <span class="p">};</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">refreshed</span><span class="dl">"</span><span class="p">);</span> <span class="k">if</span> <span class="p">(</span><span class="nx">refreshCheck</span><span class="p">)</span> <span class="p">{</span> <span class="nx">parseMarkdown</span><span class="p">();</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">parsing!</span><span class="dl">"</span><span class="p">);</span> <span class="p">}</span> <span class="p">},</span> <span class="p">[</span><span class="nx">refreshCheck</span><span class="p">]);</span> <span class="kd">const</span> <span class="nx">createMarkdownMarkup</span> <span class="o">=</span> <span class="p">()</span> <span class="o">=></span> <span class="p">({</span> <span class="na">__html</span><span class="p">:</span> <span class="nx">html</span><span class="p">,</span> <span class="p">});</span> <span class="kd">const</span> <span class="nx">handleTextArea</span> <span class="o">=</span> <span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span> <span class="nx">setMarkdown</span><span class="p">(</span><span class="nx">e</span><span class="p">.</span><span class="nx">currentTarget</span><span class="p">.</span><span class="nx">value</span><span class="p">);</span> <span class="nx">setRefreshCheck</span><span class="p">(</span><span class="kc">true</span><span class="p">);</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">need new markdown!</span><span class="dl">"</span><span class="p">);</span> <span class="p">};</span> <span class="k">return</span> <span class="p">(</span> <span class="p"><</span><span class="nt">div</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">className</span><span class="p">=</span><span class="s">"row"</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span><span class="p">></span> <span class="p"><</span><span class="nt">textarea</span> <span class="na">id</span><span class="p">=</span><span class="s">"greet-input"</span> <span class="na">onChange</span><span class="p">=</span><span class="si">{</span><span class="nx">handleTextArea</span><span class="si">}</span> <span class="p">/></span> <span class="p"><</span><span class="nt">button</span> <span class="na">type</span><span class="p">=</span><span class="s">"button"</span> <span class="na">onClick</span><span class="p">=</span><span class="si">{</span><span class="p">()</span> <span class="o">=></span> <span class="nx">greet</span><span class="p">()</span><span class="si">}</span><span class="p">></span> Convert to HTML <span class="p"></</span><span class="nt">button</span><span class="p">></span> <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">dangerouslySetInnerHTML</span><span class="p">=</span><span class="si">{</span><span class="nx">createMarkdownMarkup</span><span class="p">()</span><span class="si">}</span> <span class="p">/></span> <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p">);</span> <span class="p">}</span> <span class="k">export</span> <span class="k">default</span> <span class="nx">App</span><span class="p">;</span> </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>Here’s <a href="https://github.com/whoisryosuke/tauri-markdown-editor/commit/4a3b351509d1ec0b363950c778f4ea42f92a32f6">the commit on Github for reference.</a></p> <h1> <a name="the-world-is-your-markdown" href="#the-world-is-your-markdown"> </a> The world is your Markdown </h1> <p>Now you can parse Markdown using Rust, there’s lots of cool stuff you can do! From CLIs to UI apps, you can make tools that help people use Markdown. And ideally, this will run faster than most other implementations (like Electron apps) because it’s running in Rust <em>(unless you can write Markdown tooling in C or something)</em>.</p> <p>I hope this helped you understand some Rust fundamentals and new techniques to use down the line.</p> <p>As always, if you have any questions or make something using this guide, <a href="https://twitter.com/whoisryosuke">tag me on Twitter</a>. I’d love to help or see what cool stuff you’re hacking on.</p> <p><strong>You can find the complete code for this post in <a href="https://github.com/whoisryosuke/tauri-markdown-editor">my tauri-markdown-editor repo</a>.</strong></p> <p>Stay regular!<br> Ryo</p> -
Right-to-left languages rendering issue on the new reddit website and app
I was going to contribute to [comrak](https://github.com/kivikakk/comrak) the upstream project of snoomark (Reddit's Markdown renderer) to add bidirectional Markdown support, however, the spec isn't ready yet and a lot of things need to be clarified before considering using it in production.
Bidirectional text is not supported on Reddit. Related issue: https://github.com/kivikakk/comrak/issues/205
-
In Defence of the Boring Web
Jumping on the bandwagon of the guy that jumped on the bandwagon, because you mentioned potentially re-doing your own code at some point so might be interested: my personal site [0] looks very much like this (and is also written in Rust), but uses the comrak [1] crate to convert blog posts from Markdown to HTML on the fly before formatting with the header/footer theme. Using comrak lets you write in-line HTML (so you're not stuck with only Markdown syntax if you want to do something fancier like code blocks or ), and the whole site is <100 LoC, including route definitions.
It handled the one potential HN hug-of-death event I've had without issues while running on the smallest DO droplet offered, and has basically been issue-free since I got it up and running.
[0] https://github.com/quietlychris/site
[1] https://github.com/kivikakk/comrak
-
Meta Programming
I chose to use comrak. It is one of the most developed and well known markdown to html crates and is very easy to use. The main logic of my code is very simple and is done as such
Docusaurus
-
Alternatives to Docusaurus for product documentation
Docusaurus is a popular open-source documentation tool primarily designed for product documentation and other technical documentation needs. It was first released in 2017 by Facebook Open Source (now Meta Open Source). Just recently, Docsaurus version 3.0 was released.
-
Docusaurus doesn't recognize brackets {} on the markdown files
// @ts-check // `@type` JSDoc annotations allow editor autocompletion and type checking // (when paired with `@ts-check`). // There are various equivalent ways to declare your Docusaurus config. // See: https://docusaurus.io/docs/api/docusaurus-config import { themes as prismThemes } from "prism-react-renderer"; /** @type {import('@docusaurus/types').Config} */ const config = { title: "My Site", tagline: "Dinosaurs are cool", url: "https://your-docusaurus-test-site.com", baseUrl: "/", onBrokenLinks: "throw", onBrokenMarkdownLinks: "warn", favicon: "img/favicon.ico", organizationName: "facebook", // Usually your GitHub org/user name. projectName: "docusaurus", // Usually your repo name. presets: [ [ "docusaurus-preset-openapi", /** @type {import('docusaurus-preset-openapi').Options} */ ({ docs: { sidebarPath: require.resolve("./sidebars.js"), // Please change this to your repo. editUrl: "https://github.com/facebook/docusaurus/tree/main/packages/create-docusaurus/templates/shared/", }, blog: { showReadingTime: true, // Please change this to your repo. editUrl: "https://github.com/facebook/docusaurus/tree/main/packages/create-docusaurus/templates/shared/", }, theme: { customCss: require.resolve("./src/css/custom.css"), }, }), ], ], themeConfig: /** @type {import('docusaurus-preset-openapi').ThemeConfig} */ ({ navbar: { title: "My Site", logo: { alt: "My Site Logo", src: "img/logo.svg", }, items: [ { type: "doc", docId: "intro", position: "left", label: "Tutorial", }, { to: "/api", label: "API", position: "left" }, { to: "/blog", label: "Blog", position: "left" }, { href: "https://github.com/facebook/docusaurus", label: "GitHub", position: "right", }, ], }, footer: { style: "dark", links: [ { title: "Docs", items: [ { label: "Tutorial", to: "/docs/intro", }, ], }, { title: "Community", items: [ { label: "Stack Overflow", href: "https://stackoverflow.com/questions/tagged/docusaurus", }, { label: "Discord", href: "https://discordapp.com/invite/docusaurus", }, { label: "Twitter", href: "https://twitter.com/docusaurus", }, ], }, { title: "More", items: [ { label: "Blog", to: "/blog", }, { label: "GitHub", href: "https://github.com/facebook/docusaurus", }, ], }, ], copyright: `Copyright © ${new Date().getFullYear()} My Project, Inc. Built with Docusaurus.`, }, prism: { theme: prismThemes.github, darkTheme: prismThemes.dracula, }, }), }; export default config;
- Looking for open source documentation generator
-
Show HN: A Python-based static site generator using Jinja templates
Facebook's React/Markdown SSG docusaurus does those things: https://docusaurus.io/
Though you may have to use a plugin for responsive images: https://docusaurus.io/docs/api/plugins/@docusaurus/plugin-id...
- Craft Your GitHub Profile Page in 60 Seconds with Zero Code, Absolutely Free
-
Top 5 Open-Source Documentation Development Platforms of 2024
Docusaurus is an open-source static site generator built on React and has emerged as a popular tool for developing and maintaining product documentation. Its ease of use, extensive features, and robust community support make it a compelling choice for many organizations.
-
No CMS? Writing Our Blog in React
Wondering why Docusaurus (https://docusaurus.io) did not match their needs. Works perfectly fine as a blogging engine for our tech blog.
-
Best Software Documentation Tools
This is developed by Meta. You can create really nice-looking documentation websites super fast.
-
Can Git or any other VCS be used as a database instead of SQL/NoSQL ones? Have you ever seen such a thing?
Docusaurus, a documentation tool by Facebook, hosts a showcase of other websites that use Docusaurus on their Homepage. The list of websites of this showcase is a typescript files that is maintained by Docusaurus devs, and that you can add your website to through PR: https://github.com/facebook/docusaurus/blob/main/website/src/data/users.tsx
-
Community project: PreventRansomware.io
Fix "Edit this page" links at the bottom of each doc (Problem with the Docusaurus build I guess)
What are some alternatives?
markdown-rs - CommonMark compliant markdown parser in Rust with ASTs and extensions
nextra - Simple, powerful and flexible site generation framework with everything you love from Next.js.
no-style-please - A (nearly) no-CSS, fast, minimalist Jekyll theme.
storybook - Storybook is a frontend workshop for building UI components and pages in isolation. Made for UI development, testing, and documentation.
tauri-markdown-editor
oauth2-proxy - A reverse proxy that provides authentication with Google, Azure, OpenID Connect and many more identity providers.
mdcat - cat for markdown
JSDoc - An API documentation generator for JavaScript.
mdcat - cat for markdown
VuePress - 📝 Minimalistic Vue-powered static site generator
mini_markdown - Dependency free markdown
MkDocs - Project documentation with Markdown.