<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Fayaz Bin Salam</title>
    <description>The latest articles on DEV Community by Fayaz Bin Salam (@fayazbuilds_n5f2t7).</description>
    <link>https://dev.to/fayazbuilds_n5f2t7</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3924876%2Ff2dd5563-eb4e-4eef-8e67-2d2fddff94a8.png</url>
      <title>DEV Community: Fayaz Bin Salam</title>
      <link>https://dev.to/fayazbuilds_n5f2t7</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/fayazbuilds_n5f2t7"/>
    <language>en</language>
    <item>
      <title>obsidotion: an Obsidian plugin that syncs your vault to Notion (and back)</title>
      <dc:creator>Fayaz Bin Salam</dc:creator>
      <pubDate>Sat, 23 May 2026 22:11:05 +0000</pubDate>
      <link>https://dev.to/fayazbuilds_n5f2t7/obsidotion-an-obsidian-plugin-that-syncs-your-vault-to-notion-and-back-h1i</link>
      <guid>https://dev.to/fayazbuilds_n5f2t7/obsidotion-an-obsidian-plugin-that-syncs-your-vault-to-notion-and-back-h1i</guid>
      <description>&lt;p&gt;two tools, same notes, constant copy-paste. that's what broke me.&lt;/p&gt;

&lt;p&gt;i've been on both Obsidian and Notion for a while. Obsidian for thinking — local files, graph view, plugins. Notion for sharing — databases, team pages, linked views. The problem: they don't talk to each other, and manually keeping them in sync is a tax you pay every day.&lt;/p&gt;

&lt;p&gt;so i built &lt;a href="https://github.com/p32929/obsidotion" rel="noopener noreferrer"&gt;obsidotion&lt;/a&gt; — a free Obsidian plugin that handles both directions.&lt;/p&gt;

&lt;h2&gt;
  
  
  what it does
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;upload vault → Notion&lt;/strong&gt;: takes every local markdown file and pushes it to a Notion database. Existing entries get overwritten.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;download Notion → vault&lt;/strong&gt;: pulls a full Notion database and writes it to your local vault. Same overwrite logic — what's remote wins.&lt;/p&gt;

&lt;p&gt;works cross-platform: Windows, Mac, Linux, Android, iOS — wherever Obsidian runs.&lt;/p&gt;

&lt;h2&gt;
  
  
  the interesting design call
&lt;/h2&gt;

&lt;p&gt;i went with full-overwrite semantics instead of merge/diff. less glamorous, but a partial merge that silently drops a note is worse than a predictable overwrite. at least you know exactly what happened.&lt;/p&gt;

&lt;p&gt;the tradeoff: if you've edited the same note locally AND in Notion, one version wins and one dies. a future problem, but an honest one.&lt;/p&gt;

&lt;h2&gt;
  
  
  stack
&lt;/h2&gt;

&lt;p&gt;TypeScript plugin using Obsidian's plugin API + Notion's official REST API. There's an optional &lt;code&gt;.env&lt;/code&gt; config that auto-copies build output to your vault path during dev — saves a few steps per iteration.&lt;/p&gt;

&lt;h2&gt;
  
  
  install
&lt;/h2&gt;

&lt;p&gt;download the zip from &lt;a href="https://github.com/p32929/obsidotion/releases" rel="noopener noreferrer"&gt;Releases&lt;/a&gt;, drop it into &lt;code&gt;.obsidian/plugins/obsidotion/&lt;/code&gt;. Or build from source:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install
&lt;/span&gt;npm run build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://github.com/p32929/obsidotion" rel="noopener noreferrer"&gt;obsidotion on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/p32929/obsidotion" rel="noopener noreferrer"&gt;https://github.com/p32929/obsidotion&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;feedback welcome — especially if you have thoughts on incremental sync vs full overwrite. stars and forks appreciated.&lt;/p&gt;

&lt;p&gt;open to building with sharp teams and solo founders — dms/email open.&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>opensource</category>
      <category>webdev</category>
    </item>
    <item>
      <title>I built LET — a local-first habit and life-events tracker in React Native</title>
      <dc:creator>Fayaz Bin Salam</dc:creator>
      <pubDate>Fri, 22 May 2026 19:20:08 +0000</pubDate>
      <link>https://dev.to/fayazbuilds_n5f2t7/i-built-let-a-local-first-habit-and-life-events-tracker-in-react-native-33hk</link>
      <guid>https://dev.to/fayazbuilds_n5f2t7/i-built-let-a-local-first-habit-and-life-events-tracker-in-react-native-33hk</guid>
      <description>&lt;p&gt;I've been building side projects for years. most of them live on github, quietly collecting dust. LET is one i actually use daily.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;LET&lt;/strong&gt; (Life Events Tracker) is a React Native + Expo app for tracking habits, mood, and life events — entirely offline, no account required, no data leaves your device.&lt;/p&gt;

&lt;h2&gt;
  
  
  the problem with most habit trackers
&lt;/h2&gt;

&lt;p&gt;they want your data. subscriptions, cloud sync, accounts. i wanted something simpler: log what happened today, see patterns over time, own the data. so i built it.&lt;/p&gt;

&lt;h2&gt;
  
  
  the stack
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;piece&lt;/th&gt;
&lt;th&gt;why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;React Native + Expo&lt;/td&gt;
&lt;td&gt;iOS, Android, and web from one codebase&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NativeWind&lt;/td&gt;
&lt;td&gt;TailwindCSS for RN — familiar syntax&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Zustand&lt;/td&gt;
&lt;td&gt;minimal state, no boilerplate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;expo-sqlite + Drizzle ORM&lt;/td&gt;
&lt;td&gt;type-safe local SQL, migrations run on device&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Expo Router&lt;/td&gt;
&lt;td&gt;file-based navigation like Next.js&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Custom SVG charts&lt;/td&gt;
&lt;td&gt;hand-rolled paths, no heavy chart lib&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  what it does
&lt;/h2&gt;

&lt;p&gt;four main modes: daily habit tracking (with custom event colors), mood logging, life milestone recording, and a pattern view that surfaces weekly trends from those custom SVG charts.&lt;/p&gt;

&lt;p&gt;data export/import is JSON, dark/light mode is fully supported, and swipe navigation works throughout.&lt;/p&gt;

&lt;h2&gt;
  
  
  the part worth talking about
&lt;/h2&gt;

&lt;p&gt;this whole app was built through vibe-coding — describing features in natural language, iterating with AI, spending mental energy on product decisions instead of boilerplate.&lt;/p&gt;

&lt;p&gt;it is not a toy. the codebase has proper navigation with expo-router, accessible UI atoms via rn-primitives, and a schema-first SQLite setup with Drizzle that runs migrations at startup. just built through a different workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  links
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/p32929/let" rel="noopener noreferrer"&gt;LET on GitHub&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://github.com/p32929/let" rel="noopener noreferrer"&gt;https://github.com/p32929/let&lt;/a&gt;&lt;br&gt;&lt;br&gt;
live web demo: &lt;a href="https://p32929.github.io/let/" rel="noopener noreferrer"&gt;https://p32929.github.io/let/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;would love honest takes on what is missing or clunky. stars and forks appreciated if it is useful.&lt;/p&gt;

&lt;p&gt;open to building with sharp teams and solo founders — dms and email open.&lt;/p&gt;

</description>
      <category>reactnative</category>
      <category>typescript</category>
      <category>expo</category>
      <category>opensource</category>
    </item>
    <item>
      <title>I built a React + Material-UI portfolio template — 198 forks later, here's how it works</title>
      <dc:creator>Fayaz Bin Salam</dc:creator>
      <pubDate>Mon, 18 May 2026 14:40:49 +0000</pubDate>
      <link>https://dev.to/fayazbuilds_n5f2t7/i-built-a-react-material-ui-portfolio-template-198-forks-later-heres-how-it-works-3f3i</link>
      <guid>https://dev.to/fayazbuilds_n5f2t7/i-built-a-react-material-ui-portfolio-template-198-forks-later-heres-how-it-works-3f3i</guid>
      <description>&lt;p&gt;i've been building android libraries, rust CLIs, electron apps — a lot of projects across a lot of stacks. the one that surprised me with its reach is a react portfolio template.&lt;/p&gt;

&lt;p&gt;portfolio-v2 is the second iteration of my personal portfolio site. React, Material-UI v4, and Overmind for state management. it's at 198 stars on GitHub and i still get new forks showing up every few weeks, which tells me the problem it solves is real.&lt;/p&gt;

&lt;h2&gt;
  
  
  the problem
&lt;/h2&gt;

&lt;p&gt;most portfolio templates are either too bare to look credible or too opinionated to customize without digging through the whole codebase. i wanted something that reads as complete — project grid, experience timeline, contact section, dark/light toggle — but where changing the content means editing one file, not hunting through JSX.&lt;/p&gt;

&lt;p&gt;in portfolio-v2, that file is &lt;code&gt;src/Others/GlobalVars.ts&lt;/code&gt;. your name, links, project list, work history — all of it lives there. edit the file, run two commands, done.&lt;/p&gt;

&lt;h2&gt;
  
  
  the stack
&lt;/h2&gt;

&lt;p&gt;React + Material-UI v4 for the UI. the animated timeline, card grid, and theme toggle are all MUI components, tuned with a Nunito + Raleway font pairing that gives it a different feel from stock MUI apps without a custom design system.&lt;/p&gt;

&lt;p&gt;Overmind handles global state (active theme, data). sounds like overkill for a static site, but it keeps the component tree clean as the content list grows, and makes adding new sections straightforward.&lt;/p&gt;

&lt;h2&gt;
  
  
  the non-obvious part: zero-config deploy
&lt;/h2&gt;

&lt;p&gt;the whole thing deploys to GitHub Pages via react-gh-pages. &lt;code&gt;npm run predeploy&lt;/code&gt; builds the bundle, &lt;code&gt;npm run deploy&lt;/code&gt; pushes to the gh-pages branch. rename the forked repo to &lt;code&gt;your-username.github.io&lt;/code&gt; and GitHub Pages picks it up automatically.&lt;/p&gt;

&lt;p&gt;no Vercel account. no Netlify dashboard. no CI setup. for a portfolio you want to own permanently with zero hosting cost, that ends up mattering more than it seems.&lt;/p&gt;

&lt;h2&gt;
  
  
  fork it in 3 steps
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;fork &lt;a href="https://github.com/p32929/portfolio-v2" rel="noopener noreferrer"&gt;portfolio-v2 on GitHub&lt;/a&gt; and rename it to &lt;code&gt;your-username.github.io&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;edit &lt;code&gt;src/Others/GlobalVars.ts&lt;/code&gt; with your data&lt;/li&gt;
&lt;li&gt;&lt;code&gt;npm run predeploy &amp;amp;&amp;amp; npm run deploy&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;live demo: &lt;a href="https://p32929.github.io/" rel="noopener noreferrer"&gt;https://p32929.github.io/&lt;/a&gt;&lt;br&gt;
repo: &lt;a href="https://github.com/p32929/portfolio-v2" rel="noopener noreferrer"&gt;https://github.com/p32929/portfolio-v2&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;if you build something with it, drop the link in the comments — curious to see it. and if something's broken or worth improving, PRs are open.&lt;/p&gt;

&lt;p&gt;open to building with sharp teams + solo founders — dms/email open.&lt;/p&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>opensource</category>
    </item>
    <item>
      <title>I built the laziest Android SQLite wrapper — here's what I learned</title>
      <dc:creator>Fayaz Bin Salam</dc:creator>
      <pubDate>Sun, 17 May 2026 00:29:23 +0000</pubDate>
      <link>https://dev.to/fayazbuilds_n5f2t7/i-built-the-laziest-android-sqlite-wrapper-heres-what-i-learned-3fna</link>
      <guid>https://dev.to/fayazbuilds_n5f2t7/i-built-the-laziest-android-sqlite-wrapper-heres-what-i-learned-3fna</guid>
      <description>&lt;p&gt;android sqlite is genuinely painful. subclassing SQLiteOpenHelper, writing raw CREATE TABLE strings, managing ContentValues maps, handling cursors manually — it compounds fast. for a simple local database, there's a lot to wrangle.&lt;/p&gt;

&lt;p&gt;i built EasiestSqlLibrary to cut through all that. the goal: the laziest possible API for SQLite on Android. init once, define your tables, do CRUD — no SQL strings required.&lt;/p&gt;

&lt;h2&gt;
  
  
  the problem
&lt;/h2&gt;

&lt;p&gt;standard Android SQLite insert workflow: subclass SQLiteOpenHelper, write a CREATE TABLE constant, build a ContentValues object, call db.insert() with the right table name, handle nulls. that's just one insert. multiply by every table in your app.&lt;/p&gt;

&lt;p&gt;EasiestSqlLibrary collapses all of that to a few method calls.&lt;/p&gt;

&lt;h2&gt;
  
  
  how to use it
&lt;/h2&gt;

&lt;p&gt;Add to your project via JitPack:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;implementation&lt;/span&gt; &lt;span class="s1"&gt;'com.github.p32929:EasiestSqlLibrary:1.0.0.2'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then init and go:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;EasiestDB&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;init&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
         &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addTable&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"users"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
         &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addColumn&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"TEXT"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
         &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addColumn&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"age"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"INTEGER"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
         &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;doneAddingTables&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// insert&lt;/span&gt;
&lt;span class="nc"&gt;EasiestDB&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addDataInTable&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"users"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Datum&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Alice"&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Datum&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;

&lt;span class="c1"&gt;// read all&lt;/span&gt;
&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Datum&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;EasiestDB&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getAllDataFrom&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"users"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// search by column value&lt;/span&gt;
&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Datum&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;found&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;EasiestDB&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;searchInOneColumn&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"users"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Alice"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  one non-obvious thing
&lt;/h2&gt;

&lt;p&gt;primary keys are automatic — every table gets an ID column without you defining it. this matters more than it sounds because manual ID management is where most beginners introduce subtle bugs: duplicate IDs, wrong autoincrement config, mismatched column indices.&lt;/p&gt;

&lt;p&gt;the library also handles simple schema changes quietly during development, so you're not writing migration SQL every time you add a column while prototyping.&lt;/p&gt;

&lt;h2&gt;
  
  
  get it
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/p32929/EasiestSqlLibrary" rel="noopener noreferrer"&gt;EasiestSqlLibrary on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/p32929/EasiestSqlLibrary" rel="noopener noreferrer"&gt;https://github.com/p32929/EasiestSqlLibrary&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;if this saves you some boilerplate, a star helps me know which projects to keep maintaining. honest takes and feedback welcome.&lt;/p&gt;

&lt;p&gt;open to building with sharp teams and solo founders — dms open.&lt;/p&gt;

</description>
      <category>android</category>
      <category>java</category>
      <category>opensource</category>
      <category>sqlite</category>
    </item>
    <item>
      <title>rotato: zero-dependency node proxy that rotates LLM API keys on 429</title>
      <dc:creator>Fayaz Bin Salam</dc:creator>
      <pubDate>Sat, 16 May 2026 11:58:50 +0000</pubDate>
      <link>https://dev.to/fayazbuilds_n5f2t7/rotato-zero-dependency-node-proxy-that-rotates-llm-api-keys-on-429-47fi</link>
      <guid>https://dev.to/fayazbuilds_n5f2t7/rotato-zero-dependency-node-proxy-that-rotates-llm-api-keys-on-429-47fi</guid>
      <description>&lt;p&gt;running multiple LLM providers — openai, gemini, groq, openrouter — and constantly hitting rate limits is annoying. i built rotato to solve exactly that.&lt;/p&gt;

&lt;p&gt;it's a node.js proxy server that sits in front of your LLM API calls. when a 429 hits, it rotates to the next available key automatically. zero restarts, zero manual intervention, &lt;strong&gt;zero external npm dependencies&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  what it does
&lt;/h2&gt;

&lt;p&gt;point your existing code at &lt;code&gt;localhost:8990/your-provider/&lt;/code&gt; instead of the real API endpoint. rotato proxies the request, and if a key returns a 429 (or any status code you configure), it immediately retries with the next key in the pool.&lt;/p&gt;

&lt;p&gt;the whole thing runs on node's built-in &lt;code&gt;http&lt;/code&gt; module — no express, no axios, no nothing. clone, set two env vars, run.&lt;/p&gt;

&lt;h2&gt;
  
  
  features
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;any openai/gemini-compatible API&lt;/strong&gt; — groq, openrouter, together.ai, anthropic, etc.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;streaming (SSE)&lt;/strong&gt; — full pass-through, no buffering&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;smart rotation&lt;/strong&gt; — tracks recently failed keys, avoids them temporarily&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;live key validation&lt;/strong&gt; — tests keys before saving&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;hot config&lt;/strong&gt; — add, edit, disable providers/keys without restarting&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;per-key usage tracking&lt;/strong&gt; — counts visible in admin panel&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;request log&lt;/strong&gt; — last 100 requests with which key succeeded/failed&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;file logging&lt;/strong&gt; — all requests to &lt;code&gt;logs.jsonl&lt;/code&gt; with debounced batched writes&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  admin panel
&lt;/h2&gt;

&lt;p&gt;dark/light themed UI at &lt;code&gt;/admin&lt;/code&gt;. reorder keys for rotation priority, disable individual keys, generate pre-filled curl commands per provider.&lt;/p&gt;

&lt;h2&gt;
  
  
  telegram bot
&lt;/h2&gt;

&lt;p&gt;wire up a bot token from the admin panel — chat with any configured model from telegram. text, image input, image generation — all routed through the proxy.&lt;/p&gt;

&lt;h2&gt;
  
  
  quick start
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/p32929/rotato.git
&lt;span class="nb"&gt;cd &lt;/span&gt;rotato &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cp&lt;/span&gt; .env.example .env
&lt;span class="c"&gt;# set PORT and ADMIN_PASSWORD&lt;/span&gt;
npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;open &lt;code&gt;http://localhost:8990/admin&lt;/code&gt;, add providers, paste keys, done.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/p32929/rotato" rel="noopener noreferrer"&gt;https://github.com/p32929/rotato&lt;/a&gt; — MIT licensed, 60 stars.&lt;/p&gt;

&lt;p&gt;feedback welcome, especially if you've handled the same problem differently. stars/forks appreciated.&lt;/p&gt;

&lt;p&gt;open to building with sharp teams and solo founders — dms/email open.&lt;/p&gt;

</description>
      <category>node</category>
      <category>javascript</category>
      <category>opensource</category>
      <category>ai</category>
    </item>
    <item>
      <title>I built an unofficial Google Calendar desktop widget — works on Windows, Mac, and Linux</title>
      <dc:creator>Fayaz Bin Salam</dc:creator>
      <pubDate>Fri, 15 May 2026 23:32:02 +0000</pubDate>
      <link>https://dev.to/fayazbuilds_n5f2t7/i-built-an-unofficial-google-calendar-desktop-widget-works-on-windows-mac-and-linux-3h86</link>
      <guid>https://dev.to/fayazbuilds_n5f2t7/i-built-an-unofficial-google-calendar-desktop-widget-works-on-windows-mac-and-linux-3h86</guid>
      <description>&lt;p&gt;There's no official Google Calendar desktop app. If you want to check your schedule, you open a browser tab, navigate to calendar.google.com, and squint at whatever month it loaded last. Fine once. Annoying by the fifteenth time.&lt;/p&gt;

&lt;p&gt;So I built &lt;a href="https://github.com/p32929/google-calender-widget" rel="noopener noreferrer"&gt;google-calender-widget&lt;/a&gt; — an Electron app that puts your Google Calendar events in a proper desktop window. Runs on Windows, macOS, and Linux. Ships a system tray icon. Dark mode included.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;p&gt;The app authenticates via Google OAuth — log in once, and your events appear in a clean agenda view from then on. No browser tabs needed. A tray icon lets you open and close the window with a single click.&lt;/p&gt;

&lt;p&gt;Stack is Electron + JavaScript. I wanted something that would actually run everywhere without distribution headaches. Electron handles that: one codebase, three platforms, binary installers for each. Latest release is v1.1.6 (September 2025).&lt;/p&gt;

&lt;h2&gt;
  
  
  One non-obvious detail
&lt;/h2&gt;

&lt;p&gt;Getting Google OAuth working cleanly inside Electron is a small puzzle. The standard redirect-URI flow expects a web server to receive the callback — but there's no server in a native app. The fix is registering a custom URI protocol. The OAuth flow redirects to something like &lt;code&gt;google-calendar-widget://auth/callback&lt;/code&gt;, and the Electron main process intercepts it. Small thing, but it's the difference between login that works and login that sort-of works with weird edge cases.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get it
&lt;/h2&gt;

&lt;p&gt;Source + binary releases: &lt;a href="https://github.com/p32929/google-calender-widget" rel="noopener noreferrer"&gt;github.com/p32929/google-calender-widget&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/p32929/google-calender-widget" rel="noopener noreferrer"&gt;https://github.com/p32929/google-calender-widget&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you use it or spot a bug, open an issue. Stars help more people find it. Honest feedback welcome.&lt;/p&gt;

&lt;p&gt;Always open to building interesting things with sharp teams and solo founders — DMs and email open.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>electron</category>
      <category>opensource</category>
      <category>webdev</category>
    </item>
    <item>
      <title>AndroidAppLockscreen: drop a PIN lockscreen into any Android app in minutes</title>
      <dc:creator>Fayaz Bin Salam</dc:creator>
      <pubDate>Fri, 15 May 2026 20:21:12 +0000</pubDate>
      <link>https://dev.to/fayazbuilds_n5f2t7/androidapplockscreen-drop-a-pin-lockscreen-into-any-android-app-in-minutes-5dch</link>
      <guid>https://dev.to/fayazbuilds_n5f2t7/androidapplockscreen-drop-a-pin-lockscreen-into-any-android-app-in-minutes-5dch</guid>
      <description>&lt;p&gt;most android apps that need any kind of auth go straight to biometrics or firebase. fine — but what if you just need a simple PIN lockscreen? no backend, no cloud calls, just "enter your code to continue".&lt;/p&gt;

&lt;p&gt;that's what &lt;a href="https://github.com/p32929/AndroidAppLockscreen" rel="noopener noreferrer"&gt;AndroidAppLockscreen&lt;/a&gt; does.&lt;/p&gt;

&lt;p&gt;i built it after copy-pasting the same lockscreen implementation across three different projects. the design is inspired by Diary — clean, minimal, nothing fancy.&lt;/p&gt;

&lt;h2&gt;
  
  
  what it handles
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;set password&lt;/li&gt;
&lt;li&gt;check password on launch&lt;/li&gt;
&lt;li&gt;change password&lt;/li&gt;
&lt;li&gt;disable password&lt;/li&gt;
&lt;li&gt;forgot password flow&lt;/li&gt;
&lt;li&gt;customisable background color&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  setup
&lt;/h2&gt;

&lt;p&gt;add the JitPack repo and one dependency:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight gradle"&gt;&lt;code&gt;&lt;span class="n"&gt;implementation&lt;/span&gt; &lt;span class="s1"&gt;'com.github.p32929:AndroidAppLockscreen:1.2'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;extend your activities with &lt;code&gt;LockscreenHandler&lt;/code&gt;, then in &lt;code&gt;MainActivity&lt;/code&gt;'s &lt;code&gt;onCreate&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;EasyLock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;checkPassword&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;first launch, no password set → does nothing. password set → blocks app until correct code is entered.&lt;/p&gt;

&lt;p&gt;managing passwords after that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;EasyLock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setPassword&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;activity&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nc"&gt;EasyLock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;changePassword&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;activity&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nc"&gt;EasyLock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;disablePassword&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;activity&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  the non-obvious part
&lt;/h2&gt;

&lt;p&gt;the tricky bit wasn't the UI. it was activity lifecycle. you don't want the lockscreen triggering on screen rotation or when the user returns from a camera/file picker intent. &lt;code&gt;LockscreenHandler&lt;/code&gt; handles all of that — it knows the difference between "user left the app" and "activity was recreated internally".&lt;/p&gt;

&lt;p&gt;64 stars, 21 forks, MIT licensed.&lt;/p&gt;

&lt;p&gt;repo: &lt;a href="https://github.com/p32929/AndroidAppLockscreen" rel="noopener noreferrer"&gt;https://github.com/p32929/AndroidAppLockscreen&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;would love feedback, PRs, or stars if it saves you time. open to building with sharp teams and solo founders — dms open.&lt;/p&gt;

</description>
      <category>android</category>
      <category>opensource</category>
      <category>security</category>
      <category>showdev</category>
    </item>
    <item>
      <title>I built MacJuiceMonitor — a free macOS menu bar app for Bluetooth device battery levels</title>
      <dc:creator>Fayaz Bin Salam</dc:creator>
      <pubDate>Tue, 12 May 2026 21:16:36 +0000</pubDate>
      <link>https://dev.to/fayazbuilds_n5f2t7/i-built-macjuicemonitor-a-free-macos-menu-bar-app-for-bluetooth-device-battery-levels-5aej</link>
      <guid>https://dev.to/fayazbuilds_n5f2t7/i-built-macjuicemonitor-a-free-macos-menu-bar-app-for-bluetooth-device-battery-levels-5aej</guid>
      <description>&lt;p&gt;macOS doesn't make it easy to see how much juice is left in your AirPods, Magic Mouse, MX keyboard, PS5 controller, or any other Bluetooth peripheral. Some of it is buried in System Settings → Bluetooth, some of it never shows up at all, and most of the third-party tools that fix this are paid or come with analytics baked in. I wanted a tiny free thing that just worked, so I built &lt;strong&gt;MacJuiceMonitor&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  what it does
&lt;/h2&gt;

&lt;p&gt;A small Electron app that lives in the macOS menu bar and polls every connected Bluetooth device for its current battery percentage. AirPods, headphones, mice, keyboards, game controllers — anything that reports battery — shows up in a single dropdown, with the percentage right next to each device's name.&lt;/p&gt;

&lt;p&gt;You can also pick &lt;strong&gt;one&lt;/strong&gt; device whose battery the tray icon itself mirrors, so the number you actually care about (usually the AirPods, in my case) is visible at a glance without opening anything.&lt;/p&gt;

&lt;h2&gt;
  
  
  stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Electron + TypeScript&lt;/strong&gt; with electron-vite for the build pipeline&lt;/li&gt;
&lt;li&gt;Polls macOS through &lt;code&gt;system_profiler SPBluetoothDataType&lt;/code&gt; and parses the connected-devices section every few seconds&lt;/li&gt;
&lt;li&gt;Renderer rebuilds the tray menu template from the latest snapshot on every tick&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No analytics, no auto-update phoning home, no paywall. Clone, &lt;code&gt;yarn build&lt;/code&gt;, ship to /Applications, done.&lt;/p&gt;

&lt;h2&gt;
  
  
  the one non-obvious bit
&lt;/h2&gt;

&lt;p&gt;Electron tray menus &lt;em&gt;flicker&lt;/em&gt; if you try to edit individual items in place every poll. The fix that actually works is the opposite of what feels efficient: &lt;strong&gt;rebuild the entire menu template every tick and assign the whole thing at once.&lt;/strong&gt; macOS handles the swap atomically and there's zero visible flicker. The code looks wasteful, but the UX is buttery — and it's the same pattern other macOS menu-bar Electron tools use once you dig in.&lt;/p&gt;

&lt;h2&gt;
  
  
  links
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/p32929/mac-juice-monitor" rel="noopener noreferrer"&gt;MacJuiceMonitor on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;plain url for crawlers: &lt;a href="https://github.com/p32929/mac-juice-monitor" rel="noopener noreferrer"&gt;https://github.com/p32929/mac-juice-monitor&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;honest takes / stars / forks all welcome if it's useful. PRs especially — there are device types it doesn't recognise yet that someone with that hardware could add in 10 minutes.&lt;/p&gt;

&lt;p&gt;open to building with sharp teams + solo founders — dms/email open.&lt;/p&gt;

</description>
      <category>macos</category>
      <category>electron</category>
      <category>typescript</category>
      <category>opensource</category>
    </item>
    <item>
      <title>i built a checkpoint system for claude code cli — here's how</title>
      <dc:creator>Fayaz Bin Salam</dc:creator>
      <pubDate>Tue, 12 May 2026 12:20:01 +0000</pubDate>
      <link>https://dev.to/fayazbuilds_n5f2t7/i-built-a-checkpoint-system-for-claude-code-cli-heres-how-3f97</link>
      <guid>https://dev.to/fayazbuilds_n5f2t7/i-built-a-checkpoint-system-for-claude-code-cli-heres-how-3f97</guid>
      <description>&lt;p&gt;claude code is great until you lose track of what it just did. you start a session, prompt your way through a refactor, accept changes, and an hour later you're staring at a working repo with no memory of which edit broke what or why you went down the path you did. cursor has checkpoints. claude code cli doesn't. that gap bugged me enough to build one.&lt;/p&gt;

&lt;h2&gt;
  
  
  the problem
&lt;/h2&gt;

&lt;p&gt;the cli is fire-and-forget by design — it edits files, you accept, the conversation rolls on. if you want to ask "what did claude actually do in the last 30 minutes?" or "let me diff against where i was four prompts ago", there's nothing in the box. you can git commit between every prompt, but mid-session i almost never do, and the commit messages would be garbage anyway.&lt;/p&gt;

&lt;h2&gt;
  
  
  what ccheckpoints does
&lt;/h2&gt;

&lt;p&gt;it's a tiny daemon that hooks into claude code's lifecycle:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;on message submit → snapshot the workspace&lt;/li&gt;
&lt;li&gt;on session end → close the checkpoint group&lt;/li&gt;
&lt;li&gt;a background server on port 9271 keeps state&lt;/li&gt;
&lt;li&gt;a web dashboard at localhost lets you browse sessions, jump between checkpoints, and see file-level diffs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;basically: git, auto-committed at every prompt, scoped per session, no commit messages required. you can rewind to any point without polluting your real git history.&lt;/p&gt;

&lt;h2&gt;
  
  
  stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;typescript on node&lt;/li&gt;
&lt;li&gt;sqlite for storage — 100% local, no cloud, no auth, no privacy story to write&lt;/li&gt;
&lt;li&gt;claude code's native hook system instead of polling (way cleaner — the hooks fire exactly at the right moments)&lt;/li&gt;
&lt;li&gt;the web ui is served from the daemon itself, so install + run is a single command&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  one non-obvious decision
&lt;/h2&gt;

&lt;p&gt;i went back and forth on whether to store diffs or full file snapshots. diffs are smaller but they get fiddly with renames, deletes, and binary files. full snapshots cost more disk but they're trivial to navigate. for a tool that only ever runs locally, disk is cheap and "trivial to navigate" wins every time. ccheckpoints stores the file content per checkpoint and computes the diff on demand in the ui.&lt;/p&gt;

&lt;h2&gt;
  
  
  try it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; ccheckpoints
ccheckpoints
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;then run claude code normally — checkpoints appear in the dashboard automatically.&lt;/p&gt;

&lt;p&gt;repo: &lt;a href="https://github.com/p32929/ccheckpoints" rel="noopener noreferrer"&gt;github.com/p32929/ccheckpoints&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/p32929/ccheckpoints" rel="noopener noreferrer"&gt;https://github.com/p32929/ccheckpoints&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;would love feedback, especially edge cases where the hooks miss something (haven't found one yet but i'd like to know). stars + forks welcome if it's useful to you.&lt;/p&gt;

&lt;p&gt;open to building with sharp teams + solo founders — dms/email open.&lt;/p&gt;

</description>
      <category>claudecode</category>
      <category>typescript</category>
      <category>opensource</category>
      <category>webdev</category>
    </item>
    <item>
      <title>I built a clean Electron + React + Shadcn starter so I'd never have to wire it up from scratch again</title>
      <dc:creator>Fayaz Bin Salam</dc:creator>
      <pubDate>Mon, 11 May 2026 19:53:00 +0000</pubDate>
      <link>https://dev.to/fayazbuilds_n5f2t7/i-built-a-clean-electron-react-shadcn-starter-so-id-never-have-to-wire-it-up-from-scratch-again-ami</link>
      <guid>https://dev.to/fayazbuilds_n5f2t7/i-built-a-clean-electron-react-shadcn-starter-so-id-never-have-to-wire-it-up-from-scratch-again-ami</guid>
      <description>&lt;p&gt;Every time I sat down to start a new desktop app, the first hour was always the same boring dance. Bolt Vite onto Electron, wire React, sprinkle TypeScript, set up Tailwind, install Shadcn, then fight the build config until it stops crying.&lt;/p&gt;

&lt;p&gt;Eventually I stopped re-doing it and extracted the parts that always have to be there into a starter. It's been sitting at ~80 stars on GitHub without much promotion, which suggests other folks were doing the same dance.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's in the box
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Electron&lt;/strong&gt; + &lt;strong&gt;electron-forge&lt;/strong&gt; + &lt;strong&gt;electron-vite&lt;/strong&gt; — instant dev reload, one-command packaging for mac/win/linux&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;React&lt;/strong&gt; + &lt;strong&gt;TypeScript&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TailwindCSS&lt;/strong&gt; + &lt;strong&gt;shadcn/ui&lt;/strong&gt; — real components, not just primitives&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;usm-redux&lt;/strong&gt; for state — lighter than redux-toolkit, less boilerplate than scaling vanilla zustand into a large app&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prettier&lt;/strong&gt; + &lt;strong&gt;ESLint&lt;/strong&gt; preconfigured so the linter shuts up day one&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The non-obvious part
&lt;/h2&gt;

&lt;p&gt;The bit that took the longest to settle was keeping the main process and the renderer process completely separate in the Vite config so HMR doesn't get confused when you touch a preload script. If you've ever had Vite quietly stop hot-reloading after you edited &lt;code&gt;preload.ts&lt;/code&gt;, you know the exact bug. Once that clicked, everything else slotted in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Commands you actually use
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn          &lt;span class="c"&gt;# install&lt;/span&gt;
yarn dev      &lt;span class="c"&gt;# develop&lt;/span&gt;
yarn build:mac    &lt;span class="c"&gt;# ship mac&lt;/span&gt;
yarn build:win    &lt;span class="c"&gt;# ship windows&lt;/span&gt;
yarn build:linux  &lt;span class="c"&gt;# ship linux&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Repo
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/p32929/electron-shadcn-typescript" rel="noopener noreferrer"&gt;electron-shadcn-typescript on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;https://github.com/p32929/electron-shadcn-typescript&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;If you've been stalling at the setup phase before building a desktop app — clone it, run it, build something. Would love stars / forks / issues if it saves you some setup time.&lt;/p&gt;

&lt;p&gt;Open to collaborating with sharp teams and solo founders on real product work — DMs / email open at &lt;a href="https://github.com/p32929" rel="noopener noreferrer"&gt;github.com/p32929&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Ollama Models Explorer — a clean Next.js UI to browse and filter local LLMs</title>
      <dc:creator>Fayaz Bin Salam</dc:creator>
      <pubDate>Mon, 11 May 2026 11:34:06 +0000</pubDate>
      <link>https://dev.to/fayazbuilds_n5f2t7/ollama-models-explorer-a-clean-nextjs-ui-to-browse-and-filter-local-llms-15a8</link>
      <guid>https://dev.to/fayazbuilds_n5f2t7/ollama-models-explorer-a-clean-nextjs-ui-to-browse-and-filter-local-llms-15a8</guid>
      <description>&lt;p&gt;If you're running local LLMs through Ollama, finding the right model is annoying. The official model page scrolls forever, capability tags are inconsistent, and there's no way to sort by context window or size without doing it in your head.&lt;/p&gt;

&lt;p&gt;So I built &lt;strong&gt;Ollama Models Explorer&lt;/strong&gt; — a small Next.js + Tailwind app that pulls the full Ollama model catalog and gives you a real, fast table. Search by name, filter by capability (chat, vision, embedding), sort by name, size, or context length by clicking the column header. Dark-themed because that's where I live.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Live demo:&lt;/strong&gt; &lt;a href="https://ollama-models-explorer.vercel.app/" rel="noopener noreferrer"&gt;https://ollama-models-explorer.vercel.app/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Repo:&lt;/strong&gt; &lt;a href="https://github.com/p32929/ollama_models_explorer" rel="noopener noreferrer"&gt;https://github.com/p32929/ollama_models_explorer&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Next.js (app router) + TypeScript&lt;/li&gt;
&lt;li&gt;Tailwind CSS + shadcn/ui for the table and inputs&lt;/li&gt;
&lt;li&gt;Lucide for icons&lt;/li&gt;
&lt;li&gt;Model data loaded from a local JSON file, so the deploy is fully static and instant&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  One non-obvious thing
&lt;/h2&gt;

&lt;p&gt;The capability filter ANDs the pills together instead of ORing them. Most "filter by tag" UIs default to OR ("any of these tags") which is almost never what you want when you're hunting for a model that does &lt;em&gt;both&lt;/em&gt; vision AND chat. Small detail, surprisingly different feel.&lt;/p&gt;

&lt;p&gt;Also: sorting context window numerically meant parsing strings like &lt;code&gt;128k&lt;/code&gt;, &lt;code&gt;1M&lt;/code&gt;, &lt;code&gt;32768&lt;/code&gt; into a consistent unit before comparing. Looks trivial, but the Ollama catalog mixes formats freely.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;It's bare-bones on purpose — would love feedback on what filters or columns you'd actually use day-to-day. Stars / forks welcome if it's useful.&lt;/p&gt;

&lt;p&gt;Open to building with sharp teams + solo founders — DMs and email open.&lt;/p&gt;

&lt;p&gt;— Fayaz (&lt;a href="https://github.com/p32929" rel="noopener noreferrer"&gt;github.com/p32929&lt;/a&gt;)&lt;/p&gt;

</description>
      <category>ollama</category>
      <category>ai</category>
      <category>opensource</category>
      <category>nextjs</category>
    </item>
  </channel>
</rss>
