<?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: Omar Afifi</title>
    <description>The latest articles on DEV Community by Omar Afifi (@omarafifi).</description>
    <link>https://dev.to/omarafifi</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%2F3954903%2F1b239da9-6bec-4707-b255-626d891d9dd7.png</url>
      <title>DEV Community: Omar Afifi</title>
      <link>https://dev.to/omarafifi</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/omarafifi"/>
    <language>en</language>
    <item>
      <title>My Phone Broke, and Gemini Fixed It</title>
      <dc:creator>Omar Afifi</dc:creator>
      <pubDate>Wed, 09 Sep 2026 22:26:48 +0000</pubDate>
      <link>https://dev.to/omarafifi/my-phone-broke-and-gemini-fixed-it-329l</link>
      <guid>https://dev.to/omarafifi/my-phone-broke-and-gemini-fixed-it-329l</guid>
      <description>&lt;h2&gt;
  
  
  📱 Picture This: An All-Nighter Turns Into a Morning Nightmare
&lt;/h2&gt;

&lt;p&gt;It is early morning. You’ve just pulled an all-nighter tinkering with an Android project over ADB. The sun is already creeping through the window, tests finally pass, and you unplug the USB cable from your OnePlus to finally go to bed.&lt;/p&gt;

&lt;p&gt;You unlock your screen, swipe down to open Settings, and freeze.&lt;/p&gt;

&lt;p&gt;Right across your menus is a block of bright-red monospace text:&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%2Fyyu387sfmchubhw4o4si.jpg" 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%2Fyyu387sfmchubhw4o4si.jpg" alt="The mysterious red debug overlay on OxygenOS" width="431" height="948"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;isOverScrolling: false
X: FlingVX: 0.0, ClickVX: 0.0
Y: FlingVY: 15435.0, ClickVY: 0.0
AbortVX:0.0, AbortVY:15435.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You stare at it, then touch the screen again. The numbers immediately jump, recalculating in real time with every movement of your thumb. You swipe fast, and &lt;code&gt;FlingVY&lt;/code&gt; shoots into the tens of thousands. &lt;/p&gt;

&lt;p&gt;Then you notice something even worse: the phone is lagging badly. &lt;/p&gt;

&lt;p&gt;When you swipe from the screen edge to go back, the gesture stutters. Pulling out the OnePlus Smart Sidebar hesitates and drops frames. The smooth 120Hz display suddenly feels like a cheap phone struggling to keep up.&lt;/p&gt;




&lt;h2&gt;
  
  
  🥶 Zero Results: When Google Goes Completely Silent
&lt;/h2&gt;

&lt;p&gt;Your developer reflexes kick in immediately. You jump straight into &lt;strong&gt;Developer Options&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Pointer Location?&lt;/em&gt; Off.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Show Layout Bounds?&lt;/em&gt; Off.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Show Taps?&lt;/em&gt; Off.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Strict Mode?&lt;/em&gt; Off.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Everything is disabled. &lt;/p&gt;

&lt;p&gt;Fine. You hold down the power button and reboot. The phone restarts, you unlock it, open Settings, scroll down—and the red numbers are still right there.&lt;/p&gt;

&lt;p&gt;That's when you start to panic. You open your browser and start searching:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;"red text on android screen"&lt;/code&gt;: &lt;em&gt;Generic guides telling you to turn off Developer Options.&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;"OnePlus red text on screen"&lt;/code&gt;: &lt;em&gt;Threads asking about the red "1" on the lock screen clock (funny, but not helping).&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;"OnePlus red text error"&lt;/code&gt;: &lt;em&gt;Nothing remotely relevant.&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;"isOverScrolling: false" OnePlus&lt;/code&gt;: &lt;em&gt;No results found.&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;"AbortVX"&lt;/code&gt; / &lt;code&gt;"ClickVX"&lt;/code&gt;: &lt;em&gt;Absolute zero.&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Zero results. Not a single thread on XDA Forums, not a mention on Reddit, and nothing on the OnePlus Community.&lt;/p&gt;

&lt;p&gt;If you've worked with Android long enough, you know what this silence means: when an issue has zero documentation in AOSP and zero community threads, you're usually looking at a full factory reset.&lt;/p&gt;




&lt;h2&gt;
  
  
  😴 "That’s Future Me’s Problem"
&lt;/h2&gt;

&lt;p&gt;At that point, the morning sun was already up. I had stayed up all night, I was running on zero sleep, completely exhausted, and my brain felt like an OutOfMemory error. I stared at the red numbers on my screen and made the most mature engineering decision possible:&lt;/p&gt;

&lt;p&gt;I decided to go to sleep.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"You know what? This is future me's problem."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I put the phone face-down on the nightstand, closed my eyes, and hoped it would just magically disappear by the time I woke up.&lt;/p&gt;

&lt;p&gt;Fast forward to later that afternoon: I woke up, tapped the screen, and nope—the red velocity numbers were still right there, tracking every single swipe while I was still half-asleep.&lt;/p&gt;

&lt;p&gt;Now awake and ready to deal with it, I decided it was time to bring in serious backup.&lt;/p&gt;




&lt;h2&gt;
  
  
  🤖 Handing the Terminal to Gemini
&lt;/h2&gt;

&lt;p&gt;I plugged the USB cable back into my laptop. I opened my Antigravity IDE where I had been pair-programming with &lt;strong&gt;Gemini&lt;/strong&gt;, and typed out of pure frustration:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Hey, you ran some ADB commands on my phone earlier for some app testing and now there's this weird red text on my screen and the edges are lagging. I plugged it in—fix it."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If this were a standard chatbot, you already know what its first response would be:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Go to Settings &amp;gt; Developer Options and disable 'Pointer Location', 'Show Layout Bounds', or 'Strict Mode'. If those are already off, try backing up your data and performing a factory reset."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Every standard LLM defaults to that exact script because it assumes you're dealing with standard Android toggles. And when you tell it they're already off, it hits a complete dead end.&lt;/p&gt;

&lt;p&gt;Instead, Gemini took the wheel. What happened on my terminal over the next few minutes was impressive: rather than giving boilerplate advice, Gemini started systematically diagnosing the operating system from the inside out.&lt;/p&gt;




&lt;h2&gt;
  
  
  🎨 Step 1: The Canvas Deduction
&lt;/h2&gt;

&lt;p&gt;Gemini didn't guess. It queried the device's runtime settings via ADB:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;adb shell settings get system pointer_location
adb shell settings get system show_touches
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both came back &lt;code&gt;0&lt;/code&gt;. Standard Android touch telemetry was definitively off.&lt;/p&gt;

&lt;p&gt;Then Gemini dumped the entire live UI hierarchy of the Settings app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;adb shell uiautomator dump /sdcard/window_dump.xml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It grepped the XML tree for &lt;code&gt;isOverScrolling&lt;/code&gt;. The result? &lt;strong&gt;Empty.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That silence told Gemini everything:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"This text does not exist as a TextView or a system overlay window. It is being painted directly onto the hardware Canvas buffer during draw passes by an internal UI component."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Because the text appeared during scrolling in OnePlus system menus, Gemini formed an immediate hypothesis: the code belonged to OnePlus/OPPO’s proprietary UI toolkit—&lt;strong&gt;COUI&lt;/strong&gt; (ColorOS UI).&lt;/p&gt;




&lt;h2&gt;
  
  
  🔬 Step 2: Disassembling the Phone on the Fly
&lt;/h2&gt;

&lt;p&gt;Because these OEM classes are completely proprietary and unreleased, Gemini couldn't look up documentation. It had to read the machine code directly from the phone.&lt;/p&gt;

&lt;p&gt;To inspect the COUI framework without needing root access, Gemini pulled a built-in pre-installed app that uses the same UI components:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;adb pull /product/app/Calculator2/Calculator2.apk ./calc.apk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, right inside my terminal, Gemini wrote and executed Python scripts to parse the raw binary DEX (Dalvik Executable) structures of the APK.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Hunting the String Table
&lt;/h3&gt;

&lt;p&gt;Gemini parsed the DEX string pool inside &lt;code&gt;calc.apk&lt;/code&gt; and found the exact IDs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;String &lt;code&gt;#2600&lt;/code&gt; = &lt;code&gt;'AbortVX:'&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;String &lt;code&gt;#37919&lt;/code&gt; = &lt;code&gt;'isOverScrolling: '&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Pinpointing the Method
&lt;/h3&gt;

&lt;p&gt;It mapped those string IDs across every class definition in the binary. In seconds, Gemini had the exact location:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Class:&lt;/strong&gt; &lt;code&gt;androidx.recyclerview.widget.COUIRecyclerView&lt;/code&gt; (OnePlus’s internal extension of the Jetpack RecyclerView)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Method:&lt;/strong&gt; &lt;code&gt;dispatchDraw(Canvas canvas)&lt;/code&gt; at bytecode offset &lt;code&gt;0x1fbea0&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Reading the Dalvik Bytecode
&lt;/h3&gt;

&lt;p&gt;Gemini disassembled the opcodes at that offset. The bytecode showed exactly what was going on under the hood:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;0x1fbea0: invoke-virtual View-&amp;gt;dispatchDraw
0x1fbea6: sget COUIRecyclerView-&amp;gt;COUI_DEBUG:Z   // Check static boolean flag
0x1fbeaa: if-eqz -&amp;gt; 0x1fc03c                   // If false, skip entirely!
0x1fbeae: iget COUIRecyclerView-&amp;gt;mDebugPaint   // Get Paint object
0x1fbec4: invoke-virtual Paint-&amp;gt;setColor       // Set paint color to RED
0x1fbed4: const-string 'isOverScrolling: '     // Format velocity strings
0x1fbf2a: const-string 'X: FlingVX: '
0x1fbf86: const-string 'Y: FlingVY: '
0x1fbfe4: const-string 'AbortVX:'
0x1fc036: invoke-virtual Canvas-&amp;gt;drawText      // Draw directly on screen!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There it was: an undocumented, internal scroll-physics debugger built by OEM engineers to measure fling velocities during ROM development.&lt;/p&gt;




&lt;h2&gt;
  
  
  💥 Step 3: The Domino Effect — Finding Patient Zero
&lt;/h2&gt;

&lt;p&gt;Gemini now knew &lt;em&gt;what&lt;/em&gt; was painting the screen, but why had &lt;code&gt;COUI_DEBUG&lt;/code&gt; suddenly turned &lt;code&gt;true&lt;/code&gt; across my entire phone?&lt;/p&gt;

&lt;p&gt;Gemini inspected the class initializer &lt;code&gt;&amp;lt;clinit&amp;gt;&lt;/code&gt; of &lt;code&gt;COUIRecyclerView&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;0x1fb9bc: const-string 'COUIRecyclerView'
0x1fb9c2: invoke-static COUILog-&amp;gt;isLoggable("COUIRecyclerView", Log.DEBUG)
0x1fb9d6: sput COUIRecyclerView-&amp;gt;COUI_DEBUG:Z
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;COUILog.isLoggable()&lt;/code&gt; simply calls Android's built-in &lt;code&gt;android.util.Log.isLoggable(tag, level)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In Android, &lt;code&gt;Log.isLoggable()&lt;/code&gt; first checks for a tag-specific system property (&lt;code&gt;log.tag.&amp;lt;TAG&amp;gt;&lt;/code&gt;). If that doesn't exist, it checks the global fallback property:&lt;/p&gt;

&lt;blockquote&gt;
&lt;h3&gt;
  
  
  🎯 The Culprit: &lt;code&gt;persist.log.tag&lt;/code&gt;
&lt;/h3&gt;
&lt;/blockquote&gt;

&lt;p&gt;Gemini queried the phone:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;adb shell getprop persist.log.tag
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The terminal printed one character:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;V
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Connecting the Dots
&lt;/h3&gt;

&lt;p&gt;Hours earlier, while debugging an audio service over ADB, a command had set &lt;code&gt;persist.log.tag&lt;/code&gt; to &lt;code&gt;V&lt;/code&gt; (Verbose) to inspect background audio logs. &lt;/p&gt;

&lt;p&gt;That one command triggered an unexpected chain reaction:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Why it survived reboots:&lt;/strong&gt; Properties starting with &lt;code&gt;persist.&lt;/code&gt; are saved directly to flash storage (&lt;code&gt;/data/property&lt;/code&gt;). A reboot does not wipe them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why the red text appeared:&lt;/strong&gt; Every time any app initialized a scrollable list, &lt;code&gt;COUIRecyclerView&lt;/code&gt; checked if debug logging was enabled. Because &lt;code&gt;persist.log.tag&lt;/code&gt; was set globally to &lt;code&gt;V&lt;/code&gt;, Android replied that everything was in verbose mode. &lt;code&gt;COUI_DEBUG&lt;/code&gt; became &lt;code&gt;true&lt;/code&gt;, and the red velocity overlay activated.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why the edges were lagging:&lt;/strong&gt; With global verbose logging enabled across the entire OS, every single touch event, swipe coordinate, and edge gesture flooded the system log daemon with hundreds of log lines per second. The CPU was overwhelmed with logging I/O, dropping frames and causing noticeable edge touch lag.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  ⚡ The Cure: Total Relief in Five Seconds
&lt;/h2&gt;

&lt;p&gt;Once Gemini identified the exact root cause, the fix was quick and clean:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Resetting the Global Flag
&lt;/h3&gt;

&lt;p&gt;Gemini reset the dangerous persistent global log property to empty:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;adb shell setprop persist.log.tag &lt;span class="s2"&gt;""&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Preventing It from Happening Again
&lt;/h3&gt;

&lt;p&gt;To make sure &lt;code&gt;COUIRecyclerView&lt;/code&gt; could never wake up its debug drawing again—even if global verbose logging were accidentally enabled in a future project—Gemini injected a permanent tag-level suppression rule:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;adb shell setprop persist.log.tag.COUIRecyclerView SUPPRESS
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Killing the Active Process for Instant Testing
&lt;/h3&gt;

&lt;p&gt;Gemini killed the Settings process to immediately test the fix on the current window:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;adb shell am force-stop com.android.settings
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Gemini triggered an automated swipe gesture through ADB, captured a live screenshot, and verified that the red text had vanished from Settings.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. The Final Device Reboot: Flushing In-Memory State
&lt;/h3&gt;

&lt;p&gt;While Settings was fixed immediately, Gemini pointed out a crucial Android runtime mechanic:&lt;/p&gt;

&lt;p&gt;In the Dalvik/ART virtual machine, static initializers (&lt;code&gt;&amp;lt;clinit&amp;gt;&lt;/code&gt;) run &lt;strong&gt;only once&lt;/strong&gt; when a class is first loaded into a process. That meant any app or system service already running in RAM (like the Launcher, SystemUI, or Contacts) still held &lt;code&gt;COUI_DEBUG = true&lt;/code&gt; cached in memory.&lt;/p&gt;

&lt;p&gt;To guarantee that every background process, system daemon, and service reloaded cleanly from scratch with the new suppressed properties, we performed a fresh device reboot:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;adb reboot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the phone rebooted, I picked it up, unlocked it, and scrolled through every corner of the system:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The red text was 100% gone across all apps.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The edges were instantly responsive.&lt;/strong&gt; Edge gestures, the Smart Sidebar, and the silky 120Hz display were completely restored.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Zero personal data touched, zero system partitions modified.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;All temporary extraction scripts were cleanly wiped from both the phone and PC.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🎓 Final Thoughts: An Engineering Student Left Speechless
&lt;/h2&gt;

&lt;p&gt;As a Computer &amp;amp; Systems Engineering student, I usually take AI hype with a grain of salt. I understand how systems work under the hood, and I know how complex and messy OEM Android frameworks get when low-level state gets corrupted.&lt;/p&gt;

&lt;p&gt;If I had posted this on a forum or asked a standard chatbot, the advice would have been the same generic response: back up your data and factory reset.&lt;/p&gt;

&lt;p&gt;Watching Gemini handle this live in my terminal was genuinely impressive. It didn't guess, and it didn't give generic tips. It followed a solid systems debugging process:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It systematically eliminated UI layers until only hardware Canvas rendering was left.&lt;/li&gt;
&lt;li&gt;It pulled compiled OEM packages directly from the phone without needing root access.&lt;/li&gt;
&lt;li&gt;It wrote Python scripts on the spot to parse binary DEX string pools and find the right class offsets.&lt;/li&gt;
&lt;li&gt;It analyzed the raw Dalvik bytecode and isolated the exact &lt;code&gt;sget&lt;/code&gt; instruction controlling the overlay.&lt;/li&gt;
&lt;li&gt;It traced an undocumented UI flag back to a persistent Linux system property that had survived reboots.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Watching an AI autonomously reverse-engineer compiled bytecode on a live device—and solve an undocumented framework issue in minutes without touching a single byte of my personal data—was amazing to see.&lt;/p&gt;

&lt;p&gt;This isn't just about autocompleting syntax or generating boilerplate code anymore. This is a real glimpse into the future of systems engineering—and as an engineering student, it completely blew me away.&lt;/p&gt;

</description>
      <category>android</category>
      <category>gemini</category>
      <category>googleai</category>
      <category>debugging</category>
    </item>
    <item>
      <title>The Ghost in the View Transitions API: A Bizarre Debugging Saga with Google AI 👻</title>
      <dc:creator>Omar Afifi</dc:creator>
      <pubDate>Fri, 07 Aug 2026 17:35:35 +0000</pubDate>
      <link>https://dev.to/omarafifi/the-ghost-in-the-view-transitions-api-a-bizarre-debugging-saga-with-google-ai-5aph</link>
      <guid>https://dev.to/omarafifi/the-ghost-in-the-view-transitions-api-a-bizarre-debugging-saga-with-google-ai-5aph</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for &lt;a href="https://dev.to/bugsmash"&gt;DEV's Summer Bug Smash: Clear the Lineup&lt;/a&gt; powered by &lt;a href="https://sentry.io/" rel="noopener noreferrer"&gt;Sentry&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🎨 Project Overview
&lt;/h2&gt;

&lt;p&gt;While working on a major upgrade for my personal portfolio (Check it out here: &lt;a href="https://omar-afifi.com/" rel="noopener noreferrer"&gt;Live Portfolio Link&lt;/a&gt; | &lt;a href="https://github.com/omarafifi-cse/omarafifi-cse.github.io" rel="noopener noreferrer"&gt;GitHub Repo&lt;/a&gt;), I built a feature called the &lt;strong&gt;Theme Showcase&lt;/strong&gt;. When a user first loads the homepage, a slick &lt;code&gt;View Transitions API&lt;/code&gt; animation sweeps across the screen, temporarily switching the site to one of my custom showcase themes (like "Obsidian" or "Midnight"), before gracefully sweeping back to their chosen default theme. It was a 1.7-second "Wow Effect" designed to show off the site's dynamic color system.&lt;/p&gt;

&lt;h2&gt;
  
  
  🐛 Bug Fix
&lt;/h2&gt;

&lt;p&gt;Soon after deploying, I noticed a terrifying, chaotic bug. Sometimes, I would open the website and quickly switch to another tab to check an email, or minimize the browser window completely. When I switched back... &lt;/p&gt;

&lt;p&gt;Instead of my beautiful default theme, the website was permanently stuck on one of the other showcase themes! The animation had frozen mid-way, leaving the site in an awkward intermediate state. While the website was still fully functional and I could manually fix it by selecting my original theme from the dropdown menu, it was a terrible first impression for a professional portfolio. It was as if a ghost had possessed the CSS.&lt;/p&gt;

&lt;p&gt;My first instinct? Smash the refresh button. Refreshing would actually fix the glitch and play the animation perfectly—but &lt;em&gt;only&lt;/em&gt; if I stayed patiently on the tab and watched it finish! If I refreshed and immediately switched tabs again, the ghost would return.&lt;/p&gt;

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

&lt;p&gt;Here is the exact code change we made to exorcise the ghost. &lt;em&gt;(You can view the full fix in this &lt;a href="https://github.com/OmarAfifi-CSE/omarafifi-cse.github.io/commit/e5563dd648562f10317cd382b7876efad9285517" rel="noopener noreferrer"&gt;GitHub Commit&lt;/a&gt;)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Buggy Code (Before):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;transition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startViewTransition&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data-theme&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// If the browser aborts the transition here, the Promise rejects and execution halts forever!&lt;/span&gt;
&lt;span class="nx"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ready&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;animation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;documentElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;animate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="cm"&gt;/* ... */&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;animation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onfinish&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The Bulletproof Code (After):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;isCancelled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// 1. Gracefully handle tab switching&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleVisibility&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hidden&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;isCancelled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data-theme&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;originalTheme&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Instantly restore&lt;/span&gt;
    &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;visibilitychange&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;handleVisibility&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;visibilitychange&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;handleVisibility&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// 2. Bulletproof the View Transition Promise&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;transition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startViewTransition&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isCancelled&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data-theme&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nx"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ready&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isCancelled&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;animation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;documentElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;animate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="cm"&gt;/* ... */&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;animation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onfinish&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;animation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;oncancel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// THE MAGIC FIX: If the browser aborts the transition (background tab), resolve immediately!&lt;/span&gt;
      &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Fallback if startViewTransition throws synchronously&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isCancelled&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data-theme&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🛠️ My Improvements
&lt;/h2&gt;

&lt;p&gt;My technical approach to resolving this was two-fold:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;State Management on Visibility Change:&lt;/strong&gt; I added a &lt;code&gt;visibilitychange&lt;/code&gt; event listener. If the user navigates away, we instantly cancel the sequence and forcefully restore the original theme to prevent intermediate states.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Error Handling for the View Transitions API:&lt;/strong&gt; The core issue was that browsers forcefully abort the &lt;code&gt;startViewTransition&lt;/code&gt; when a tab is hidden to save battery. This caused the &lt;code&gt;transition.ready&lt;/code&gt; Promise to reject. Because there was no &lt;code&gt;.catch()&lt;/code&gt; block, the unhandled rejection caused the entire async sequence to hang permanently. By wrapping the transition in a &lt;code&gt;try/catch&lt;/code&gt; and adding a &lt;code&gt;.catch(() =&amp;gt; resolve())&lt;/code&gt; handler, we guarantee that the execution flow always continues gracefully, even if the browser interrupts it.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  🤖 Best Use of Google AI
&lt;/h2&gt;

&lt;p&gt;I couldn't figure out why this bug was only happening &lt;em&gt;sometimes&lt;/em&gt;, and no errors were showing up in the console. So, I turned to my trusty pair programmer: &lt;strong&gt;Google AI integrated within the Antigravity IDE&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;What's really cool about my workflow is that I chat with my AI agent completely casually in Arabic. It feels incredibly natural, like brainstorming with a senior engineer sitting right next to me. But debugging with AI isn't always a "one-shot" magic trick. It’s an iterative journey. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;(Note: I interact with my AI agent natively in Arabic! For this post, I have translated our debugging conversation. I've kept the original Arabic screenshots on hand just in case the judges want to see them!)&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Phase 1: The Misdiagnosis
&lt;/h3&gt;

&lt;p&gt;At first, I explained the symptoms loosely to the AI: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Me:&lt;/strong&gt; &lt;em&gt;"I have another issue. Sometimes the theme changes to dark suddenly and weird things happen. I don't know what it is."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The AI analyzed the code and thought the &lt;em&gt;animation feature itself&lt;/em&gt; was the bug (assuming it was just annoying the user). So, it enthusiastically offered a "fix" by completely disabling the showcase animation function! &lt;/p&gt;

&lt;p&gt;I had to intervene and correct its assumption: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Me:&lt;/strong&gt; &lt;em&gt;"Bro, no, you misunderstood! The animation at the beginning is natural and fine. What's NOT natural is leaving the website open, going to another tab, and coming back to find it permanently stuck on a new theme."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Phase 2: The Deep Dive
&lt;/h3&gt;

&lt;p&gt;Once I steered the AI in the right direction, it immediately dug into the mechanics of tab-switching. It realized that modern browsers aggressively pause &lt;code&gt;requestAnimationFrame&lt;/code&gt; when a tab goes to the background. &lt;/p&gt;

&lt;p&gt;The AI wrote the &lt;code&gt;visibilitychange&lt;/code&gt; listener, but then we ran into another issue when I was testing it locally with my dev server.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Me:&lt;/strong&gt; &lt;em&gt;"Why does it open on a different theme every time I hit save in my editor? I have to manually refresh to get my original theme back."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The AI assumed I was just being impatient with the 1.7-second animation delay during live-reloads:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;AI:&lt;/strong&gt; &lt;em&gt;"When you save, the dev server refreshes the page. The animation starts, switches to the showcase theme, and then returns. You are just manually refreshing before the 1.7 seconds finish, trapping yourself in a loop! Just wait 1.7 seconds."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Phase 3: The "Aha!" Moment
&lt;/h3&gt;

&lt;p&gt;I knew I wasn't just being impatient. The UI was &lt;em&gt;literally getting stuck&lt;/em&gt;. I pushed the AI to look deeper: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Me:&lt;/strong&gt; &lt;em&gt;"Bro, I'm not rushing. I open it, find it on a new theme, and it is completely STUCK on it. This causes problems if the user goes to another tab. Focus well, think carefully, and take your time."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That’s when the AI hit the absolute jackpot. &lt;/p&gt;

&lt;p&gt;It didn't just hand me a snippet of code; it provided a masterclass on browser mechanics. It analyzed the &lt;code&gt;View Transitions API&lt;/code&gt; behavior and uncovered the true root cause: to save battery and resources, modern browsers aggressively throttle or abort animations when a tab is hidden. This aggressive optimization was causing the &lt;code&gt;transition.ready&lt;/code&gt; promise to reject silently, which hung the entire async sequence because it lacked a &lt;code&gt;.catch()&lt;/code&gt; block.&lt;/p&gt;

&lt;p&gt;I initially asked the AI if we could just "force" the browser to play the animation in the background anyway. Instead of blindly agreeing, the AI pushed back and explained the engineering philosophy behind this: it’s a hard performance constraint built into modern browsers. You can't fight the browser's resource management. Instead, it taught me the "Best Practice": monitor the tab's state (which led to the &lt;code&gt;visibilitychange&lt;/code&gt; listener), gracefully catch any forced abortions (the &lt;code&gt;.catch()&lt;/code&gt; fix), instantly restore the original UI, and respect the browser's rules.&lt;/p&gt;

&lt;p&gt;Thanks to Google AI in the Antigravity IDE, I didn't just fix a bug—I learned exactly &lt;em&gt;why&lt;/em&gt; the browser was behaving that way under the hood. It transformed a frustrating debugging session into a profound learning experience about advanced JavaScript APIs and browser rendering mechanics. This is exactly what the future of pair programming looks like!&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>bugsmash</category>
      <category>webdev</category>
      <category>googleai</category>
    </item>
    <item>
      <title>Reviving Daphq: How I Built an Open-Source, Bare-Metal File Transfer Engine Solo with GitHub Copilot</title>
      <dc:creator>Omar Afifi</dc:creator>
      <pubDate>Wed, 27 May 2026 21:12:32 +0000</pubDate>
      <link>https://dev.to/omarafifi/reviving-daphq-how-i-built-an-open-source-bare-metal-file-transfer-engine-solo-with-github-copilot-5cd8</link>
      <guid>https://dev.to/omarafifi/reviving-daphq-how-i-built-an-open-source-bare-metal-file-transfer-engine-solo-with-github-copilot-5cd8</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/github-2026-05-21"&gt;GitHub Finish-Up-A-Thon Challenge&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Daphq&lt;/strong&gt; is a robust, open-source, cross-platform file transfer application engineered entirely from scratch to deliver hardware-saturating speeds over local area networks (LAN), Wi-Fi, and Mobile Hotspots. &lt;/p&gt;

&lt;p&gt;As a solo developer, I built Daphq to solve a personal frustration: the bloated overhead, privacy concerns, and artificial speed limits of conventional HTTP-based or cloud-reliant file-sharing tools. Daphq bypasses the standard Web/REST layer completely, establishing direct, bare-metal &lt;strong&gt;TCP Sockets&lt;/strong&gt; (&lt;code&gt;Socket&lt;/code&gt; &amp;amp; &lt;code&gt;ServerSocket&lt;/code&gt;) with &lt;code&gt;SocketOption.tcpNoDelay&lt;/code&gt; enabled to eliminate protocol latency and push network hardware to its absolute physical limits.&lt;/p&gt;

&lt;p&gt;Built using &lt;strong&gt;Flutter (Dart)&lt;/strong&gt; for seamless cross-platform performance (currently native on Android and Windows), Daphq features a highly decoupled architecture using the &lt;strong&gt;BLoC/Cubit&lt;/strong&gt; pattern, multi-gigabyte memory protection guards, and background network synchronization, and intelligent user guidance. &lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;The entire project is completely open-source and free to review, fork, or run locally:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🌐 &lt;strong&gt;Official Product Website:&lt;/strong&gt; &lt;a href="http://omar-afifi.com/daphq/" rel="noopener noreferrer"&gt;omar-afifi.com/daphq/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;💻 &lt;strong&gt;GitHub Repository:&lt;/strong&gt; &lt;a href="https://github.com/omarafifi-cse/daphq" rel="noopener noreferrer"&gt;github.com/omarafifi-cse/daphq&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Screenshots in Action
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Android UI Layout&lt;/th&gt;
&lt;th&gt;Windows Desktop Experience&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&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.amazonaws.com%2Fuploads%2Farticles%2Fuhthhu31i9z3r5zp4c2v.png" alt="Android Interface" width="800" height="1666"&gt;&lt;/td&gt;
&lt;td&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.amazonaws.com%2Fuploads%2Farticles%2F6s9qm7a2y59mo7hxic2o.png" alt="Windows Interface" width="799" height="510"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;em&gt;Clean, robust mobile layout with active background service tracking.&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Fluid, modern, and highly responsive desktop experience.&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  The Comeback Story
&lt;/h2&gt;

&lt;p&gt;Daphq was born under high pressure, but after shipping the bare bones of version &lt;strong&gt;v1.0.0&lt;/strong&gt;, it was essentially abandoned in my local repository directory. The project had immense raw speed potential, but it was plagued by architectural gaps, clunky user workflows, and high-stress bugs that made it unviable for everyday users. &lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;GitHub Finish-Up-A-Thon Challenge&lt;/strong&gt; was the exact catalyst I needed to dust off the codebase and transform it from a fragile command-like utility into a highly polished, resilient consumer application (&lt;code&gt;v2.3.0&lt;/code&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  The Deep Evolution: v1.0.0 vs. v2.3.0
&lt;/h3&gt;

&lt;p&gt;Here is exactly how the architecture and UX matured over the course of this challenge:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Discovery, Connectivity &amp;amp; Resilience:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Before (v1.0.0):&lt;/em&gt; Zero automation. Users manually typed cryptic IPv4 addresses, and any connection drop required a full app restart.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;After (v2.3.0):&lt;/em&gt; A custom &lt;strong&gt;UDP Auto-Discovery Service&lt;/strong&gt; with an integrated watchdog. Backed by a rewritten, &lt;strong&gt;Bulletproof Receiver Engine&lt;/strong&gt;, the app instantly bounces back if a connection drops or cancels abruptly—never getting stuck on "Connecting...".&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;User Interface, Fluidity &amp;amp; Storage:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Before (v1.0.0):&lt;/em&gt; Rigid layouts, storage bloat from cached files, and main-thread hangs (UI freezing) when selecting large directories.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;After (v2.3.0):&lt;/em&gt; A unified dashboard anchored by an &lt;strong&gt;"Orbital Radar"&lt;/strong&gt; and Non-Blocking Async UI. Sharing 1,000+ files is now buttery smooth with zero lagging. Plus, it features &lt;strong&gt;Persistent Folders&lt;/strong&gt; to permanently remember your download path and &lt;strong&gt;Smart Auto-Cleaning&lt;/strong&gt; to wipe cache bloat automatically.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Smart User Guidance:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Before (v1.0.0):&lt;/em&gt; Users suffered from slow transfers due to ISP router bottlenecks without knowing why.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;After (v2.3.0):&lt;/em&gt; Integrated a real-time speed monitor that triggers a &lt;strong&gt;Smart Hotspot Guide&lt;/strong&gt; if speeds drop below 2.0 MB/s, guiding users to utilize 5GHz direct hotspots for up to 10x faster speeds.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory Safety &amp;amp; Disk I/O Flow Control:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Before (v1.0.0):&lt;/em&gt; High risk of Out-Of-Memory (OOM) crashes when streaming multi-gigabyte files due to unthrottled asynchronous disk reads.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;After (v2.3.0):&lt;/em&gt; Implemented zero-copy byte slicing (&lt;code&gt;Uint8List.sublistView&lt;/code&gt;) and &lt;strong&gt;Natural Socket Backpressure&lt;/strong&gt; (&lt;code&gt;await socket.flush()&lt;/code&gt;). Daphq now self-throttles to match real-time network speeds, saving physical RAM.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deep System-Wide Integrations:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Before (v1.0.0):&lt;/em&gt; Isolated sandboxed app.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;After (v2.3.0):&lt;/em&gt; Deployed deep OS hooks, including native Android Share Menu entry and background execution. On Android, &lt;strong&gt;Instant Wake-up&lt;/strong&gt; intelligently turns on the screen for incoming requests. On Windows, the &lt;strong&gt;Context Menu Shell Extension&lt;/strong&gt; was completely rebuilt to be unbreakable, eliminating all File Explorer hangs.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  My Experience with GitHub Copilot
&lt;/h2&gt;

&lt;p&gt;As a solo developer, you are the software architect, the UI designer, the QA technician, and the DevOps engineer all at once. Time is your greatest bottleneck. Throughout this sprint, GitHub Copilot didn’t just autocomplete lines of boilerplate—it acted as an incredibly knowledgeable senior engineering peer, unlocking complex architectural problems.&lt;/p&gt;

&lt;p&gt;Here are three distinct breakthroughs where GitHub Copilot completely changed the trajectory of the project:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Solving the Math of the Orbital Radar UI
&lt;/h3&gt;

&lt;p&gt;I wanted the home dashboard to feel alive with an evenly distributed, deterministic "Orbital Radar" circle showing available peer devices. Distributing dynamic widgets symmetrically on a circular plane without them overlapping can quickly become a headache.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Copilot's Intervention:&lt;/strong&gt; By providing Copilot with my layout boundaries and device state arrays, it instantly mapped out the exact trigonometric distribution math using &lt;code&gt;cos&lt;/code&gt; and &lt;code&gt;sin&lt;/code&gt; over a mapped index. Furthermore, it proactively recommended assigning unique &lt;code&gt;ValueKey&lt;/code&gt;s to the dynamically generated widgets, preventing unnecessary re-paints and maximizing rendering performance on low-end hardware.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Eliminating the Windows FFI Mutex Deadlock
&lt;/h3&gt;

&lt;p&gt;A critical, highly frustrating platform bug on Windows caused Daphq to accidentally trigger background execution loops whenever a user performed completely unrelated right-click shell actions elsewhere in the OS. Worse, exiting the app caused a stubborn Dart VM deadlock.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Copilot's Intervention:&lt;/strong&gt; I explained the process conflict to Copilot. It guided me through structuring a clean, platform-specific local session mutex check. When it came to bypassing the Dart VM shutdown hang, Copilot instantly generated the safe native Windows FFI code block to execute a clean &lt;code&gt;ExitProcess&lt;/code&gt; command, killing the deadlocks instantly and safely.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Engineering Flow-Safe Dynamic Streaming Buffer
&lt;/h3&gt;

&lt;p&gt;To stop aggressive disk-read bursts from triggering Out-Of-Memory exceptions, I needed a way to throttle file streams smoothly based on network capacity.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Copilot's Intervention:&lt;/strong&gt; Copilot analyzed my asynchronous data-sync loop and suggested transitioning away from arbitrary chunk delay timers. It helped refactor the code to explicitly await &lt;code&gt;socket.flush()&lt;/code&gt; based on monitored &lt;code&gt;bytesBuffered&lt;/code&gt; thresholds. The result was an automated backpressure loop that scales throughput up or down safely based on real-time network conditions.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Standard path for larger files: stream in chunks&lt;/span&gt;
&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;reader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fileToRead&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;openRead&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;reader&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_isCancelled&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="s"&gt;"Transfer Cancelled"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="n"&gt;bytesBuffered&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bytesBuffered&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AppConstants&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;socketFlushThresholdBytes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Awaited flush: provides backpressure so the disk read loop&lt;/span&gt;
        &lt;span class="c1"&gt;// runs at network speed, not disk speed. Without this, a fast&lt;/span&gt;
        &lt;span class="c1"&gt;// UFS device would read the entire file in &amp;lt;500ms before any&lt;/span&gt;
        &lt;span class="c1"&gt;// onUpdate fires, causing the snapshot/stutter UI bug.&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;flush&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;bytesBuffered&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_isCancelled&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="s"&gt;"Transfer Cancelled"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;SocketException&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"reset by peer"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="s"&gt;"Receiver disconnected during transfer."&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;rethrow&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// ... UI update logic &lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Verdict
&lt;/h2&gt;

&lt;p&gt;Building Daphq independently meant encountering dozens of architectural edge cases. Having GitHub Copilot available to instantly generate platform-specific guards, analyze multi-threaded socket behavior, and handle tedious Win32 registry bindings allowed me to focus on what mattered most: delivering a breathtakingly fast file transfer engine. &lt;/p&gt;

&lt;p&gt;Daphq went from an unpolished repository artifact to a production-grade open-source engine capable of hitting &lt;strong&gt;80-90 MB/s (~93% bandwidth saturation)&lt;/strong&gt; over a standard local Wi-Fi 5 connection. I couldn't be prouder of how this challenge ended.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>githubchallenge</category>
      <category>flutter</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
