<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Dibodev]]></title><description><![CDATA[Dibodev]]></description><link>https://dibodev.hashnode.dev</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1593680282896/kNC7E8IR4.png</url><title>Dibodev</title><link>https://dibodev.hashnode.dev</link></image><generator>RSS for Node</generator><lastBuildDate>Wed, 09 Sep 2026 06:47:12 GMT</lastBuildDate><atom:link href="https://dibodev.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[One Vue app, three homes: desktop with Tauri, Android with Capacitor, and the web]]></title><description><![CDATA[I wanted a Reddit client that lived on my desktop and my phone — without maintaining two separate apps. So I built one Vue app and gave it three homes: Tauri (Rust) wraps it as a desktop app for Windo]]></description><link>https://dibodev.hashnode.dev/one-vue-app-three-homes-desktop-with-tauri-android-with-capacitor-and-the-web</link><guid isPermaLink="true">https://dibodev.hashnode.dev/one-vue-app-three-homes-desktop-with-tauri-android-with-capacitor-and-the-web</guid><category><![CDATA[Tauri]]></category><category><![CDATA[Rust]]></category><category><![CDATA[vue]]></category><category><![CDATA[Vue.js]]></category><dc:creator><![CDATA[Léo Guillaume (Dibodev)]]></dc:creator><pubDate>Tue, 18 Aug 2026 23:55:59 GMT</pubDate><content:encoded><![CDATA[<p>I wanted a Reddit client that lived on my desktop <em>and</em> my phone — without maintaining two separate apps. So I built one Vue app and gave it three homes: <strong>Tauri</strong> (Rust) wraps it as a desktop app for Windows/macOS/Linux, <strong>Capacitor</strong> wraps it for Android, and it still runs as a plain web app. One UI, one set of Reddit API calls, one OAuth flow — three targets.</p>
<p>It works. But "just wrap your web app" hides a few things. Here's the reality.</p>
<h2>The shape</h2>
<ul>
<li>A single <strong>Vue.js</strong> frontend: the whole UI, the Reddit API calls, the OAuth.</li>
<li><strong>Tauri</strong> for desktop — the webview is the OS's native one, the backend is Rust.</li>
<li><strong>Capacitor</strong> for Android — desktop got Tauri, Android got Capacitor. Use the wrapper that's mature for each target instead of forcing one tool everywhere.</li>
<li>Plain <strong>web</strong> for anyone who just wants a URL.</li>
</ul>
<h2>Gotcha 1: you can't cross-compile</h2>
<p>You cannot build a Windows <code>.msi</code> on your Mac. Tauri leans on each platform's native toolchain:</p>
<ul>
<li><code>.msi</code> / <code>.exe</code> → built on Windows</li>
<li><code>.dmg</code> / <code>.app</code> → built on macOS</li>
<li><code>.deb</code> / <code>.AppImage</code> → built on Linux</li>
</ul>
<p>"Ship for all platforms" really means a CI matrix with one runner per OS. Plan for it early — it shapes your whole release pipeline.</p>
<h2>Gotcha 2: WebView2 on older Windows</h2>
<p>Tauri doesn't ship a browser engine; it uses the system webview. Windows 11 has WebView2; Windows 7–10 might not — and a missing webview means users open the app to a blank white window. Bundle the WebView2 bootstrapper in your installer so it self-installs. Tiny detail, terrible first impression if you miss it.</p>
<h2>Gotcha 3: OAuth redirect is the fiddly part</h2>
<p>In a browser, OAuth is easy: Reddit redirects to your callback URL, you read the code, done. In a <em>packaged</em> app there's no web server at that URL. So you register a custom scheme / deep link, let the OS hand the redirect back to the app, and pull the <code>code</code> out of the incoming URL — on desktop and Android, each with its own quirks. Easily the most time-consuming part, and the bit tutorials skip.</p>
<h2>Gotcha 4: a default Rust release binary is chunky</h2>
<p>A handful of <code>Cargo.toml</code> flags make a real dent:</p>
<pre><code class="language-toml">[profile.release]
panic = "abort"      # drop the unwinding machinery
codegen-units = 1    # compile as one unit so the optimizer can do more
lto = true           # link-time optimization
opt-level = "s"      # optimize for size
strip = true         # strip symbols from the binary
</code></pre>
<p>Free size win, no code changes.</p>
<h2>Was it worth it?</h2>
<p>Yes. One Vue codebase, native apps on desktop and Android, plus a web version — without three projects to keep in sync. If you're a web dev eyeing desktop/mobile, Vue + Tauri + Capacitor is a very reasonable way in — just budget time for the packaging and OAuth reality, not the happy-path demo.</p>
<p>I do this kind of cross-platform / custom-tooling work as a freelance dev at <a href="https://dibodev.fr">dibodev.fr</a>. Happy to get into the OAuth deep-linking or the CI matrix in the comments.</p>
<p><em>Originally published on <a href="https://dev.to/dibodev/one-vue-app-three-homes-desktop-with-tauri-android-with-capacitor-and-the-web-4eoe">DEV</a>.</em></p>
]]></content:encoded></item><item><title><![CDATA[My static Nuxt blog publishes itself: drafts, scheduled "drip", and auto-rebuilds]]></title><description><![CDATA[I run my freelance site as a fully static (SSG) Nuxt site. It's fast, cheap to host, and boring in the good way. But static sites are genuinely bad at one thing: publishing an article next Tuesday at ]]></description><link>https://dibodev.hashnode.dev/my-static-nuxt-blog-publishes-itself-drafts-scheduled-drip-and-auto-rebuilds</link><guid isPermaLink="true">https://dibodev.hashnode.dev/my-static-nuxt-blog-publishes-itself-drafts-scheduled-drip-and-auto-rebuilds</guid><category><![CDATA[Nuxt]]></category><category><![CDATA[vue]]></category><category><![CDATA[webdev]]></category><category><![CDATA[JavaScript]]></category><dc:creator><![CDATA[Léo Guillaume (Dibodev)]]></dc:creator><pubDate>Tue, 18 Aug 2026 23:47:41 GMT</pubDate><content:encoded><![CDATA[<p>I run my freelance site as a fully static (SSG) Nuxt site. It's fast, cheap to host, and boring in the good way. But static sites are genuinely bad at one thing: publishing an article next Tuesday at 9am. There's no server rendering each request, so "schedule this post" doesn't exist — something has to rebuild the site at the right moment.</p>
<p>I also write in bursts. Some evenings I'll draft two or three articles at once, and I don't want them all going live the same day. So I bolted a small publishing engine onto my existing stack: drafts, a scheduled queue (a "drip"), and automatic rebuilds. Here's how the pieces fit.</p>
<h2>The stack</h2>
<ul>
<li>Nuxt 4, prerendered (SSG) for every public page</li>
<li>Storyblok as the headless CMS — the source of truth for content</li>
<li>A Nitro server (the same Nuxt app) on a small VPS under PM2 — the always-on part</li>
<li>GitHub Actions for the deploy/rebuild</li>
</ul>
<p>The whole thing rests on one realization: on an SSG site, "publish" is two actions, not one — (1) put the content in the CMS, and (2) rebuild the static site so it actually shows up. Scheduling is just doing both later, automatically.</p>
<h2>1. Drafts: write now, decide later</h2>
<p>The dashboard has a markdown editor. Drafts are saved server-side (Nitro's storage layer), each with a status (draft / scheduled / published) and a publish date. Nothing touches the live site yet — a draft is just a row waiting for its moment.</p>
<h2>2. The queue and the "drip"</h2>
<p>The fun part. A Nitro scheduled task runs every hour and asks one question: <em>is anything due?</em></p>
<pre><code class="language-ts">// nuxt.config.ts
export default defineNuxtConfig({
  nitro: {
    experimental: { tasks: true },
    scheduledTasks: {
      '5 * * * *': ['articles:process-queue'], // every hour, at :05
    },
  },
})
</code></pre>
<pre><code class="language-ts">// server/tasks/articles/process-queue.ts
export default defineTask({
  meta: { name: 'articles:process-queue' },
  async run() {
    const due = await getDueScheduledArticles() // publishDate &lt;= now
    if (!due.length) return { result: 'nothing due' }

    for (const article of due) {
      await publishToStoryblok(article) // markdown -&gt; richtext -&gt; CMS
    }
    await triggerSiteRebuild() // ONE rebuild for the whole batch
    return { result: `published ${due.length}` }
  },
})
</code></pre>
<p>That's the entire scheduler. No queue service, no cron box, no extra infra — just a task the Nitro server already knows how to run.</p>
<h2>3. Markdown → CMS → rebuild</h2>
<p>When a post is due, it's converted from markdown to Storyblok's richtext format, pushed to the CMS, and then — the non-negotiable step on a static site — the site is rebuilt. No rebuild, no article. I trigger it with a <code>workflow_dispatch</code> call to my deploy workflow:</p>
<pre><code class="language-ts">async function triggerSiteRebuild() {
  await $fetch(
    'https://api.github.com/repos/&lt;owner&gt;/&lt;repo&gt;/actions/workflows/deploy.yml/dispatches',
    {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${process.env.REPO_ACCESS_TOKEN}`, // needs `actions: write`
        Accept: 'application/vnd.github+json',
      },
      body: { ref: 'main' },
    },
  )
}
</code></pre>
<h2>Three gotchas I hit</h2>
<ul>
<li><strong>There's still a server.</strong> The scheduled task only fires if something is actually running. It's easy to forget an SSG site has no always-on backend by default — the Nitro server under PM2 exists purely so the scheduler (and the admin) stay alive.</li>
<li><strong>One rebuild per batch, not per article.</strong> If three posts are due in the same run, publish all three <em>then</em> rebuild once. Otherwise you kick off three deploys back to back for nothing.</li>
<li><strong>Timezones.</strong> "9am" means nothing until you pin the timezone. Ask me how I know.</li>
</ul>
<h2>The payoff</h2>
<p>Now I can draft on a Sunday, spread posts across the next two weeks, and close the laptop. They go live on their own, the site rebuilds itself, and I stop babysitting a "publish" button.</p>
<p>It powers the blog on my freelance site, <a href="https://dibodev.fr">dibodev.fr</a> — building this kind of custom tooling (and business apps / SaaS) for small companies is basically my day job.</p>
<p><em>Originally published on <a href="https://dev.to/dibodev/my-static-nuxt-blog-publishes-itself-drafts-scheduled-drip-and-auto-rebuilds-4hmi">DEV</a>.</em></p>
]]></content:encoded></item></channel></rss>