{"version":"https://jsonfeed.org/version/1","title":"log.thisotherthing","home_page_url":"https://log.thisotherthing.co/","feed_url":"https://log.thisotherthing.co/feed.json","description":"Updates from thisotherthing","items":[{"id":"https://log.thisotherthing.co/posts/2025/tools-updates/","url":"https://log.thisotherthing.co/posts/2025/tools-updates/","title":"tools updates","content_html":"<p>I recently got back to working on my collection of online tools :D</p>\n<h2>hash</h2>\n<p><img src=\"https://log.thisotherthing.co/project_images/2408523081469759.600\" alt=\"Screenshot of the hash tool. It shows options for file hashing, and text hashing.\" loading=\"lazy\" decoding=\"async\"></p>\n<p>I needed some hashes for files.</p>\n<p>Searching for cli tools, and remembering them can be annoying. And the exisiting online hashing tools look either almost too barebone, or maybe scammy. Also I'm dubious about giving random websites access to those files.\nI get the hypocrisy, but with my own stuff I know what's going on :D</p>\n<p>The tool allows loading files, and typing text. It then gives you sha-1, sha-256, and md5 hashes.</p>\n<p>The sha hashes use the <a href=\"https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/digest\">built in browser apis</a>. For md5 I found <a href=\"https://www.npmjs.com/package/@smithy/md5-js\">@smithy/md5-js</a>, which seems reasonably popular.</p>\n<p>You can check it out at <a href=\"https://tools.thisotherthing.co/hash\">tools.thisotherthing.co/hash</a></p>\n<h2>calc</h2>\n<p><img src=\"https://log.thisotherthing.co/project_images/907394521089129.600\" alt=\"Screenshot of the updated calc tool. It shows that multiple lines are now possible.\" loading=\"lazy\" decoding=\"async\"></p>\n<p>I started working through my planned tasks there (but also started adding to the list again).</p>\n<p>You now have:</p>\n<ul>\n<li>support for negative numbers\n<ul>\n<li>no idea how I never ran into that issue before</li>\n</ul>\n</li>\n<li>functions like <code>map</code>, <code>clamp</code>, <code>min</code>, <code>max</code></li>\n<li>ms to fps convertions</li>\n<li>comparisons like <code>&lt;</code>, <code>&gt;</code>, <code>==</code>, <code>&lt;=</code>, <code>&gt;=</code>\n<ul>\n<li>they return either <code>0</code> or <code>1</code></li>\n</ul>\n</li>\n<li>named large numbers, so you can input something like <code>1billion</code></li>\n<li>multiple lines 🎉\n<ul>\n<li>this is something I've wanted for a long time, but was always a bit daunted by implementing it</li>\n<li>for now you can add/remove lines, and once you enter that &quot;mode&quot;, there's now also line labels</li>\n<li>next up is making other lines available as variables, so you can use their results in other lines\n<ul>\n<li>but it's only planned to allow using lines above the selected one, so we don't get a crazy dependency graph</li>\n</ul>\n</li>\n</ul>\n</li>\n</ul>\n<p>You can try the updates at <a href=\"https://tools.thisotherthing.co/calc\">tools.thisotherthing.co/calc</a></p>\n<h2>themes</h2>\n<p>I also added more color themes. I didn't expect it, but I really like the gameboy colors XD</p>\n","date_published":"Sat, 13 Dec 2025 00:00:00 GMT"},{"id":"https://log.thisotherthing.co/posts/2025/service-workers-for-offline-web-apps/","url":"https://log.thisotherthing.co/posts/2025/service-workers-for-offline-web-apps/","title":"Service Workers for offline web apps","content_html":"<p>I recently worked on getting my web tools to also work offline.</p>\n<p>While there are tools like <a href=\"https://developer.chrome.com/docs/workbox\">Workbox</a> and also <a href=\"https://vite-pwa-org.netlify.app/\">a wrapper for Workbox for vite</a>, those feel quite &quot;big&quot;, and config heavy.</p>\n<p>My tools mostly don't feel like a PWA to me, that you would install on your phone.</p>\n<p>So I wanted to try it myself, and learn about service workers.</p>\n<p>About the code:</p>\n<ul>\n<li>it is a vite/rollup plugin, that gathers all the output files during the build, and puts them into the service worker code</li>\n<li>the cache name is made from the hashes filenames (vite also adds filehashes in them, so the cache name should change, along with code changes)</li>\n<li>add the worker install to the index.html file</li>\n</ul>\n<pre><code class=\"language-js\">import { createHash } from &quot;node:crypto&quot;;\nimport { existsSync, mkdirSync, writeFileSync } from &quot;node:fs&quot;;\nimport { join } from &quot;node:path&quot;;\n\nimport { type Plugin } from &quot;vite&quot;;\n\nconst getServiceWorkerCode = (\n  cacheName: string,\n  fileToCache: string[],\n): string =&gt; {\n  const builder: string[] = [];\n\n  builder.push(`const cacheName = &quot;${cacheName}&quot;;`);\n  builder.push(`self.addEventListener('install', (e) =&gt; {\n  console.info('[Service Worker] Install');\n  e.waitUntil((async () =&gt; {\n    const cache = await caches.open(cacheName);\n    console.info('[Service Worker] Caching all: app shell and content');\n    await cache.addAll(${JSON.stringify(fileToCache)});\n  })());\n});`);\n\n  builder.push(`self.addEventListener('fetch', (e) =&gt; {\n    // Cache http and https only, skip unsupported chrome-extension:// and file://...\n    if (!(\n       e.request.url.startsWith('http:') || e.request.url.startsWith('https:')\n    )) {\n        return;\n    }\n\n  e.respondWith((async () =&gt; {\n    const r = await caches.match(e.request);\n    console.info(\\`[Service Worker] Fetching resource: \\${e.request.url}\\`);\n    if (r) return r;\n    const response = await fetch(e.request);\n    const cache = await caches.open(cacheName);\n    console.info(\\`[Service Worker] Caching new resource: \\${e.request.url}\\`);\n    cache.put(e.request, response.clone());\n    return response;\n  })());\n});`);\n\n  return builder.join(&quot;\\n&quot;);\n};\n\nexport const OfflineServiceWorkerPlugin = (\n  options: { projectName: string },\n): Plugin =&gt; {\n  return {\n    name: &quot;offline-service-worker-plugin&quot;, // this name will show up in logs and errors\n    enforce: &quot;post&quot;,\n    apply: &quot;build&quot;,\n    transformIndexHtml: {\n      order: &quot;post&quot;,\n      handler: () =&gt; {\n        return [{\n          tag: &quot;script&quot;,\n          children: `if (&quot;serviceWorker&quot; in navigator) {\n\t\t\tnavigator.serviceWorker.register(&quot;/sw-${options.projectName}.js&quot;, {scope: &quot;/${options.projectName}&quot;});\n\t\t}`,\n          injectTo: &quot;body-prepend&quot;,\n        }];\n      },\n    },\n    generateBundle: (bundleOptions, bundle) =&gt; {\n      // const project = options.dir.split(&quot;/&quot;).at(-1);\n\n      const files = Object.keys(bundle);\n\n      const buildHash = createHash(&quot;md5&quot;).update(files.join(&quot;,&quot;)).digest(\n        &quot;hex&quot;,\n      ).slice(0, 8);\n\n      if (!existsSync(bundleOptions.dir)) {\n        mkdirSync(bundleOptions.dir, { recursive: true });\n      }\n\n      writeFileSync(\n        join(bundleOptions.dir, &quot;../&quot;, `sw-${options.projectName}.js`),\n        getServiceWorkerCode(\n          buildHash,\n          files,\n        ),\n      );\n    },\n  };\n};\n</code></pre>\n<p>Add this to your repo, and your vite app's config in plugins. Then it should also work for your projects</p>\n<p>Some things I learned:</p>\n<ul>\n<li>Debugging issues with the service worker install is annoying. Firefox didn't give me much in the console. Chromium was slightly better</li>\n<li>service worker can only apply to pages at the same folder depth, or deeper\n<ul>\n<li>I ended up putting the service workers at the root. While the apps <code>index.html</code> was in the same folder as the service worker, this didn't work, when the page url ended up being <code>/tool</code>, rather than <code>/tool/</code>. Since <code>/tool/sw.js</code> is then deeper than <code>/tool</code></li>\n<li>and again, the browser may or may not tell you this ':)</li>\n</ul>\n</li>\n<li>even though the service worker was then in a different folder, the asset urls then still need to be relative to the current page\n<ul>\n<li>so, keep the asset path in your service worker relative to the <code>index.html</code></li>\n<li>and don't care about the path relative to the service worker file</li>\n<li>I didn't add the service worker to its own cache, and I think you're also not supposed to :D</li>\n</ul>\n</li>\n</ul>\n<p>You can try the result with all my tools, for instance here with the <a href=\"https://tools.thisotherthing.co/comic-reader\">comic reader</a>.</p>\n<p>(I mostly wanted offline support for this one :D To read comics in trains, which sometimes don't have internet)</p>\n","date_published":"Fri, 21 Nov 2025 00:00:00 GMT"},{"id":"https://log.thisotherthing.co/posts/2025/folder-sizes/","url":"https://log.thisotherthing.co/posts/2025/folder-sizes/","title":"new tool folder-sizes","content_html":"<p>New tool alert :D</p>\n<p>You can get more info on the\n<a href=\"https://thisotherthing.co/projects/folder-sizes\">project's page on my portfolio</a>.</p>\n<p>Or <a href=\"https://tools.thisotherthing.co/folder-sizes\">check it out directly</a>.</p>\n<p><img src=\"https://log.thisotherthing.co/project_images/6363362897237658.600\" alt=\"Screenshot of folder-sizes tool, showing a round chart, with the files/folders listed on the side\" loading=\"lazy\" decoding=\"async\">\n<img src=\"https://log.thisotherthing.co/project_images/8940818256608660.600\" alt=\"Screenshot of the folder-sizes tool, showing the TreeMap view. With a lot of small rectangles, and showing that two files make up most of the folders size\" loading=\"lazy\" decoding=\"async\"></p>\n","date_published":"Sun, 16 Nov 2025 00:00:00 GMT"},{"id":"https://log.thisotherthing.co/posts/2025/portfolio-updates/","url":"https://log.thisotherthing.co/posts/2025/portfolio-updates/","title":"portfolio projects updates","content_html":"<p>I've been a bit lazy about updating the portfolio, but finally was motivated to do so.</p>\n<p>It now has pages for my eInk home dashboard display/device, deborahburgstaller.com, my large cargo strap backpack, the comic-reader, and bike toke backpack.</p>\n<p>I think I'll add nicer images to the bike toke backpack, but for now I just wanted to get it onto the page.</p>\n<p><a href=\"https://thisotherthing.co/\">Link to portfolio</a></p>\n<p><img src=\"https://log.thisotherthing.co/project_images/957356951868859.600\" alt=\"Screenshot of the portfolio in a browser. It shows the newly added projects at the top\" loading=\"lazy\" decoding=\"async\"></p>\n","date_published":"Fri, 07 Mar 2025 00:00:00 GMT"},{"id":"https://log.thisotherthing.co/posts/2025/comic-reader/","url":"https://log.thisotherthing.co/posts/2025/comic-reader/","title":"new tool comic-reader","content_html":"<p>I made a new tool. It's for reading comics :D</p>\n<p><a href=\"https://www.thisotherthing.co/projects/comic-reader\">Link to project documentation</a>\n<a href=\"https://tools.thisotherthing.co/comic-reader\">Link to comic-reader</a></p>\n<p><img src=\"https://log.thisotherthing.co/project_images/1834216191594819.600\" alt=\"Screenshot of the comic-reader. It shows a comic page on a black background in a browser window. There is minimal UI for opening a comic file, rotating the display, fullscreen and a page selection dropdown\" loading=\"lazy\" decoding=\"async\"></p>\n","date_published":"Wed, 15 Jan 2025 00:00:00 GMT"},{"id":"https://log.thisotherthing.co/posts/2025/ffmpeg-wasm-tools/","url":"https://log.thisotherthing.co/posts/2025/ffmpeg-wasm-tools/","title":"new tool ffmpeg tools","content_html":"<p>I started a new tool. It's for doing ffmpeg stuff in the browser.</p>\n<p>I moved the ui from the spritesheet tool, into a shared library. Also started adding a foldable folder, and file inputs.</p>\n<p>So far it can generate a video from images, and generate a poster image from a video.</p>\n<p>Adding more tools here should be easy (as long as the inputs are already supported in my ui library).</p>\n<p>I also want a more generic tool, where you can add any file, run any command, and get the results downloaded.</p>\n<p><a href=\"https://tools.thisotherthing.co/ffmpeg-tools\">Link to ffmpeg tools</a></p>\n<p><img src=\"https://log.thisotherthing.co/project_images/3589578596492550.600\" alt=\"Screenshot of the ffmpeg tools. It shows a two tools with options on the browser. One for rendering images into a video, and another one for getting a poster image from a video. \" loading=\"lazy\" decoding=\"async\"></p>\n","date_published":"Wed, 15 Jan 2025 00:00:00 GMT"},{"id":"https://log.thisotherthing.co/posts/2024/fahrrad-wien-json-feed/","url":"https://log.thisotherthing.co/posts/2024/fahrrad-wien-json-feed/","title":"fahrrad wien events feed","content_html":"<p>Keeping up with cycling events in/around Vienna can be bothersome.</p>\n<!--more-->\n<p>There's a bunch of events, but so far I couldn't find a combined shared calendar or rss/json feed.\nThere's a facebook group, but I couldn't find a way to get the events outside of facebook (and the dom looks like a mess).</p>\n<p>So I made a custom feed for myself.</p>\n<h2>Sources</h2>\n<p>I found a couple sites with events. If I find more, I'll add them as well.</p>\n<ul>\n<li><a href=\"http://www.criticalmass.at/category/wien/\">Critical Mass Wien</a></li>\n<li><a href=\"https://www.fahrradwien.at/termine/\">Fahrrad Wien</a></li>\n<li><a href=\"https://radelnforfuture.at/\">Radeln For Future</a></li>\n<li><a href=\"https://www.radlobby.at/wien\">Radlobby Wien</a></li>\n<li><a href=\"https://radperformance.at/termine/\">RAD Performance</a></li>\n</ul>\n<h2>Implementation</h2>\n<p>It's a deno script, that returns a <a href=\"https://www.jsonfeed.org/\">json feed</a>.</p>\n<p>The script loads the first page of the sources, finds the events in the dom, and adds them to the list of feed items. Each item has the event page's url as its id.</p>\n<p>All sites are different, and have different implementations, but finding the relevant data was quick with a few <code>querySelectorAll</code> thanks to <a href=\"https://jsr.io/@b-fuze/deno-dom\">deno dom</a>.</p>\n<p>For almost all sources, it just loads just one page (for Fahrrad Wien it also loads a second page, to get the events for the following month).\nThis hopefully shouldn't put stress on the servers.\nThe links also always point back to the event pages there.</p>\n<p>If there's nice dom available for the event on the main page, then it gets put in the body of the feed items. Worst case it's just the link with a title.</p>\n<p>I used json feed, since I didn't want to bother with xml, and it seems to be well supported in rss readers now. It's also really easy to generate, especially if you're doing it with typescript :D</p>\n<h2>Possible improvements</h2>\n<h3>Caching data</h3>\n<p>Right now, the script always fetches the pages, extracts the data, and returns the feed.</p>\n<p>This could be improved by fetching the pages with a cron job and putting the data into some kinda database. This could then also generate a json file, that can be served statically.</p>\n<h3>Getting more structured data</h3>\n<p>The most obvious improvement would be getting more info on the events, and putting them in a common format.</p>\n<p>But that would probably mean loading each events page (therefore putting more strain on the pages, and running for longer). And doing more involved extraction of the data from the dom.</p>\n<p>Some pages also have no structured data, so it would likely need some natural language processing, which I'd rather skip for this project.</p>\n<p>For now I think just getting the links to events is enough. I'll click the link, and add it to my calendar by hand.</p>\n<h3>Generating calendar event files for the feed items</h3>\n<p>Create an .ics file for each event, and add it to the feed item's body. This would be really nice, but would require getting more structed data, as listed before. So this seems out of scope.</p>\n<h3>Shared calendar</h3>\n<p>The alternative would be creating a shared calendar, but this again needs more data to automate.\nAdding entries by hand is something I'd rather not do, especially if it's just for myself.</p>\n","date_published":"Sat, 28 Dec 2024 00:00:00 GMT"},{"id":"https://log.thisotherthing.co/posts/2024/hosting-updates/","url":"https://log.thisotherthing.co/posts/2024/hosting-updates/","title":"hosting updates","content_html":"<p>I've been moving my online stuff to new places.</p>\n<!--more-->\n<h2>Static websites</h2>\n<p>Things that only need static hosting (like this blog), are now served by <a href=\"https://bunny.net/\">bunny.net</a>.</p>\n<p>I wanted to switch to an <a href=\"https://european-alternatives.eu/product/bunny-net\">EU based provider</a>, and prefer paying a fair price, rather than depending fully on US based free options.</p>\n<p>Before I was using Cloudflare Pages and before that AWS S3 with Cloudflare.</p>\n<p>To upload stuff, I wrote my own package to handle syncing the build output folders to the online storage, and purging CDN caches. It's available on <a href=\"https://jsr.io/@thisotherthing/bunny-net\">jsr.io</a>.</p>\n<h2>Serverless functions</h2>\n<p>If I can, I like writing serverless functions, to gather data from multiple APIs or generate json feeds etc.</p>\n<p>Deno is really nice for that, given that I'm quite comfortable with typescript, and you can also easily run things using deno deploy.\nBut it still has issues with <a href=\"https://docs.deno.com/runtime/fundamentals/workspaces/\">deno workspaces</a>, and once again, I disliked the thought of being dependent on the free option.</p>\n<p>Bunny.net recently started offering running serverless functions. So I started to switch to that.\nIt can only handle a single script file thought, so I take the script entrypoint and bundle it into a single file, using the <code>bundle</code> function from <a href=\"https://jsr.io/@deno/emit\">@deno/emit</a>.</p>\n<p>So for now &quot;serious&quot; scripts can be run with bunny.net, and for other quick stuff I use deno deploy.</p>\n<h2>opsCanvas</h2>\n<p>I also have some apps and scripts in the <a href=\"https://opscanvas.thisotherthing.co/\">opsCanvas</a> monorepo, that generate images for me. Since this involves wasm files and a workspace, deno deploy stopped working for me there. And bunny.net's single file doesn't work with the wasm files.</p>\n<p>So a couple months ago I decided to try running that stuff on a small server. Initially on <a href=\"https://www.vultr.com/\">Vultr</a> and now <a href=\"https://www.hetzner.com/cloud/\">Hetzner</a>. It's run using <a href=\"https://pm2.io/\">pm2</a>, so that when I push changes to github, the changes are pushed to the server, and apps get restarted. If an app crashes, it should also get automatically restarted.\nIt's then served through <a href=\"https://caddyserver.com/\">caddy</a>, since that seemed easier to setup than nginx.</p>\n<h2>On servers</h2>\n<p>Before I was too afraid to run servers. I was worried about getting hacked, the cost, and having to to server administration etc.</p>\n<p>The final push was Deno Deploy still not supporting workspaces, even after a couple months.</p>\n<p>I then saw a couple tutorials for doing basic hosting and it seemed quite doable to get a basic setup running. Having a couple years of experience with using cli tools and terminals, also helped my confidence there.</p>\n<p>The servers can be rented for relatively cheap, so worst case I loose a little, and maybe have to set up a new server. After doing that 3 times, I feel like I'm kinda confident in doing that for myself :D\n(and I have written instructions for myself, that I mostly can just copy paste into the terminal )</p>\n","date_published":"Mon, 23 Dec 2024 00:00:00 GMT"},{"id":"https://log.thisotherthing.co/posts/2024/diff-updates/","url":"https://log.thisotherthing.co/posts/2024/diff-updates/","title":"diff tool updates","content_html":"<p>Continued on the diff tool.</p>\n<p><img src=\"https://log.thisotherthing.co/project_images/4386918497447570.600\" alt=\"Screenshot of the diffing tool. There are two inputs at the top, labeled &quot;Original&quot; and &quot;Changed&quot;. Then a button labeled diff. Underneath the diff between the original and diffed inputs. Once mixed, and underneath split into original, with the removals, and next to it changed, with the additions in the changed input.\" loading=\"lazy\" decoding=\"async\"></p>\n<p>It's been complicated making things work nicer, and the diffing libraries seem more focused on diffing for version control systems/unit tests, where the focus is more that a line changed. Rather than what gives a nice visual diff.</p>\n<p>I also struggled a lot, with detecting added/removed lines.</p>\n<p>For now I view this still as work in progress. Let's see when I feel up to working on it again :D</p>\n","date_published":"Mon, 23 Dec 2024 00:00:00 GMT"},{"id":"https://log.thisotherthing.co/posts/2024/diff-tool-start/","url":"https://log.thisotherthing.co/posts/2024/diff-tool-start/","title":"started text diffing tool","content_html":"<p>Well, that went fast :D</p>\n<p>I've started on the text diffing tool. I got a basic unified and split differences view working.</p>\n<!--more-->\n<p>It's inspired by <a href=\"https://www.diffchecker.com/\">Diffchecker</a>, but it will run fully locally in the browser, and without any tracking etc</p>\n<p><img src=\"https://log.thisotherthing.co/project_images/7253125513762086.600\" alt=\"Screenshot of the diffing tool. There are two inputs at the top, labeled &quot;Original&quot; and &quot;Changed&quot;. Then a button labeled diff. Underneath the diff between the original and diffed inputs. Once mixed, and underneath split into original, with the removals, and next to it changed, with the additions in the changed input\" loading=\"lazy\" decoding=\"async\"></p>\n<h2>Implementation</h2>\n<p>It's once again using a rust crate, this time the <a href=\"https://crates.io/crates/similar\">similar crate</a>. With some extra splitting, to make line based diffs for the split diff work.</p>\n<p>The wasm calls js functions directly, to insert the text into the targets in the dom.</p>\n<h2>Testing</h2>\n<p>This makes unit testing a bit harder, so I added e2e testing with playwright to the repo.</p>\n<p>I'm not sure how I'll handle the unified diff testing yet (I'll guess very manually). But for the split view, I could check, that the results have the same text as the inputs, and then extra checks, that the diffs are also showing up correctly.</p>\n<h2>Future Plans</h2>\n<p>I want to make the split diffing look way nicer, and maybe add options?\nStarting with nicer looking line numbers, and spacers for removed/added lines, so related lines are also closer together.</p>\n<p>Followed by an optimization pass, as soon as the functionality is there.</p>\n<p>Once I'm happy with how it looks and works, it'll be listed in the tools index page.</p>\n","date_published":"Fri, 20 Dec 2024 00:00:00 GMT"}]}