<?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: Noorin S</title>
    <description>The latest articles on DEV Community by Noorin S (@ns007-dev).</description>
    <link>https://dev.to/ns007-dev</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F947638%2F13485d25-ad22-4cb2-bc9f-9255787fb83b.JPG</url>
      <title>DEV Community: Noorin S</title>
      <link>https://dev.to/ns007-dev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ns007-dev"/>
    <language>en</language>
    <item>
      <title>I Built an AI App That Wants You to Close It</title>
      <dc:creator>Noorin S</dc:creator>
      <pubDate>Fri, 09 Oct 2026 03:57:01 +0000</pubDate>
      <link>https://dev.to/ns007-dev/i-built-an-ai-app-that-wants-you-to-close-it-3kff</link>
      <guid>https://dev.to/ns007-dev/i-built-an-ai-app-that-wants-you-to-close-it-3kff</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-week1-2026-10-05"&gt;Hacktoberfest Open-Source AI Challenge Week 1: Touch Grass&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Most AI apps are built to keep you talking. I wanted to build one whose success condition is the opposite: &lt;strong&gt;the best interaction with it is closing it.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;OFFLINE is an expedition-map-style web app. You tell it how much time you have and what you need. A &lt;strong&gt;local Gemma model&lt;/strong&gt; then writes you a short outdoor mission made of small, strange, sensory steps, and the app gets out of your way. Its last message is always the same:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;You're done. Close OFFLINE.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;OFFLINE is an AI adventure guide &lt;strong&gt;designed to become unnecessary&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The flow takes about thirty seconds of screen time:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Check in.&lt;/strong&gt; Pick how long you have (15, 30, 60 or 90+ minutes) and what you need: &lt;em&gt;clear my head, explore, move, be alone,&lt;/em&gt; or &lt;em&gt;surprise me&lt;/em&gt;. Optionally say roughly where you are (city, park, village). There's no GPS and nothing is requested from your browser.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Get a mission.&lt;/strong&gt; Gemma writes the whole thing: a title like "The Quiet Corner", a one-line premise, 4 to 7 steps, a reflection question, and the closing line.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Walk it.&lt;/strong&gt; Your mission is drawn like a route on an old map, with a red &lt;strong&gt;X&lt;/strong&gt; marking where you are. Each step asks for one concrete thing: &lt;em&gt;"Turn toward the most interesting sound you can hear and walk a little way toward it."&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Put the phone down.&lt;/strong&gt; Press the button and the screen turns into a bare night chart: one calm ring timer, the instruction, and nothing else. You come back for the next step only when you're ready.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Close it.&lt;/strong&gt; A one-line reflection, an ink stamp ("EXPEDITION COMPLETE"), and a button that says CLOSE OFFLINE.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Who it's for:&lt;/strong&gt; anyone who opens their phone "for a second" and loses an hour, and anyone who wants a reason to walk somewhere familiar and notice it differently. It's for people who don't want a hiking app, a fitness tracker or another feed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What it deliberately does not have:&lt;/strong&gt; streaks, badges, points, accounts, history, notifications or a chat box. Every one of those would pull people &lt;em&gt;back&lt;/em&gt; to the screen, which is the opposite of the point.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/NS007-dev" rel="noopener noreferrer"&gt;
        NS007-dev
      &lt;/a&gt; / &lt;a href="https://github.com/NS007-dev/offline" rel="noopener noreferrer"&gt;
        offline
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;OFFLINE&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;em&gt;An AI adventure guide designed to become unnecessary.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Created by &lt;strong&gt;Noorin Sakhi&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Built for &lt;strong&gt;Hacktoberfest 2026, Challenge 2: Open-Source AI ("Touch Grass")&lt;/strong&gt;.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;What OFFLINE is&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;You open the app, say how much time you have and what you need, and a &lt;strong&gt;local Gemma model&lt;/strong&gt; writes you a short outdoor mission: a few sensory, non-optimized steps like &lt;em&gt;"turn toward the most interesting sound you can hear."&lt;/em&gt; Then the app tells you to put your phone down. It reveals the next step only when you come back for it, and its last message is: &lt;strong&gt;"You're done. Close OFFLINE."&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;It is deliberately &lt;em&gt;not&lt;/em&gt; a chatbot, a hiking planner, or a map app.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Why it exists&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;Most AI products are built to keep you talking to them. OFFLINE's success condition is the opposite: &lt;strong&gt;the best interaction with OFFLINE is closing OFFLINE.&lt;/strong&gt; No feeds, streaks, badges, or accounts. The screen should be…&lt;/p&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/NS007-dev/offline" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Run it in about five minutes&lt;/strong&gt; (you need Python 3.10+, Node 20.19+ or 22.12+, and &lt;a href="https://ollama.com" rel="noopener noreferrer"&gt;Ollama&lt;/a&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. a Gemma model (any Gemma tag works)&lt;/span&gt;
ollama pull gemma3:1b            &lt;span class="c"&gt;# small and fast; or a larger one if your machine can take it&lt;/span&gt;

&lt;span class="c"&gt;# 2. backend&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;backend
python3 &lt;span class="nt"&gt;-m&lt;/span&gt; venv .venv &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;source&lt;/span&gt; .venv/bin/activate
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
&lt;span class="nv"&gt;OFFLINE_MODEL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;gemma3:1b python3 &lt;span class="nt"&gt;-m&lt;/span&gt; uvicorn app.main:app &lt;span class="nt"&gt;--reload&lt;/span&gt;

&lt;span class="c"&gt;# 3. frontend (new terminal)&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;frontend
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm run dev       &lt;span class="c"&gt;# open http://localhost:5173&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No API keys, no accounts, no cloud. &lt;code&gt;python3 -m app.cli --time 30 --need surprise&lt;/code&gt; generates a mission straight in the terminal if you want to test Gemma before the UI.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The stack
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;React + TypeScript + Vite  -&amp;gt;  FastAPI  -&amp;gt;  Ollama  -&amp;gt;  Gemma (open weights)
                                  |
              validate -&amp;gt; safety screen -&amp;gt; retry once -&amp;gt; hand-written fallback
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Gemma writes every mission.&lt;/strong&gt; The app contains no mission templates for the main path. It sends Gemma a short check-in and asks for JSON that matches a schema, using Ollama's structured outputs. The hand-written missions exist only as a fallback, and the app tells you when you're seeing one.&lt;/p&gt;

&lt;h3&gt;
  
  
  Making Gemma interesting, not just correct
&lt;/h3&gt;

&lt;p&gt;My first missions were safe and samey. "Take a deep breath and notice your surroundings" is not a quest. Three changes fixed most of it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A voice.&lt;/strong&gt; The system prompt casts Gemma as &lt;em&gt;a quiet, slightly mysterious game master&lt;/em&gt; and bans clichés outright ("be present", "connect with nature", "embrace the moment").&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A twist per step.&lt;/strong&gt; Every step must ask for &lt;em&gt;one&lt;/em&gt; concrete thing with a small rule that makes it feel like a game: &lt;em&gt;"Follow the quietest direction for ten steps,"&lt;/em&gt; not &lt;em&gt;"Take a walk."&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A random lens.&lt;/strong&gt; Each request carries one of twelve lenses (sound, colour, age, edges, light and shadow, small things, texture, direction, stillness, signs of people, weather and air, looking up) that Gemma threads through the mission, so two requests rarely come out alike.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;LENSES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sound: what you hear, where it comes from, what is just out of earshot&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;age: things that were here long before you, and things that will outlast today&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;edges: borders, corners, thresholds, where one place turns into another&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;# ...nine more
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Never trust a model you can't audit
&lt;/h3&gt;

&lt;p&gt;A small local model will sometimes ignore instructions, and this app tells people to walk around outdoors, so I treat everything Gemma returns as untrusted input. Safety is three layers:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Prompt&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Tells Gemma the rules: ordinary public places only; no trespassing, climbing, unsafe road crossings, approaching strangers, ignoring signs, being lost on purpose, special equipment, purchases, or after-dark activity.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Safety screen&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A deliberately blunt filter over every sentence. It blocks "Climb the fence" and "Open Google Maps," but lets "Do not search your phone for it" through.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Validator&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Enforces the design rules: 4 to 7 steps, at least two phone-down steps, durations that fit your time, and a final line that tells you to close the app.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If a mission fails, the backend &lt;strong&gt;retries once&lt;/strong&gt; and tells Gemma exactly what was wrong ("step 3 has no instruction; durations add up to 41 minutes, more than the 30 available"). If it fails again, or Ollama is down, slow or missing the model, you get a hand-written mission that passes the &lt;em&gt;same&lt;/em&gt; validator, with an honest notice explaining why. The app never pretends Gemma wrote something it didn't.&lt;/p&gt;

&lt;h3&gt;
  
  
  Things the tests caught
&lt;/h3&gt;

&lt;p&gt;I wrote 30 backend tests and 15 browser tests. The backend tests run against a pretend Ollama server I can make misbehave on command: bad JSON, an unsafe mission, a slow reply, a missing model, or no server at all. That earned its keep:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;My safety filter blocked my own fallback.&lt;/strong&gt; It flagged "keep your phone in your pocket" as phone use. I narrowed the rule and added a regression test.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Screen-reader users had no heading&lt;/strong&gt; on step 2 onward. My accessibility check caught it, and it's fixed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A "pop-in" animation shrank buttons to 41px&lt;/strong&gt; mid-animation and failed my 44px tap-target check. I dropped the scaling.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;An honest note on what I did not measure:&lt;/em&gt; the automated tests use a stand-in model, so they prove the pipeline, not how good Gemma's missions are. That part I judged by running real missions myself (see the field test above).&lt;/p&gt;

&lt;h3&gt;
  
  
  The design journey
&lt;/h3&gt;

&lt;p&gt;I redesigned the interface three times, and it's the part I learned the most from:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;A calm forest-green theme.&lt;/strong&gt; It looked fine and felt like every other app, a slideshow of words you tap through.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A bright, quirky sticker style.&lt;/strong&gt; Too childish, and it still looked like an app.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An expedition map.&lt;/strong&gt; Parchment, contour lines, a compass whose needle swings and settles, a dotted route with a red X, and a night-chart "phone down" screen. The goal was a page that doesn't feel like it's on a device at all.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Accessibility and privacy by construction
&lt;/h3&gt;

&lt;p&gt;Choices are real radio buttons (arrow keys work), headings take focus on every screen, tap targets are at least 44px, and all motion switches off under &lt;code&gt;prefers-reduced-motion&lt;/code&gt;. Phone-down timers compare against the clock, so they stay correct when your screen sleeps. There are no cookies, no localStorage and no third-party requests, and a test asserts that the browser only ever talks to localhost.&lt;/p&gt;

&lt;h3&gt;
  
  
  Built with an AI pair-programmer
&lt;/h3&gt;

&lt;p&gt;In the spirit of transparency: I built OFFLINE from a written build spec, using &lt;strong&gt;Claude as my coding partner&lt;/strong&gt; in a long back-and-forth. I made the product calls, ran everything on my own machine, hit the real errors (a failed &lt;code&gt;ollama pull&lt;/code&gt;, a model-name mismatch), and redirected the design when it wasn't right. The &lt;em&gt;runtime&lt;/em&gt; has no closed dependency at all. The only AI inside the app is open-weight Gemma running locally.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Does Open Innovation Matter?
&lt;/h2&gt;

&lt;p&gt;I built OFFLINE for people who are trying to spend &lt;em&gt;less&lt;/em&gt; time with technology. A cloud API would quietly undercut that, and an open model fixes it in four ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Privacy is a fact, not a promise.&lt;/strong&gt; The mission is written on your machine. There's no server to trust, no key to leak, and no log of when and why you wanted to get outside. The only context Gemma ever sees is "30 minutes, clear my head, a park."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No signal needed once you leave.&lt;/strong&gt; The mission is generated before you go. After that, every step and timer lives in the page, and the app makes no network request again. You can walk out of Wi-Fi range with your mission in your pocket.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;I could swap the model by changing one word.&lt;/strong&gt; The model is a single environment variable (&lt;code&gt;OFFLINE_MODEL&lt;/code&gt;). I could trade a tiny, fast Gemma for a larger, more creative one on the same laptop without touching code, and see the difference in the missions right away. A closed API would have chosen that trade-off for me.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It costs nothing to run.&lt;/strong&gt; No per-mission fee, no rate limit, no billing page. An app meant to be opened for thirty seconds shouldn't come with a meter.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There's also a quieter reason. An app that treats model output as &lt;strong&gt;untrusted input&lt;/strong&gt; is much easier to build when you control the model, the prompt, the sampling and the schema. I could tune the temperature, constrain the output shape and reproduce failures locally. For a project that sends people out the door, that control is a safety feature.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prize Categories
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Gemma.&lt;/strong&gt; Gemma runs locally via Ollama and writes every mission end to end: title, premise, steps, reflection and closing line. It's constrained with a JSON schema, steered by a voice and a rotating creative lens, validated and safety-screened, and swappable across Gemma sizes with one setting.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;OFFLINE is deliberately small, and I'd like to keep it that way. If I keep building, it would be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Gentle context, kept ephemeral:&lt;/strong&gt; optional daylight and weather so missions fit the hour, never stored.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A "found it" step type:&lt;/strong&gt; missions that ask for something to &lt;em&gt;spot&lt;/em&gt;, and read it back in the reflection.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sound for the end of a timer:&lt;/strong&gt; a soft chime, for people who put the phone in a pocket.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A real phone field test&lt;/strong&gt; on cellular, away from the laptop.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Known limits:&lt;/strong&gt; the safety screen is a keyword filter, not a guarantee. OFFLINE doesn't know your surroundings, so it can't verify any place exists. Small models vary from run to run, and it can't buzz you while your screen is locked.&lt;/p&gt;

&lt;p&gt;Made by &lt;strong&gt;Noorin Sakhi&lt;/strong&gt;, MIT licensed. If you try it, I'd love to hear what your mission was and what you noticed.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
      <category>ai</category>
      <category>opensource</category>
    </item>
    <item>
      <title>I built my sister a personal stylist that lives on my laptop</title>
      <dc:creator>Noorin S</dc:creator>
      <pubDate>Mon, 05 Oct 2026 08:02:59 +0000</pubDate>
      <link>https://dev.to/ns007-dev/i-built-my-sister-a-personal-stylist-that-lives-on-my-laptop-jco</link>
      <guid>https://dev.to/ns007-dev/i-built-my-sister-a-personal-stylist-that-lives-on-my-laptop-jco</guid>
      <description>&lt;p&gt;Every morning, I get asked the same question: &lt;em&gt;"What am I going to wear?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Her closet is full of potential outfits, but choosing takes time, she doesn't want to repeat an outfit, and she wants whatever she wears to feel like &lt;em&gt;her&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;So I built her something. It's called &lt;strong&gt;ClosetCue&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I started it for the Hacktoberfest &lt;em&gt;Build for a Friend&lt;/em&gt; challenge. I missed the submission cutoff by about twenty minutes, so this isn't an entry. I still wanted to share it, because it's the project I'm proudest of so far.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnejlhfakjjnp0s60jfqi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnejlhfakjjnp0s60jfqi.png" alt="The Today screen" width="800" height="1218"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  I asked her first
&lt;/h2&gt;

&lt;p&gt;Before writing any code, I asked my sister what the problem actually was. Her answers became the product:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Choosing outfits for school mornings and for going out is hard.&lt;/li&gt;
&lt;li&gt;Not repeating outfits is a big worry.&lt;/li&gt;
&lt;li&gt;Choosing what to wear is stressful.&lt;/li&gt;
&lt;li&gt;Her two main occasions are school and shopping / going out.&lt;/li&gt;
&lt;li&gt;Style matters most to her.&lt;/li&gt;
&lt;li&gt;She's willing to photograph her entire wardrobe.&lt;/li&gt;
&lt;li&gt;Her dream feature: an outfit waiting for her every morning.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The problem wasn't "nothing to wear." It was decision fatigue.&lt;/p&gt;

&lt;h2&gt;
  
  
  What ClosetCue does
&lt;/h2&gt;

&lt;p&gt;She photographs the clothes she already owns. A local AI model reads each photo and writes a caption: category, type, colour, pattern, style, season. She can correct anything it gets wrong. She picks the styles and moods she loves, and then the app styles a look for &lt;strong&gt;School&lt;/strong&gt; or &lt;strong&gt;Shopping / Out&lt;/strong&gt;, using only clothes she actually owns.&lt;/p&gt;

&lt;p&gt;She can tap &lt;strong&gt;Wear this&lt;/strong&gt; to file the outfit, or &lt;strong&gt;Try another&lt;/strong&gt; and tell it why. Every look she wears goes into an archive, so the app remembers.&lt;/p&gt;

&lt;h2&gt;
  
  
  The AI doesn't get to break the rules
&lt;/h2&gt;

&lt;p&gt;The decision I'm happiest with was splitting the work on purpose:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;The AI handles&lt;/th&gt;
&lt;th&gt;Plain code handles&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Reading clothing photos&lt;/td&gt;
&lt;td&gt;Storing the wardrobe&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Proposing outfits&lt;/td&gt;
&lt;td&gt;Remembering what was worn&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Explaining why a look works&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Blocking repeats&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Checking an outfit is complete (top + bottom or a dress, plus shoes)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;Ranking and validating&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The AI proposes four outfits. Then code throws out anything invalid or recently worn, ranks what's left (pieces she hasn't worn lately and her chosen styles score higher), and shows the best one. No complete outfit repeats within 14 days, and every look comes only from her real closet.&lt;/p&gt;

&lt;p&gt;If I'd let the AI do everything, it would eventually suggest a jacket she doesn't own. Putting the rules in normal code means they can't be ignored.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why open-weight matters here
&lt;/h2&gt;

&lt;p&gt;ClosetCue runs an open-weight &lt;strong&gt;Gemma&lt;/strong&gt; model on my own laptop through &lt;strong&gt;Ollama&lt;/strong&gt;. It costs nothing to run, needs no account or paid API, and her photos are read on the same machine that stores them.&lt;/p&gt;

&lt;p&gt;I'll be exact about what that means. Photos and the database stay on my laptop. The AI runs locally, so nothing is sent to an AI service. The browser does load the typefaces from Google Fonts, and downloading the model the first time needs internet. It's also a single-user local app with no accounts, so it shouldn't be put on the public internet as it is.&lt;/p&gt;

&lt;h2&gt;
  
  
  It had to look like her, not like a dashboard
&lt;/h2&gt;

&lt;p&gt;I didn't want a chatbot or an analytics screen. I wanted it to feel like opening a magazine: a serif masthead, hairline rules, ivory paper, one oxblood accent, and big numbered photos with captions, like a fashion spread.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fd51vhc8hmmlt52k0lwdk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fd51vhc8hmmlt52k0lwdk.png" alt="The closet" width="800" height="1218"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What went wrong
&lt;/h2&gt;

&lt;p&gt;This is the part I learned the most from.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Uploads froze.&lt;/strong&gt; At first, every photo waited for the AI to finish reading it before the next one could upload. Ten photos took forever, and reloading the page lost everything. I rebuilt it so photos save instantly and the AI reads them in a background queue on the server. It survives reloads and restarts, and unfinished pieces show "Reading…".&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Outfits hung.&lt;/strong&gt; When the AI was busy with photos, asking for an outfit just sat there. Now the AI gets 25 seconds, and if it's too slow, a simpler mode takes over. The simple mode still builds valid, non-repeating outfits, so she's never left waiting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A hosting lesson.&lt;/strong&gt; I wanted to put it online, then realised that would mean accounts, private storage per user, and a plan for the AI. It's a rewrite, not a setting. So it stays local for now, and that's in my "next" list.&lt;/p&gt;

&lt;h2&gt;
  
  
  Her reaction
&lt;/h2&gt;

&lt;p&gt;[Replace this with what she actually said or did when she used it. Keep it honest, even if it's mixed.]&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Choose several pieces and build a look around all of them&lt;/li&gt;
&lt;li&gt;Build an outfit by hand&lt;/li&gt;
&lt;li&gt;Accounts and private per-user storage, so others can use it safely&lt;/li&gt;
&lt;li&gt;A real morning notification&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;The code and setup steps are on GitHub: &lt;strong&gt;&lt;a href="https://github.com/NS007-dev/closet-cue" rel="noopener noreferrer"&gt;NS007-dev/closetcue&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Built with React, TypeScript, Vite, Tailwind, Python, FastAPI, SQLite, Ollama and Gemma.&lt;/p&gt;

&lt;p&gt;If you build something for one specific person, I recommend it. Asking her what she needed did more for the project than any technical choice I made.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>ai</category>
      <category>react</category>
      <category>python</category>
    </item>
  </channel>
</rss>
