<rss xmlns:source="http://source.scripting.com/" version="2.0">
  <channel>
    <title>Hey Loura</title>
    <link>https://heyloura-test.micro.blog/</link>
    <description></description>
    
    <language>en</language>
    
    <lastBuildDate>Tue, 21 Apr 2026 12:33:51 -0400</lastBuildDate>
    <item>
      <title></title>
      <link>https://heyloura-test.micro.blog/2026/03/05/okay-just-making-sure-this.html</link>
      <pubDate>Thu, 05 Mar 2026 16:34:15 -0400</pubDate>
      
      <guid>http://heyloura-test.micro.blog/2026/03/05/okay-just-making-sure-this.html</guid>
      <description>&lt;p&gt;Okay, just making sure this post editor works&amp;hellip;.&lt;/p&gt;
</description>
      <source:markdown>Okay, just making sure this post editor works....
</source:markdown>
    </item>
    
    <item>
      <title></title>
      <link>https://heyloura-test.micro.blog/2026/03/01/read-as-text-one-of.html</link>
      <pubDate>Sun, 01 Mar 2026 17:05:44 -0400</pubDate>
      
      <guid>http://heyloura-test.micro.blog/2026/03/01/read-as-text-one-of.html</guid>
      <description>&lt;map name=&#34;map1&#34;&gt;
  &lt;area shape=&#34;rect&#34;
        coords=&#34;200,30,900,175&#34;
        href=&#34;https://heyloura-test.micro.blog/2026/03/01/read-as-text-one-of.html&#34;
        alt=&#34;Handwriting a blog – March 1st 2026&#34; /&gt;
  &lt;area shape=&#34;rect&#34;
        coords=&#34;175,190,880,880&#34;
        href=&#34;https://web.archive.org/web/20250227184810/https://www.handwritten.blog&#34;
        alt=&#34;Link reference [1]: handwritten.blog on the Internet Archive&#34; /&gt;
  &lt;area shape=&#34;rect&#34;
        coords=&#34;940,180,990,880&#34;
        href=&#34;https://web.archive.org/web/20250227184810/https://www.handwritten.blog&#34;
        alt=&#34;Link reference [1]: handwritten.blog on the Internet Archive&#34; /&gt;
&lt;/map&gt;
&lt;div class=&#34;handwritten-post-wrap&#34;&gt;
&lt;img usemap=&#34;#map1&#34; src=&#34;https://heyloura-test.micro.blog/uploads/2026/formatconvert-20260301-175638-8978.webp&#34; class=&#34;overflow handwritten-post&#34; alt=&#34;Handwritten blog post — transcript below. Decorative doodles: Left margin: a vertical trail of blue teardrop/raindrop shapes, and a blue swirl/spiral shape Inline after mind wander.: a small drawing of a swimmer with a red cap and goggles, with blue splashes Bottom of page: a row of colorful triangular bunting/pennant flags in red, yellow, and blue&#34;&gt;
&lt;/div&gt;
&lt;details&gt;
  &lt;summary&gt;Read as text&lt;/summary&gt;
  &lt;p&gt;One of the nice things about going to my kids swim meet is that I have time to let my mind wander.&lt;/p&gt;
&lt;p&gt;A few years ago, I came across a website called &#34;Handwritten. blog.&#34; Sadly, it is no longer hosted but you can still see some of it on the internet archive [1].&lt;/p&gt;
&lt;/details&gt;
&lt;p&gt;&lt;strong&gt;Links&lt;/strong&gt;&lt;br&gt;
[1] &lt;a href=&#34;https://web.archive.org/web/20250227184810/https://www.handwritten.blog&#34;&gt;Internet Archive&lt;/a&gt;
&lt;/p&gt;
</description>
      <source:markdown>&lt;map name=&#34;map1&#34;&gt;
  &lt;area shape=&#34;rect&#34;
        coords=&#34;200,30,900,175&#34;
        href=&#34;https://heyloura-test.micro.blog/2026/03/01/read-as-text-one-of.html&#34;
        alt=&#34;Handwriting a blog – March 1st 2026&#34; /&gt;
  &lt;area shape=&#34;rect&#34;
        coords=&#34;175,190,880,880&#34;
        href=&#34;https://web.archive.org/web/20250227184810/https://www.handwritten.blog&#34;
        alt=&#34;Link reference [1]: handwritten.blog on the Internet Archive&#34; /&gt;
  &lt;area shape=&#34;rect&#34;
        coords=&#34;940,180,990,880&#34;
        href=&#34;https://web.archive.org/web/20250227184810/https://www.handwritten.blog&#34;
        alt=&#34;Link reference [1]: handwritten.blog on the Internet Archive&#34; /&gt;
&lt;/map&gt;
&lt;div class=&#34;handwritten-post-wrap&#34;&gt;
&lt;img usemap=&#34;#map1&#34; src=&#34;https://heyloura-test.micro.blog/uploads/2026/formatconvert-20260301-175638-8978.webp&#34; class=&#34;overflow handwritten-post&#34; alt=&#34;Handwritten blog post — transcript below. Decorative doodles: Left margin: a vertical trail of blue teardrop/raindrop shapes, and a blue swirl/spiral shape Inline after mind wander.: a small drawing of a swimmer with a red cap and goggles, with blue splashes Bottom of page: a row of colorful triangular bunting/pennant flags in red, yellow, and blue&#34;&gt;
&lt;/div&gt;
&lt;details&gt;
  &lt;summary&gt;Read as text&lt;/summary&gt;
  &lt;p&gt;One of the nice things about going to my kids swim meet is that I have time to let my mind wander.&lt;/p&gt;
&lt;p&gt;A few years ago, I came across a website called &#34;Handwritten. blog.&#34; Sadly, it is no longer hosted but you can still see some of it on the internet archive [1].&lt;/p&gt;
&lt;/details&gt;
&lt;p&gt;&lt;strong&gt;Links&lt;/strong&gt;&lt;br&gt;
[1] &lt;a href=&#34;https://web.archive.org/web/20250227184810/https://www.handwritten.blog&#34;&gt;Internet Archive&lt;/a&gt;
&lt;/p&gt;
</source:markdown>
    </item>
    
    <item>
      <title></title>
      <link>https://heyloura-test.micro.blog/2025/09/25/just-seeing-how-this-works.html</link>
      <pubDate>Thu, 25 Sep 2025 17:26:26 -0400</pubDate>
      
      <guid>http://heyloura-test.micro.blog/2025/09/25/just-seeing-how-this-works.html</guid>
      <description>&lt;h1 id=&#34;just-seeing-how-this-works&#34;&gt;Just seeing how this works&amp;hellip;&lt;/h1&gt;
&lt;iframe onload=&#34;disappear(this)&#34; src=&#34;https://code.heyloura.com/color-contrast-on-black-white-backgrounds.html&#34; style=&#34;width:100%;height:600px;&#34;&gt;&lt;/iframe&gt;
</description>
      <source:markdown># Just seeing how this works...


&lt;iframe onload=&#34;disappear(this)&#34; src=&#34;https://code.heyloura.com/color-contrast-on-black-white-backgrounds.html&#34; style=&#34;width:100%;height:600px;&#34;&gt;&lt;/iframe&gt;
</source:markdown>
    </item>
    
    <item>
      <title></title>
      <link>https://heyloura-test.micro.blog/2025/08/27/hello-categories.html</link>
      <pubDate>Wed, 27 Aug 2025 11:15:26 -0400</pubDate>
      
      <guid>http://heyloura-test.micro.blog/2025/08/27/hello-categories.html</guid>
      <description>&lt;p&gt;hello categories&lt;/p&gt;
</description>
      <source:markdown>hello categories
</source:markdown>
    </item>
    
    <item>
      <title></title>
      <link>https://heyloura-test.micro.blog/2025/08/27/another-category-test.html</link>
      <pubDate>Wed, 27 Aug 2025 11:13:17 -0400</pubDate>
      
      <guid>http://heyloura-test.micro.blog/2025/08/27/another-category-test.html</guid>
      <description>&lt;p&gt;Another category test&amp;hellip;&lt;/p&gt;
</description>
      <source:markdown>Another category test...
</source:markdown>
    </item>
    
    <item>
      <title></title>
      <link>https://heyloura-test.micro.blog/2025/08/27/hmmmmmmm.html</link>
      <pubDate>Wed, 27 Aug 2025 11:06:59 -0400</pubDate>
      
      <guid>http://heyloura-test.micro.blog/2025/08/27/hmmmmmmm.html</guid>
      <description>&lt;p&gt;Hmmmmmmm&lt;/p&gt;
</description>
      <source:markdown>Hmmmmmmm
</source:markdown>
    </item>
    
    <item>
      <title></title>
      <link>https://heyloura-test.micro.blog/2025/08/27/does-this-post-with-the.html</link>
      <pubDate>Wed, 27 Aug 2025 11:05:18 -0400</pubDate>
      
      <guid>http://heyloura-test.micro.blog/2025/08/27/does-this-post-with-the.html</guid>
      <description>&lt;p&gt;Does this post with the title?&lt;/p&gt;
</description>
      <source:markdown>Does this post with the title?
</source:markdown>
    </item>
    
    <item>
      <title></title>
      <link>https://heyloura-test.micro.blog/2025/08/27/setting-a-title-and-a.html</link>
      <pubDate>Wed, 27 Aug 2025 10:53:41 -0400</pubDate>
      
      <guid>http://heyloura-test.micro.blog/2025/08/27/setting-a-title-and-a.html</guid>
      <description>&lt;p&gt;setting a title and a summary&lt;/p&gt;
</description>
      <source:markdown>setting a title and a summary
</source:markdown>
    </item>
    
    <item>
      <title></title>
      <link>https://heyloura-test.micro.blog/2025/08/27/setting-a-category.html</link>
      <pubDate>Wed, 27 Aug 2025 10:53:14 -0400</pubDate>
      
      <guid>http://heyloura-test.micro.blog/2025/08/27/setting-a-category.html</guid>
      <description>&lt;p&gt;Setting a category&lt;/p&gt;
</description>
      <source:markdown>Setting a category
</source:markdown>
    </item>
    
    <item>
      <title></title>
      <link>https://heyloura-test.micro.blog/2025/08/27/testing-a-post-where-i.html</link>
      <pubDate>Wed, 27 Aug 2025 10:51:32 -0400</pubDate>
      
      <guid>http://heyloura-test.micro.blog/2025/08/27/testing-a-post-where-i.html</guid>
      <description>&lt;p&gt;Testing a post where I use the checkbox to include an upload&amp;hellip;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&#34;polaroid&#34;&gt;
  &lt;span class=&#34;polaroid-frame&#34;&gt;
    &lt;img src=&#34;https://heyloura-test.micro.blog/uploads/2025/logo-ai-lillihub-sm.png&#34; alt=&#34;A cheerful green frog is peeking over a large lily pad with a yellow flower on it.&#34; loading=&#34;lazy&#34; decoding=&#34;async&#34;&gt;
  &lt;/span&gt;
  &lt;svg class=&#34;photo-dagger&#34; viewBox=&#34;0 0 40 40&#34; aria-hidden=&#34;true&#34; focusable=&#34;false&#34;&gt;
  
  &lt;g transform=&#34;rotate(-8 20 20)&#34;&gt;
    
    &lt;circle cx=&#34;20&#34; cy=&#34;6&#34; r=&#34;2.4&#34; fill=&#34;#d4a017&#34; stroke=&#34;#5a3e08&#34; stroke-width=&#34;0.4&#34;/&gt;
    &lt;circle cx=&#34;20&#34; cy=&#34;6&#34; r=&#34;0.8&#34; fill=&#34;#b02a2a&#34;/&gt;
    
    &lt;rect x=&#34;18&#34; y=&#34;8.5&#34; width=&#34;4&#34; height=&#34;7&#34; fill=&#34;#6a3f18&#34; stroke=&#34;#3a2010&#34; stroke-width=&#34;0.4&#34;/&gt;
    &lt;line x1=&#34;18&#34; y1=&#34;10.5&#34; x2=&#34;22&#34; y2=&#34;10.5&#34; stroke=&#34;#3a2010&#34; stroke-width=&#34;0.3&#34;/&gt;
    &lt;line x1=&#34;18&#34; y1=&#34;12&#34; x2=&#34;22&#34; y2=&#34;12&#34; stroke=&#34;#3a2010&#34; stroke-width=&#34;0.3&#34;/&gt;
    &lt;line x1=&#34;18&#34; y1=&#34;13.5&#34; x2=&#34;22&#34; y2=&#34;13.5&#34; stroke=&#34;#3a2010&#34; stroke-width=&#34;0.3&#34;/&gt;
    
    &lt;rect x=&#34;14&#34; y=&#34;15.5&#34; width=&#34;12&#34; height=&#34;2&#34; rx=&#34;0.5&#34; fill=&#34;#a37a1a&#34; stroke=&#34;#5a3e08&#34; stroke-width=&#34;0.4&#34;/&gt;
    
    &lt;polygon points=&#34;18.5,17.5 21.5,17.5 21,34 20,36 19,34&#34; fill=&#34;#d8d8e0&#34; stroke=&#34;#3a3a45&#34; stroke-width=&#34;0.4&#34;/&gt;
    &lt;line x1=&#34;20&#34; y1=&#34;18.5&#34; x2=&#34;20&#34; y2=&#34;34&#34; stroke=&#34;#9a9aa6&#34; stroke-width=&#34;0.35&#34;/&gt;
  &lt;/g&gt;
&lt;/svg&gt;
&lt;/figure&gt;
&lt;/p&gt;
</description>
      <source:markdown>Testing a post where I use the checkbox to include an upload...

![A cheerful green frog is peeking over a large lily pad with a yellow flower on it.](https://heyloura-test.micro.blog/uploads/2025/logo-ai-lillihub-sm.png)

</source:markdown>
    </item>
    
    <item>
      <title></title>
      <link>https://heyloura-test.micro.blog/2025/08/27/testing-a-plain-post-that.html</link>
      <pubDate>Wed, 27 Aug 2025 10:50:52 -0400</pubDate>
      
      <guid>http://heyloura-test.micro.blog/2025/08/27/testing-a-plain-post-that.html</guid>
      <description>&lt;p&gt;Testing a plain post that I preview first&lt;/p&gt;
</description>
      <source:markdown>Testing a plain post that I preview first
</source:markdown>
    </item>
    
    <item>
      <title></title>
      <link>https://heyloura-test.micro.blog/2025/08/27/testing-a-plain-post.html</link>
      <pubDate>Wed, 27 Aug 2025 10:50:30 -0400</pubDate>
      
      <guid>http://heyloura-test.micro.blog/2025/08/27/testing-a-plain-post.html</guid>
      <description>&lt;p&gt;Testing a plain post&lt;/p&gt;
</description>
      <source:markdown>Testing a plain post
</source:markdown>
    </item>
    
    <item>
      <title>With a title</title>
      <link>https://heyloura-test.micro.blog/2025/02/11/with-a-title.html</link>
      <pubDate>Tue, 11 Feb 2025 13:18:21 -0400</pubDate>
      
      <guid>http://heyloura-test.micro.blog/2025/02/11/with-a-title.html</guid>
      <description>&lt;p&gt;Making sure posting still works with the auto save&amp;hellip;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://heyloura-test.micro.blog/2023/09/26/more-tests.html&#34;&gt;heyloura-test.micro.blog/2023/09/2&amp;hellip;&lt;/a&gt;&lt;/p&gt;
</description>
      <source:markdown>Making sure posting still works with the auto save....

[heyloura-test.micro.blog/2023/09/2...](https://heyloura-test.micro.blog/2023/09/26/more-tests.html)
</source:markdown>
    </item>
    
    <item>
      <title></title>
      <link>https://heyloura-test.micro.blog/2025/02/11/making-sure-posting-still-works.html</link>
      <pubDate>Tue, 11 Feb 2025 13:17:33 -0400</pubDate>
      
      <guid>http://heyloura-test.micro.blog/2025/02/11/making-sure-posting-still-works.html</guid>
      <description>&lt;p&gt;Making sure posting still works with the auto save&amp;hellip;.&lt;/p&gt;
</description>
      <source:markdown>Making sure posting still works with the auto save....
</source:markdown>
    </item>
    
    <item>
      <title></title>
      <link>https://heyloura-test.micro.blog/2025/01/30/i-am-a-long-time.html</link>
      <pubDate>Thu, 30 Jan 2025 12:24:45 -0400</pubDate>
      
      <guid>http://heyloura-test.micro.blog/2025/01/30/i-am-a-long-time.html</guid>
      <description>&lt;blockquote cite=&#34;https://frankmcpherson.blog/2025/01/30/i-am-a-long-time.html&#34;&gt;I am a long time user of micro.blog and one of the reasons why is the ability to use alternative applications with the service. For example, I currently use lillihub.com as the my preferred web application for micro.blog and I want to give a shout out to Loura’s fantastic work on the application. She conti... &lt;a href=&#34;https://frankmcpherson.blog/2025/01/30/i-am-a-long-time.html&#34;&gt;frankmcpherson.blog&lt;/a&gt;&lt;footer&gt;- frankm, &lt;cite&gt;&lt;a href=&#34;https://frankmcpherson.blog/2025/01/30/i-am-a-long-time.html&#34; class=&#34;u-in-reply-to&#34;&gt;https://frankmcpherson.blog/2025/01/30/i-am-a-long-time.html&lt;/a&gt;&lt;/cite&gt;&lt;/footer&gt;&lt;/blockquote&gt;
</description>
      <source:markdown>&lt;blockquote cite=&#34;https://frankmcpherson.blog/2025/01/30/i-am-a-long-time.html&#34;&gt;I am a long time user of micro.blog and one of the reasons why is the ability to use alternative applications with the service. For example, I currently use lillihub.com as the my preferred web application for micro.blog and I want to give a shout out to Loura’s fantastic work on the application. She conti... &lt;a href=&#34;https://frankmcpherson.blog/2025/01/30/i-am-a-long-time.html&#34;&gt;frankmcpherson.blog&lt;/a&gt;&lt;footer&gt;- frankm, &lt;cite&gt;&lt;a href=&#34;https://frankmcpherson.blog/2025/01/30/i-am-a-long-time.html&#34; class=&#34;u-in-reply-to&#34;&gt;https://frankmcpherson.blog/2025/01/30/i-am-a-long-time.html&lt;/a&gt;&lt;/cite&gt;&lt;/footer&gt;&lt;/blockquote&gt;
</source:markdown>
    </item>
    
    <item>
      <title>Testing a title post from Lillihub</title>
      <link>https://heyloura-test.micro.blog/2024/12/27/testing-a-title-post-from.html</link>
      <pubDate>Fri, 27 Dec 2024 22:07:40 -0400</pubDate>
      
      <guid>http://heyloura-test.micro.blog/2024/12/27/testing-a-title-post-from.html</guid>
      <description>&lt;p&gt;Because testing is the &lt;em&gt;besting&lt;/em&gt;.&lt;/p&gt;
</description>
      <source:markdown> Because testing is the *besting*.
</source:markdown>
    </item>
    
    <item>
      <title>Here is a titled post, testing in production</title>
      <link>https://heyloura-test.micro.blog/2024/11/26/095430.html</link>
      <pubDate>Tue, 26 Nov 2024 10:54:30 -0400</pubDate>
      
      <guid>http://heyloura-test.micro.blog/2024/11/26/095430.html</guid>
      <description>&lt;p&gt;Hello people!&lt;/p&gt;
</description>
      <source:markdown>Hello people!
</source:markdown>
    </item>
    
    <item>
      <title>Here is a titled post</title>
      <link>https://heyloura-test.micro.blog/2024/11/26/here-is-a.html</link>
      <pubDate>Tue, 26 Nov 2024 10:47:21 -0400</pubDate>
      
      <guid>http://heyloura-test.micro.blog/2024/11/26/here-is-a.html</guid>
      <description>&lt;p&gt;Do you post with this title I wonder?&lt;/p&gt;
&lt;p&gt;Awesome!&lt;/p&gt;
&lt;p&gt;or mayber&lt;/p&gt;
&lt;p&gt;Not Awesome!&lt;/p&gt;
&lt;p&gt;depending on how this goes&amp;hellip;..&lt;/p&gt;
</description>
      <source:markdown>Do you post with this title I wonder?

Awesome!

or mayber

Not Awesome!

depending on how this goes.....
</source:markdown>
    </item>
    
    <item>
      <title></title>
      <link>https://heyloura-test.micro.blog/2024/09/25/hopefully-this-makes.html</link>
      <pubDate>Wed, 25 Sep 2024 16:55:24 -0400</pubDate>
      
      <guid>http://heyloura-test.micro.blog/2024/09/25/hopefully-this-makes.html</guid>
      <description>&lt;p&gt;Hopefully this makes it to my test blog&lt;/p&gt;
</description>
      <source:markdown>Hopefully this makes it to my test blog
</source:markdown>
    </item>
    
    <item>
      <title>Coding Adventure: Micro.blog Bookmarks &amp; Highlights</title>
      <link>https://heyloura-test.micro.blog/2024/08/14/coding-adventure-microblog.html</link>
      <pubDate>Wed, 14 Aug 2024 08:32:55 -0400</pubDate>
      
      <guid>http://heyloura-test.micro.blog/2024/08/14/coding-adventure-microblog.html</guid>
      <description>&lt;h1 id=&#34;coding-adventure-microblog-bookmarks--highlights&#34;&gt;Coding Adventure: Micro.blog Bookmarks &amp;amp; Highlights&lt;/h1&gt;
&lt;p&gt;Life has been way too much lately and most of my hobbies were put on pause while I dealt with keeping up with the unexpected. One of those that was dropped was writing down weekly notes that focused on my development side projects (and honestly, even coding side-projects took a nose-dive). My last post was on March 1st 😱&lt;/p&gt;
&lt;p&gt;So I&amp;rsquo;m doing a little reframing to make it feel a bit easier to write, thus the &amp;ldquo;Coding Adventure&amp;rdquo;. I want to keep these posts more along the line of &amp;ldquo;Hey, here&amp;rsquo;s something fun I played around with and it might still be mostly broken 🤪&amp;rdquo;&lt;/p&gt;
&lt;h2 id=&#34;background&#34;&gt;Background&lt;/h2&gt;
&lt;p&gt;I use Micro.blog, I know&amp;hellip;. you&amp;rsquo;re shocked 😝 and one of the things I love, is how open they are with their API. It&amp;rsquo;s a fun playground of potential side-projects just waiting to be explored. One piece that I&amp;rsquo;ve dabbled with is the bookmarks feature. Basically you can save URLs as bookmarks and if you are a premium member, organize them with tags and add highlights. Manton, the creator of Micro.blog, rolled out a feature to bookmarks including having AI summarize the link (only if you&amp;rsquo;ve enabled AI on your account and, important to me, if the robots.txt of the bookmarked link allows it).&lt;/p&gt;
&lt;p&gt;But it&amp;rsquo;s not a feature I use all that much and I wanted to see if I could change that with a few of my own touches.&lt;/p&gt;
&lt;h2 id=&#34;getting-started&#34;&gt;Getting Started&lt;/h2&gt;
&lt;p&gt;Like most of my recent side-projects, I&amp;rsquo;ve been using Deno. Its quick to get started with a Github repo and they have hard free tier limits so I feel safe playing around.&lt;/p&gt;
&lt;p&gt;The first thing I needed was a valid authorization token from micro.blog. I got this using indieauth and saving the resulting token in a http-only cookie. That way I&amp;rsquo;ll have the token with each page request. To make things easier in this proof of concept, I hard-coded values inside the server generated HTML form I was using. For instance the form action, the scope and response type. On the server I generated a random string for the state (the &lt;code&gt;${}&lt;/code&gt; notation is a &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals&#34;&gt;JavaScript template literal&lt;/a&gt;) and I also included the state as a session cookie when the page is rendered.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;form&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;action&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://micro.blog/indieauth/auth&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;method&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;get&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;input&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;type&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;url&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;placeholder&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://username.micro.blog&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;me&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;input&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;type&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;hidden&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;client_id&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://my-demo.com&amp;#34;&lt;/span&gt;/&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;input&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;type&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;hidden&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;redirect_uri&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;https://my-demo.com/auth&amp;#34;&lt;/span&gt;/&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;input&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;type&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;hidden&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;state&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;${uuid}&amp;#34;&lt;/span&gt;/&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;input&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;type&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;hidden&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;scope&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;create&amp;#34;&lt;/span&gt;/&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;input&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;type&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;hidden&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;name&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;response_type&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;code&amp;#34;&lt;/span&gt;/&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#f92672&#34;&gt;button&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;type&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;submit&amp;#34;&lt;/span&gt;&amp;gt;Sign In&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;button&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#f92672&#34;&gt;form&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Submitting this form creates a get request to Mico.blog&amp;rsquo;s indieauth endpoint which redirects me to approve the login. Once I approve it, it sends a request back to my &lt;code&gt;/auth&lt;/code&gt; endpoint with a code and state in the query string of a URL. I check that the state matches the state inside the cookie and then I make a POST request to Micro.blog&amp;rsquo;s token endpoint, &lt;code&gt;https://micro.blog/indieauth/token&lt;/code&gt;, using the code. If everything was wired up correctly I get back my lovey authorization token. Here is a code snippet of the token portion:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;formBody&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;URLSearchParams&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;formBody&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;append&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;code&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;code&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;formBody&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;append&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;client_id&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;https://my-demo.com&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;formBody&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;append&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;grant_type&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;authorization_code&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// request access_token
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fetching&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;await&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fetch&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;https://micro.blog/indieauth/token&amp;#39;&lt;/span&gt;, {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;method&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;POST&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;body&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;formBody&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;toString&lt;/span&gt;(),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;headers&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Content-Type&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;application/x-www-form-urlencoded; charset=utf-8&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Accept&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;application/json&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;});    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;response&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;await&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fetching&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;json&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;a-fetch-quest-getting-my-bookmarks&#34;&gt;A Fetch Quest: Getting My Bookmarks&lt;/h2&gt;
&lt;p&gt;Okay, let&amp;rsquo;s get some bookmarks. Micro.blog has &lt;a href=&#34;https://help.micro.blog/t/json-api-bookmarks/110&#34;&gt;documentation on its bookmarks&lt;/a&gt; endpoints in its help center, so that&amp;rsquo;s the first place to go diving. This was all familiar to me since I put bookmarks in my side project Lillihub a while back.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fetching&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;await&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fetch&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;`https://micro.blog/posts/bookmarks`&lt;/span&gt;, { 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;method&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;GET&amp;#34;&lt;/span&gt;, 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;headers&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; { &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Authorization&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Bearer &amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;access_token&lt;/span&gt; } 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;let&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;results&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;await&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fetching&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;json&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This returns twenty-five of my most recent bookmarks. Nice. There was some trial and error but I figured out that you can use query parameter &lt;code&gt;before_id&lt;/code&gt; to enable getting more bookmarks. For example, if the last bookmark id was &lt;code&gt;123123&lt;/code&gt; then I could call &lt;code&gt;https://micro.blog/posts/bookmarks?before_id=123123&lt;/code&gt; and get the twenty-five after that last bookmark.&lt;/p&gt;
&lt;p&gt;If you have tags you can get the by adding the &lt;code&gt;tag&lt;/code&gt; with the name of the tag, like this: &lt;code&gt;https://micro.blog/posts/bookmarks?tag=code&lt;/code&gt; and you will get the first 25 bookmarks with that tag.&lt;/p&gt;
&lt;h2 id=&#34;lost-in-the-woods-bookmarks-and-what-i-couldnt-figure-out&#34;&gt;Lost in the Woods: Bookmarks and What I Couldn&amp;rsquo;t Figure Out&lt;/h2&gt;
&lt;p&gt;What I couldn&amp;rsquo;t figure out is how to go backwards. The Micro.blog post API supports a &lt;code&gt;since_id&lt;/code&gt; parameter and a &lt;code&gt;count&lt;/code&gt; parameter allowing for a full paging experience. Those don&amp;rsquo;t seem to work on bookmarks endpoint. I tried grabbing the bookmarks via the micropub endpoint but I had no luck there, just getting the posts on my blog.&lt;/p&gt;
&lt;p&gt;Nor could I figure out how to search for a bookmark. I figured this would be handy with the AI summarization or even to search part of the bookmark title. There is limited support for searching on the Micro.blog website but it mixes results together from the other related sections of links and highlights.&lt;/p&gt;
&lt;p&gt;So two paths then. Load all the bookmarks on the HTML page and then use JavaScript to search OR load all the bookmarks on the server, search it, and then return the results.&lt;/p&gt;
&lt;p&gt;Since I don&amp;rsquo;t have thousands of bookmarks&amp;hellip; yet. I&amp;rsquo;m going with the quick and easy client side solution.&lt;/p&gt;
&lt;h2 id=&#34;search-for-treasure-enabling-search&#34;&gt;Search For Treasure: Enabling Search&lt;/h2&gt;
&lt;p&gt;The while loop was my weapon of choice for this part of the adventure. I iterated calling the bookmarks API and gathered the results. And I kept doing that until the request returned less than 25 results&amp;hellip; signaling the end.&lt;/p&gt;
&lt;p&gt;Then I put those bookmarks in a table with some styling and&amp;hellip;.&lt;/p&gt;
&lt;img src=&#34;https://heyloura.com/uploads/2024/screenshot-2024-08-13-114248.png&#34;&gt;
&lt;p&gt;Nice.&lt;/p&gt;
&lt;p&gt;The search box runs a little bit of JavaScript that hides table rows if they don&amp;rsquo;t contain the search term. Clicking on a tag, whether at the top or on a bookmark fills the textbox and retriggers the search.&lt;/p&gt;
&lt;p&gt;One strange thing with the Micro.blog bookmark API is that it doesn&amp;rsquo;t have separate properties for things like title or reader link. It jumbles them all together in the content_html field.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;...,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;content_html&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;&amp;lt;p&amp;gt;Homemade Oreos &amp;lt;a href=&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;https&lt;/span&gt;:&lt;span style=&#34;color:#75715e&#34;&gt;//sallysbakingaddiction.com/homemade-oreos/&amp;#34;&amp;gt;sallysbakingaddiction.com&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;p class=&amp;#34;post_archived_links&amp;#34;&amp;gt;Reader: &amp;lt;a href=&amp;#34;https://micro.blog/bookmarks/6888713\&amp;#34;&amp;gt;sallysbakingaddiction.com&amp;lt;/a&amp;gt; &amp;lt;/p&amp;gt;&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So I&amp;rsquo;m displaying the bookmark title as what&amp;rsquo;s in the first paragraph tag and removing everything else.&lt;/p&gt;
&lt;h2 id=&#34;side-quest-adding-bookmarks&#34;&gt;Side Quest: Adding Bookmarks&lt;/h2&gt;
&lt;p&gt;This was actually something &lt;a href=&#34;https://heyloura.com/2023/10/16/dev-diary-week.html#bookmarks&#34;&gt;I figured out a while back&lt;/a&gt;, but I figured I&amp;rsquo;d actually list the code sample here for a more complete picture.&lt;/p&gt;
&lt;p&gt;First you need to create the bookmark using micropub:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;formBody&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;URLSearchParams&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;formBody&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;append&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;h&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;entry&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;formBody&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;append&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;bookmark-of&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;url&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;posting&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;await&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fetch&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;`https://micro.blog/micropub`&lt;/span&gt;, {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;method&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;POST&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;body&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;formBody&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;toString&lt;/span&gt;(),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;headers&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Content-Type&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;application/x-www-form-urlencoded; charset=utf-8&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Authorization&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Bearer &amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;accessTokenValue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;and then you need to add the tags using the Micro.blog bookmarks API.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Get the bookmarks&lt;/li&gt;
&lt;li&gt;The new one should be first one returned, grab it&amp;rsquo;s &lt;code&gt;id&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Add the tags (comma-separated list)&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;formBodyTags&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;URLSearchParams&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;formBodyTags&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;append&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;tags&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;tags&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;postingTags&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;await&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fetch&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;`https://micro.blog/posts/bookmarks/&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;bookmark&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;`&lt;/span&gt;, {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;method&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;POST&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;body&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;formBodyTags&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;toString&lt;/span&gt;(),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;headers&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Content-Type&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;application/x-www-form-urlencoded; charset=utf-8&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Authorization&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Bearer &amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;accessTokenValue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;responseTags&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;await&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;postingTags&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;text&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;exploring-the-dungeon-highlights&#34;&gt;Exploring the Dungeon: Highlights&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s circle back to that reader link in the content_html property. Micro.blog premium users can make highlights on bookmarks, assuming the website&amp;rsquo;s robot.txt doesn&amp;rsquo;t prohibit it being crawled and the content isn&amp;rsquo;t dynamic. A copy of the content is provided along with an interface to make highlights. &lt;a href=&#34;https://youtu.be/nwekF1H87WM?t=173&#34;&gt;This video at the 2:52 mark&lt;/a&gt; by &lt;a href=&#34;https://micro.blog/manton&#34;&gt;@manton&lt;/a&gt; shows the functionality I&amp;rsquo;m talking about.&lt;/p&gt;
&lt;p&gt;First thing I wanted to change, I want the number of highlights displayed in-line with my bookmarks. So I fire off the request to get my highlights and the first problem becomes apparent. There is no &lt;code&gt;id&lt;/code&gt; link between a bookmark and a highlight, or at least one that I could spot.&lt;/p&gt;
&lt;p&gt;So&amp;hellip;. what matches? The url of the bookmark and the url of the highlight. So I can match them up with that. Downside? I&amp;rsquo;ll need to get all the highlights and loop through to find matches on the bookmarks. That&amp;rsquo;ll work for my small collection, but probably not for someone with a lot of bookmarks/highlights.&lt;/p&gt;
&lt;p&gt;With that I was able to add a little marker to tell me when I have highlights. 🥳&amp;hellip; one monster down 🧟‍♀️⚔️&lt;/p&gt;
&lt;img src=&#34;https://heyloura.com/uploads/2024/screenshot-2024-08-13-155106.png&#34;&gt;
&lt;p&gt;Now I&amp;rsquo;m getting to the tricky part.&lt;/p&gt;
&lt;p&gt;One thing I don&amp;rsquo;t like with the highlights&amp;hellip; is that you can&amp;rsquo;t highlight the underlying HTML. Which means that if you highlight some text with an anchor tag, you get the text of the anchor, but not the anchor link itself. Which means the handy web reader you can get from Micro.blog won&amp;rsquo;t work out of the book. First, here is a look at a different part of the JSON returned for a bookmark:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;_microblog&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;links&amp;#34;&lt;/span&gt;: [
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;id&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;6888713&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#960050;background-color:#1e0010&#34;&gt;....,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        ],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can&amp;rsquo;t use the id of the bookmark to get the Micro.blog reader. The endpoint is &lt;code&gt;https://micro.blog/hybrid/bookmarks/123&lt;/code&gt;
, with the &lt;code&gt;123&lt;/code&gt; being the id of the first object under the &lt;code&gt;_microblog.links&lt;/code&gt; property in a bookmark response. This returns a text response that is an HTML page. Sweet.&lt;/p&gt;
&lt;p&gt;Now I can start dissecting it!&lt;/p&gt;
&lt;h2 id=&#34;in-which-i-spend-way-too-much-time-staring-at-source-code&#34;&gt;In Which I Spend Way Too Much Time Staring At Source Code&lt;/h2&gt;
&lt;p&gt;This was the tedious part of the adventure. I needed to parse out what was being returned, rip it apart and then rebuild it. I&amp;rsquo;m on a personal mission to not use any JavaScript libraries, so&amp;hellip;.&lt;/p&gt;
&lt;h3 id=&#34;step-one-bye-bye-jquery-&#34;&gt;Step one: Bye bye JQuery 👻⚔️&lt;/h3&gt;
&lt;p&gt;Luckily the contents of the web reader page has a div around it with an id &lt;code&gt;&amp;lt;div id=&amp;quot;content&amp;quot;&amp;gt;&lt;/code&gt;. This means I can get the content of the bookmark. A point in my favor, the inline JQuery script is outside that div. But by removing JQuery the two helpful script tags included by Micro.blog aren&amp;rsquo;t going to work anymore.&lt;/p&gt;
&lt;h3 id=&#34;step-two-fix-the-broken-images-&#34;&gt;Step two: Fix the broken images 🧪🔨&lt;/h3&gt;
&lt;p&gt;So if the bookmark had images in it&amp;hellip; they are all broken. Why? because where my app is running is not where the images are. Thankfully I have a base url to work with. It was included in the head of the HTML returned by Micro.blog. I grabbed the &lt;code&gt;&amp;lt;base href=&amp;quot;&amp;quot; /&amp;gt;&lt;/code&gt;
extracted the value and then prepended it to all the image src&amp;rsquo;s.&lt;/p&gt;
&lt;h3 id=&#34;step-three-mark-up-the-existing-highlights-&#34;&gt;Step Three: Mark up the existing highlights 🖊️🧝🏻‍♀️&lt;/h3&gt;
&lt;p&gt;Another downside, using just the reader page I don&amp;rsquo;t have the highlighted text&amp;hellip; or title. Micro.blog seems, I&amp;rsquo;m not sure on this since I can&amp;rsquo;t see their code, to generate the highlight inside a script, marking the start and end range of the highlight&amp;hellip; like this &lt;code&gt;restoreHighlight(0, 1132, 1225);&lt;/code&gt; with restoreHighlight being a custom function included on the page.&lt;/p&gt;
&lt;p&gt;For now I&amp;rsquo;m to loop through my highlights and find the ones I need. But this a place where I may need to rethink my approach. Now that have the text of the highlight, I loop through each one and add a &lt;code&gt;&amp;lt;mark&amp;gt;&lt;/code&gt; tag around the matching text of the content. This very neatly adds a highlight, no additional CSS needed.&lt;/p&gt;
&lt;h3 id=&#34;step-four-write-new-javascript-to-capture-highlighted-text-&#34;&gt;Step Four: Write new JavaScript to capture highlighted text 🐲🔥&lt;/h3&gt;
&lt;p&gt;Note: I&amp;rsquo;ve only tested this on browsers I use&amp;hellip;&lt;/p&gt;
&lt;p&gt;Okay, let&amp;rsquo;s get technical. I need to listen for a text selection event and then get what was selected. Plus I really only need to care if something was actually selected.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;document.&lt;span style=&#34;color:#a6e22e&#34;&gt;addEventListener&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;selectionchange&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;event&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;selection&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; window.&lt;span style=&#34;color:#a6e22e&#34;&gt;getSelection&lt;/span&gt;();     
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#a6e22e&#34;&gt;selection&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;rangeCount&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;===&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;getSelectionHtml&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;the &lt;code&gt;getSelectionHtml()&lt;/code&gt; function I wrote does all the nitty gritty calculating and saves the information in a hidden form on the page. This form appears in a dialog modal after a one second delay. If the submit button is hit, it posts the form and I save the highlight. This Micro.blog endpoint wasn&amp;rsquo;t actually included in the documentation&amp;hellip; but it works:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;form&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;URLSearchParams&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;form&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;append&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;text&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;text&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;form&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;append&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;start&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;start&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;form&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;append&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;end&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;end&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;         
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;const&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;posting&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;await&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fetch&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;`https://micro.blog/bookmarks/&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;${&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;id&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;}&lt;/span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;/highlights`&lt;/span&gt;, {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;method&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;POST&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;body&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;form&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;toString&lt;/span&gt;(),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;headers&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Content-Type&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;application/x-www-form-urlencoded; charset=utf-8&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Authorization&amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;:&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Bearer &amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;mbToken&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Note, the &lt;code&gt;id&lt;/code&gt; is the id of the reader, not the bookmark.&lt;/p&gt;
&lt;p&gt;Okay, so the &lt;code&gt;getSelectionHtml()&lt;/code&gt; is a bit of a beast. I haven&amp;rsquo;t really played around with widow.getSelection() before and I wanted to remove the JQuery dependency with the original code. Luckily it didn&amp;rsquo;t need too much tweaking to make it work.&lt;/p&gt;
&lt;p&gt;First we want to make sure our selection isn&amp;rsquo;t undefined. Then we want to create a container (in this case a div) to hold our HTML selection. I then iterate through and clone the nodes into that container. Then I can get the innerHTML of the container and that holds the user selected HTML. For brevity I took out the calculateOffset portion and left comments where that code would go.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;function getSelectionHtml() {
    var html = &amp;#34;&amp;#34;;
    if (typeof window.getSelection != &amp;#34;undefined&amp;#34;) {
        var sel = window.getSelection();
        if (sel.rangeCount) {
            var container = document.createElement(&amp;#34;div&amp;#34;);
            for (var i = 0, len = sel.rangeCount; i &amp;lt; len; ++i) {
                container.appendChild(sel.getRangeAt(i).cloneContents());
            }
            html = container.innerHTML;
            if (!sel.isCollapsed) {
                //save the HTML to the form
                //calculate the offsets and save values to form
                //trigger the 1 second delay
            }
        }
    } else if (typeof document.selection != &amp;#34;undefined&amp;#34;) {
        if (document.selection.type == &amp;#34;Text&amp;#34;) {
            html = document.selection.createRange().htmlText;
            //save the HTML to the form
            //calculate the offsets and save values to form
            //trigger the 1 second delay
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To calculate the offset you need to gather up all the child nodes. Luckily this code was already included in the page. I just needed to remove the JQuery portion and port it over to vanillaJS.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;function gatherNodes(startElement, results) {
    results.push(startElement);
    startElement.childNodes.forEach(function (e) {
        gatherNodes(e, results);
    });
}
                  
function calculateContentOffset(findNode, offset) {
    var e = document.getElementById(&amp;#34;content&amp;#34;);
    var results = Array();
    gatherNodes(e, results);
    var current_pos = 0;
    for (var i = 0; i &amp;lt; results.length; i++) {
        var node = results[i];
        if (node.isSameNode(findNode)) {
            return current_pos + offset;
        }
        if (node.length != undefined) {
            current_pos = current_pos + node.length;
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Whew&amp;hellip;.&lt;/p&gt;
&lt;p&gt;It works!&lt;/p&gt;
&lt;img src=&#34;https://heyloura.com/uploads/2024/screenshot-2024-08-14-080112.png&#34;&gt;
&lt;p&gt;And if I double check it on Micro.blog, I can see the link is saved. Yep!&lt;/p&gt;
&lt;img src=&#34;https://heyloura.com/uploads/2024/screenshot-2024-08-14-080344.png&#34;&gt;
&lt;p&gt;Or maybe not&amp;hellip;. 😱
&lt;img src=&#34;https://heyloura.com/uploads/2024/screenshot-2024-08-14-081102.png&#34;&gt;
As you can see, there is a mismatch between what&amp;rsquo;s in my UI and on the Micro.blog website. I wonder if the offset I&amp;rsquo;m calculating is different somehow&amp;hellip; 🤔&lt;/p&gt;
&lt;h2 id=&#34;there-are-monsters-yet-to-slay&#34;&gt;There Are Monsters Yet To Slay🦇&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Highlights saved with my app that have HTML in them&amp;hellip; aren&amp;rsquo;t visible or in the wrong position in the Micro.blog reader on the Micro.blog website. They do show up correctly in the highlight list and &lt;em&gt;when hitting the &amp;ldquo;New Post&amp;rdquo; button&lt;/em&gt;. Which was one of the reasons I started this adventure.&lt;/li&gt;
&lt;li&gt;Highlights saved with the Micro.blog website that cover HTML elements (like anchors) don&amp;rsquo;t show in my reader.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;So this isn&amp;rsquo;t ready for prime-time.&lt;/p&gt;
&lt;h2 id=&#34;conclusion&#34;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;I now have my own little web reader for my bookmarks and I can mark up highlights. It has its drawbacks, but I&amp;rsquo;m going to use it for a bit and see how it holds up.&lt;/p&gt;
&lt;p&gt;I think that&amp;rsquo;s all the adventuring I&amp;rsquo;m up for this week. I&amp;rsquo;m going home and having a nice cup of peppermint tea.&lt;/p&gt;
</description>
      <source:markdown># Coding Adventure: Micro.blog Bookmarks &amp; Highlights

Life has been way too much lately and most of my hobbies were put on pause while I dealt with keeping up with the unexpected. One of those that was dropped was writing down weekly notes that focused on my development side projects (and honestly, even coding side-projects took a nose-dive). My last post was on March 1st 😱

So I&#39;m doing a little reframing to make it feel a bit easier to write, thus the &#34;Coding Adventure&#34;. I want to keep these posts more along the line of &#34;Hey, here&#39;s something fun I played around with and it might still be mostly broken 🤪&#34;

## Background

I use Micro.blog, I know.... you&#39;re shocked 😝 and one of the things I love, is how open they are with their API. It&#39;s a fun playground of potential side-projects just waiting to be explored. One piece that I&#39;ve dabbled with is the bookmarks feature. Basically you can save URLs as bookmarks and if you are a premium member, organize them with tags and add highlights. Manton, the creator of Micro.blog, rolled out a feature to bookmarks including having AI summarize the link (only if you&#39;ve enabled AI on your account and, important to me, if the robots.txt of the bookmarked link allows it).

But it&#39;s not a feature I use all that much and I wanted to see if I could change that with a few of my own touches.

## Getting Started

Like most of my recent side-projects, I&#39;ve been using Deno. Its quick to get started with a Github repo and they have hard free tier limits so I feel safe playing around.

The first thing I needed was a valid authorization token from micro.blog. I got this using indieauth and saving the resulting token in a http-only cookie. That way I&#39;ll have the token with each page request. To make things easier in this proof of concept, I hard-coded values inside the server generated HTML form I was using. For instance the form action, the scope and response type. On the server I generated a random string for the state (the `${}` notation is a [JavaScript template literal](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals)) and I also included the state as a session cookie when the page is rendered.

```html
&lt;form action=&#34;https://micro.blog/indieauth/auth&#34; method=&#34;get&#34;&gt;
    &lt;input type=&#34;url&#34; placeholder=&#34;https://username.micro.blog&#34; name=&#34;me&#34;&gt;
    &lt;input type=&#34;hidden&#34; name=&#34;client_id&#34; value=&#34;https://my-demo.com&#34;/&gt;
    &lt;input type=&#34;hidden&#34; name=&#34;redirect_uri&#34; value=&#34;https://my-demo.com/auth&#34;/&gt;
    &lt;input type=&#34;hidden&#34; name=&#34;state&#34; value=&#34;${uuid}&#34;/&gt;
    &lt;input type=&#34;hidden&#34; name=&#34;scope&#34; value=&#34;create&#34;/&gt;
    &lt;input type=&#34;hidden&#34; name=&#34;response_type&#34; value=&#34;code&#34;/&gt;
    &lt;button type=&#34;submit&#34;&gt;Sign In&lt;/button&gt;
&lt;/form&gt;
```

Submitting this form creates a get request to Mico.blog&#39;s indieauth endpoint which redirects me to approve the login. Once I approve it, it sends a request back to my `/auth` endpoint with a code and state in the query string of a URL. I check that the state matches the state inside the cookie and then I make a POST request to Micro.blog&#39;s token endpoint, `https://micro.blog/indieauth/token`, using the code. If everything was wired up correctly I get back my lovey authorization token. Here is a code snippet of the token portion:

```javascript
const formBody = new URLSearchParams();
formBody.append(&#34;code&#34;, code);
formBody.append(&#34;client_id&#34;, &#39;https://my-demo.com&#39;);
formBody.append(&#34;grant_type&#34;, &#34;authorization_code&#34;);

// request access_token
const fetching = await fetch(&#39;https://micro.blog/indieauth/token&#39;, {
    method: &#34;POST&#34;,
    body: formBody.toString(),
    headers: {
        &#34;Content-Type&#34;: &#34;application/x-www-form-urlencoded; charset=utf-8&#34;,
        &#34;Accept&#34;: &#34;application/json&#34;
    }
});    
const response = await fetching.json();
```

## A Fetch Quest: Getting My Bookmarks

Okay, let&#39;s get some bookmarks. Micro.blog has [documentation on its bookmarks](https://help.micro.blog/t/json-api-bookmarks/110) endpoints in its help center, so that&#39;s the first place to go diving. This was all familiar to me since I put bookmarks in my side project Lillihub a while back.

```javascript
let fetching = await fetch(`https://micro.blog/posts/bookmarks`, { 
    method: &#34;GET&#34;, 
    headers: { &#34;Authorization&#34;: &#34;Bearer &#34; + access_token } 
});
let results = await fetching.json();
```

This returns twenty-five of my most recent bookmarks. Nice. There was some trial and error but I figured out that you can use query parameter `before_id` to enable getting more bookmarks. For example, if the last bookmark id was `123123` then I could call `https://micro.blog/posts/bookmarks?before_id=123123` and get the twenty-five after that last bookmark.

If you have tags you can get the by adding the `tag` with the name of the tag, like this: `https://micro.blog/posts/bookmarks?tag=code` and you will get the first 25 bookmarks with that tag.

## Lost in the Woods: Bookmarks and What I Couldn&#39;t Figure Out

What I couldn&#39;t figure out is how to go backwards. The Micro.blog post API supports a `since_id` parameter and a `count` parameter allowing for a full paging experience. Those don&#39;t seem to work on bookmarks endpoint. I tried grabbing the bookmarks via the micropub endpoint but I had no luck there, just getting the posts on my blog.

Nor could I figure out how to search for a bookmark. I figured this would be handy with the AI summarization or even to search part of the bookmark title. There is limited support for searching on the Micro.blog website but it mixes results together from the other related sections of links and highlights.

So two paths then. Load all the bookmarks on the HTML page and then use JavaScript to search OR load all the bookmarks on the server, search it, and then return the results.

Since I don&#39;t have thousands of bookmarks... yet. I&#39;m going with the quick and easy client side solution.

## Search For Treasure: Enabling Search

The while loop was my weapon of choice for this part of the adventure. I iterated calling the bookmarks API and gathered the results. And I kept doing that until the request returned less than 25 results... signaling the end.

Then I put those bookmarks in a table with some styling and....

&lt;img src=&#34;https://heyloura.com/uploads/2024/screenshot-2024-08-13-114248.png&#34;&gt;

Nice.

The search box runs a little bit of JavaScript that hides table rows if they don&#39;t contain the search term. Clicking on a tag, whether at the top or on a bookmark fills the textbox and retriggers the search.

One strange thing with the Micro.blog bookmark API is that it doesn&#39;t have separate properties for things like title or reader link. It jumbles them all together in the content\_html field.

```json
{
    ...,
    &#34;content_html&#34;: &#34;&lt;p&gt;Homemade Oreos &lt;a href=&#34;https://sallysbakingaddiction.com/homemade-oreos/&#34;&gt;sallysbakingaddiction.com&lt;/a&gt;&lt;/p&gt;&lt;p class=&#34;post_archived_links&#34;&gt;Reader: &lt;a href=&#34;https://micro.blog/bookmarks/6888713\&#34;&gt;sallysbakingaddiction.com&lt;/a&gt; &lt;/p&gt;&#34;,
    ...
},
```

So I&#39;m displaying the bookmark title as what&#39;s in the first paragraph tag and removing everything else.

## Side Quest: Adding Bookmarks

This was actually something [I figured out a while back](https://heyloura.com/2023/10/16/dev-diary-week.html#bookmarks), but I figured I&#39;d actually list the code sample here for a more complete picture.

First you need to create the bookmark using micropub:

```javascript
const formBody = new URLSearchParams();
formBody.append(&#34;h&#34;, &#34;entry&#34;);
formBody.append(&#34;bookmark-of&#34;, url);

const posting = await fetch(`https://micro.blog/micropub`, {
    method: &#34;POST&#34;,
    body: formBody.toString(),
    headers: {
        &#34;Content-Type&#34;: &#34;application/x-www-form-urlencoded; charset=utf-8&#34;,
        &#34;Authorization&#34;: &#34;Bearer &#34; + accessTokenValue
            }
});
```

and then you need to add the tags using the Micro.blog bookmarks API.

1. Get the bookmarks
2. The new one should be first one returned, grab it&#39;s `id`
3. Add the tags (comma-separated list)

```javascript
const formBodyTags = new URLSearchParams();
formBodyTags.append(&#34;tags&#34;, tags);
           
const postingTags = await fetch(`https://micro.blog/posts/bookmarks/${bookmark.id}`, {
    method: &#34;POST&#34;,
    body: formBodyTags.toString(),
    headers: {
        &#34;Content-Type&#34;: &#34;application/x-www-form-urlencoded; charset=utf-8&#34;,
        &#34;Authorization&#34;: &#34;Bearer &#34; + accessTokenValue
    }
});
const responseTags = await postingTags.text();
```

## Exploring the Dungeon: Highlights

Let&#39;s circle back to that reader link in the content\_html property. Micro.blog premium users can make highlights on bookmarks, assuming the website&#39;s robot.txt doesn&#39;t prohibit it being crawled and the content isn&#39;t dynamic. A copy of the content is provided along with an interface to make highlights. [This video at the 2:52 mark](https://youtu.be/nwekF1H87WM?t=173) by [@manton](https://micro.blog/manton) shows the functionality I&#39;m talking about.

First thing I wanted to change, I want the number of highlights displayed in-line with my bookmarks. So I fire off the request to get my highlights and the first problem becomes apparent. There is no `id` link between a bookmark and a highlight, or at least one that I could spot.

So.... what matches? The url of the bookmark and the url of the highlight. So I can match them up with that. Downside? I&#39;ll need to get all the highlights and loop through to find matches on the bookmarks. That&#39;ll work for my small collection, but probably not for someone with a lot of bookmarks/highlights.

With that I was able to add a little marker to tell me when I have highlights. 🥳... one monster down 🧟‍♀️⚔️

&lt;img src=&#34;https://heyloura.com/uploads/2024/screenshot-2024-08-13-155106.png&#34;&gt;

Now I&#39;m getting to the tricky part.

One thing I don&#39;t like with the highlights... is that you can&#39;t highlight the underlying HTML. Which means that if you highlight some text with an anchor tag, you get the text of the anchor, but not the anchor link itself. Which means the handy web reader you can get from Micro.blog won&#39;t work out of the book. First, here is a look at a different part of the JSON returned for a bookmark:

```json
   {
      ...
      &#34;_microblog&#34;: {
        &#34;links&#34;: [
          {
            &#34;id&#34;: 6888713,
            ....,
          }
        ],
      }
    }
```

You can&#39;t use the id of the bookmark to get the Micro.blog reader. The endpoint is `https://micro.blog/hybrid/bookmarks/123`
, with the `123` being the id of the first object under the `_microblog.links` property in a bookmark response. This returns a text response that is an HTML page. Sweet.

Now I can start dissecting it!

## In Which I Spend Way Too Much Time Staring At Source Code

This was the tedious part of the adventure. I needed to parse out what was being returned, rip it apart and then rebuild it. I&#39;m on a personal mission to not use any JavaScript libraries, so....

### Step one: Bye bye JQuery 👻⚔️

Luckily the contents of the web reader page has a div around it with an id `&lt;div id=&#34;content&#34;&gt;`. This means I can get the content of the bookmark. A point in my favor, the inline JQuery script is outside that div. But by removing JQuery the two helpful script tags included by Micro.blog aren&#39;t going to work anymore.

### Step two: Fix the broken images 🧪🔨

So if the bookmark had images in it... they are all broken. Why? because where my app is running is not where the images are. Thankfully I have a base url to work with. It was included in the head of the HTML returned by Micro.blog. I grabbed the `&lt;base href=&#34;&#34; /&gt;`
extracted the value and then prepended it to all the image src&#39;s.

### Step Three: Mark up the existing highlights 🖊️🧝🏻‍♀️

Another downside, using just the reader page I don&#39;t have the highlighted text... or title. Micro.blog seems, I&#39;m not sure on this since I can&#39;t see their code, to generate the highlight inside a script, marking the start and end range of the highlight... like this `restoreHighlight(0, 1132, 1225);` with restoreHighlight being a custom function included on the page.

For now I&#39;m to loop through my highlights and find the ones I need. But this a place where I may need to rethink my approach. Now that have the text of the highlight, I loop through each one and add a `&lt;mark&gt;` tag around the matching text of the content. This very neatly adds a highlight, no additional CSS needed.

### Step Four: Write new JavaScript to capture highlighted text 🐲🔥

Note: I&#39;ve only tested this on browsers I use...

Okay, let&#39;s get technical. I need to listen for a text selection event and then get what was selected. Plus I really only need to care if something was actually selected.

```javascript
document.addEventListener(&#34;selectionchange&#34;, function(event) {
    const selection = window.getSelection();     
    if (selection.rangeCount === 0) {
        return;
    }
    getSelectionHtml();
});
```

the `getSelectionHtml()` function I wrote does all the nitty gritty calculating and saves the information in a hidden form on the page. This form appears in a dialog modal after a one second delay. If the submit button is hit, it posts the form and I save the highlight. This Micro.blog endpoint wasn&#39;t actually included in the documentation... but it works:

```javascript
const form = new URLSearchParams();
form.append(&#34;text&#34;, text);
form.append(&#34;start&#34;, start);
form.append(&#34;end&#34;, end);
         
const posting = await fetch(`https://micro.blog/bookmarks/${id}/highlights`, {
    method: &#34;POST&#34;,
    body: form.toString(),
    headers: {
        &#34;Content-Type&#34;: &#34;application/x-www-form-urlencoded; charset=utf-8&#34;,
        &#34;Authorization&#34;: &#34;Bearer &#34; + mbToken
    }
});
```

Note, the `id` is the id of the reader, not the bookmark.

Okay, so the `getSelectionHtml()` is a bit of a beast. I haven&#39;t really played around with widow.getSelection() before and I wanted to remove the JQuery dependency with the original code. Luckily it didn&#39;t need too much tweaking to make it work.

First we want to make sure our selection isn&#39;t undefined. Then we want to create a container (in this case a div) to hold our HTML selection. I then iterate through and clone the nodes into that container. Then I can get the innerHTML of the container and that holds the user selected HTML. For brevity I took out the calculateOffset portion and left comments where that code would go.

```
function getSelectionHtml() {
    var html = &#34;&#34;;
    if (typeof window.getSelection != &#34;undefined&#34;) {
        var sel = window.getSelection();
        if (sel.rangeCount) {
            var container = document.createElement(&#34;div&#34;);
            for (var i = 0, len = sel.rangeCount; i &lt; len; ++i) {
                container.appendChild(sel.getRangeAt(i).cloneContents());
            }
            html = container.innerHTML;
            if (!sel.isCollapsed) {
                //save the HTML to the form
                //calculate the offsets and save values to form
                //trigger the 1 second delay
            }
        }
    } else if (typeof document.selection != &#34;undefined&#34;) {
        if (document.selection.type == &#34;Text&#34;) {
            html = document.selection.createRange().htmlText;
            //save the HTML to the form
            //calculate the offsets and save values to form
            //trigger the 1 second delay
        }
    }
}
```

To calculate the offset you need to gather up all the child nodes. Luckily this code was already included in the page. I just needed to remove the JQuery portion and port it over to vanillaJS.

```
function gatherNodes(startElement, results) {
    results.push(startElement);
    startElement.childNodes.forEach(function (e) {
        gatherNodes(e, results);
    });
}
                  
function calculateContentOffset(findNode, offset) {
    var e = document.getElementById(&#34;content&#34;);
    var results = Array();
    gatherNodes(e, results);
    var current_pos = 0;
    for (var i = 0; i &lt; results.length; i++) {
        var node = results[i];
        if (node.isSameNode(findNode)) {
            return current_pos + offset;
        }
        if (node.length != undefined) {
            current_pos = current_pos + node.length;
        }
    }
}
```

Whew....

It works!

&lt;img src=&#34;https://heyloura.com/uploads/2024/screenshot-2024-08-14-080112.png&#34;&gt;

And if I double check it on Micro.blog, I can see the link is saved. Yep!

&lt;img src=&#34;https://heyloura.com/uploads/2024/screenshot-2024-08-14-080344.png&#34;&gt;

Or maybe not.... 😱
&lt;img src=&#34;https://heyloura.com/uploads/2024/screenshot-2024-08-14-081102.png&#34;&gt;
As you can see, there is a mismatch between what&#39;s in my UI and on the Micro.blog website. I wonder if the offset I&#39;m calculating is different somehow... 🤔

## There Are Monsters Yet To Slay🦇

1. Highlights saved with my app that have HTML in them... aren&#39;t visible or in the wrong position in the Micro.blog reader on the Micro.blog website. They do show up correctly in the highlight list and *when hitting the &#34;New Post&#34; button*. Which was one of the reasons I started this adventure.
2. Highlights saved with the Micro.blog website that cover HTML elements (like anchors) don&#39;t show in my reader.

So this isn&#39;t ready for prime-time.

## Conclusion

I now have my own little web reader for my bookmarks and I can mark up highlights. It has its drawbacks, but I&#39;m going to use it for a bit and see how it holds up.

I think that&#39;s all the adventuring I&#39;m up for this week. I&#39;m going home and having a nice cup of peppermint tea.
</source:markdown>
    </item>
    
    <item>
      <title>And then she had thoughts.</title>
      <link>https://heyloura-test.micro.blog/2024/06/21/and-then-she.html</link>
      <pubDate>Fri, 21 Jun 2024 17:05:51 -0400</pubDate>
      
      <guid>http://heyloura-test.micro.blog/2024/06/21/and-then-she.html</guid>
      <description>&lt;p&gt;I guess you need a lot of characters to show the title thingy. Why? Great question. Whatever world. I don&amp;rsquo;t know what I&amp;rsquo;m doing and I don&amp;rsquo;t know anything else.&lt;/p&gt;
&lt;p&gt;This is really hard and I&amp;rsquo;m not sure what to do anymore. I wish things were easier and that anyone would actually be helpful. Oh well, guess that&amp;rsquo;s not really a thing.&lt;/p&gt;</description>
      <source:markdown>I guess you need a lot of characters to show the title thingy. Why? Great question. Whatever world. I don&#39;t know what I&#39;m doing and I don&#39;t know anything else. 

&lt;!--more--&gt;

This is really hard and I&#39;m not sure what to do anymore. I wish things were easier and that anyone would actually be helpful. Oh well, guess that&#39;s not really a thing.
</source:markdown>
    </item>
    
    <item>
      <title></title>
      <link>https://heyloura-test.micro.blog/2024/06/21/its-just-me.html</link>
      <pubDate>Fri, 21 Jun 2024 15:02:19 -0400</pubDate>
      
      <guid>http://heyloura-test.micro.blog/2024/06/21/its-just-me.html</guid>
      <description>&lt;p&gt;It&amp;rsquo;s just me and my son tonight. So pizza, ice cream, and some MTG battles it is. He already beat my bird and cat deck. Let&amp;rsquo;s see if this commander battle goes any differently 😂&lt;/p&gt;
</description>
      <source:markdown>It&#39;s just me and my son tonight. So pizza, ice cream, and some MTG battles it is. He already beat my bird and cat deck. Let&#39;s see if this commander battle goes any differently 😂
</source:markdown>
    </item>
    
    <item>
      <title></title>
      <link>https://heyloura-test.micro.blog/2024/04/17/seeing-if-this.html</link>
      <pubDate>Wed, 17 Apr 2024 17:26:04 -0400</pubDate>
      
      <guid>http://heyloura-test.micro.blog/2024/04/17/seeing-if-this.html</guid>
      <description>&lt;p&gt;Seeing if this test works&amp;hellip;&lt;/p&gt;
</description>
      <source:markdown>Seeing if this test works... 
</source:markdown>
    </item>
    
    <item>
      <title></title>
      <link>https://heyloura-test.micro.blog/2024/02/04/testing-things-out.html</link>
      <pubDate>Sun, 04 Feb 2024 20:14:31 -0400</pubDate>
      
      <guid>http://heyloura-test.micro.blog/2024/02/04/testing-things-out.html</guid>
      <description>&lt;p&gt;Testing things out.&lt;/p&gt;
</description>
      <source:markdown>Testing things out.
</source:markdown>
    </item>
    
    <item>
      <title></title>
      <link>https://heyloura-test.micro.blog/2023/12/08/test-one.html</link>
      <pubDate>Fri, 08 Dec 2023 10:38:07 -0400</pubDate>
      
      <guid>http://heyloura-test.micro.blog/2023/12/08/test-one.html</guid>
      <description>&lt;p&gt;Test One.&lt;/p&gt;
</description>
      <source:markdown>Test One.
</source:markdown>
    </item>
    
  </channel>
</rss>
