<?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: Akif Mansoor</title>
    <description>The latest articles on DEV Community by Akif Mansoor (@akif_mansoor_50c5ccaa2a7f).</description>
    <link>https://dev.to/akif_mansoor_50c5ccaa2a7f</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%2F4046739%2Fad58dcd1-afba-4e8d-b258-9e8d0f1bbf31.jpg</url>
      <title>DEV Community: Akif Mansoor</title>
      <link>https://dev.to/akif_mansoor_50c5ccaa2a7f</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/akif_mansoor_50c5ccaa2a7f"/>
    <language>en</language>
    <item>
      <title>I built a tray app that captures highlights from any Windows app into a local SQLite database</title>
      <dc:creator>Akif Mansoor</dc:creator>
      <pubDate>Sun, 26 Jul 2026 01:25:33 +0000</pubDate>
      <link>https://dev.to/akif_mansoor_50c5ccaa2a7f/i-built-a-tray-app-that-captures-highlights-from-any-windows-app-into-a-local-sqlite-database-631</link>
      <guid>https://dev.to/akif_mansoor_50c5ccaa2a7f/i-built-a-tray-app-that-captures-highlights-from-any-windows-app-into-a-local-sqlite-database-631</guid>
      <description>&lt;p&gt;&lt;strong&gt;The problem&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I do a lot of reading for research — PDFs in one window, Word docs in another, a dozen browser tabs I swore I'd get back to. Every workflow I tried for capturing highlights involved the same tax: alt-tab to Notion, paste, retype the source, retype the tags, alt-tab back. By the third paper of the day I'd just stopped bothering, and six months later I had no idea which PDF a quote came from.&lt;/p&gt;

&lt;p&gt;So I built &lt;strong&gt;NotesDB&lt;/strong&gt; — a small Python app that lives in the Windows system tray and captures text, screenshots, and notes from &lt;em&gt;any&lt;/em&gt; application without you ever leaving what you're reading.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it actually does
&lt;/h2&gt;

&lt;p&gt;Three ways to capture, all landing in the same local database:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Global hotkey.&lt;/strong&gt; Select text anywhere — Word, a PDF reader, Chrome — and hit &lt;code&gt;Ctrl+Shift+V&lt;/code&gt;. A save dialog pops up instantly, pre-filled with the source app, window title, and file path.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clipboard watcher.&lt;/strong&gt; Copy normally with &lt;code&gt;Ctrl+C&lt;/code&gt;. A small toast appears near your cursor for a few seconds; click it to save, ignore it and it disappears. No dialog if you don't want one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Browser extension.&lt;/strong&gt; Right-click selected text or an image in Chrome/Edge → "Save to NotesDB."&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Everything lands in a single SQLite file (&lt;code&gt;notes.db&lt;/code&gt;) with tags, colour-coding, project grouping, and auto-matched "connected notes" between related captures. There's also a built-in PDF reader with a snapshot tool (drag a rectangle, grab a region as an image), a highlighter, and text selection — so I stopped needing Adobe open at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it's put together
&lt;/h2&gt;

&lt;p&gt;The core is Python + Tkinter — no Electron, no web framework for the main UI, which keeps the installed footprint small (the whole PyInstaller build comes in under 30MB). A few pieces worth calling out:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The clipboard watcher runs as a daemon thread polling every 500ms:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ClipboardWatcher&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;threading&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;last&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_clipboard_content&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# text or image
&lt;/span&gt;            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;pos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;win32api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;GetCursorPos&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="n"&gt;capture_queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cursor&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;_clipboard&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
                &lt;span class="n"&gt;last&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt;
            &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The main app polls that queue every 200ms and hands items off to a capture handler — deliberately decoupled so a bad capture never kills the watcher thread.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Theming is done through module-level colour globals&lt;/strong&gt;, not a class or CSS-like system. There are six built-in themes (Dark, Light, Ocean, Midnight, Forest, Warm), and switching one calls &lt;code&gt;_apply_theme_globals()&lt;/code&gt;, which reassigns every colour constant (&lt;code&gt;BG&lt;/code&gt;, &lt;code&gt;FG&lt;/code&gt;, &lt;code&gt;ACCENT&lt;/code&gt;, etc.) app-wide, then triggers a redraw. Every custom-drawn widget — cards, the floating capture dialog, the PDF reader overlays — reads from the same globals, so nothing gets out of sync.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Citations are pulled live from CrossRef.&lt;/strong&gt; If a captured source has a DOI (or one can be inferred from the URL), a "Get Citation" button formats it as APA on the spot — genuinely one of the more satisfying small features to ship.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Source detection&lt;/strong&gt; uses &lt;code&gt;win32gui&lt;/code&gt; + &lt;code&gt;psutil&lt;/code&gt; to figure out which app and, where possible, which &lt;em&gt;file&lt;/em&gt; a selection came from — so a highlight from a PDF gets tagged with the actual filename and full path, not just "some PDF reader."&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd do differently
&lt;/h2&gt;

&lt;p&gt;If I started over I'd probably reach for a proper widget toolkit sooner — hand-rolling rounded-corner cards on a Tkinter &lt;code&gt;Canvas&lt;/code&gt; works, but it's a lot of geometry math for something a real UI framework gives you for free. The tradeoff was a genuinely tiny, dependency-light executable, which for a tray app I'm fine with.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where it's at now
&lt;/h2&gt;

&lt;p&gt;It's a real, working Windows app today — installer, tray icon, PDF reader, exports to DOCX/PDF/TXT, the whole thing. Local-only by design: your data is one SQLite file, there's no account, no sync service, nothing phoning home.&lt;/p&gt;

&lt;p&gt;If capturing research across PDFs/browser/Word without breaking flow is a problem you also have, I'd love feedback — happy to answer questions about the clipboard-polling approach, the theming system, or anything else in the comments.&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>python</category>
      <category>sideprojects</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
