<?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: shaojie gong</title>
    <description>The latest articles on DEV Community by shaojie gong (@shaojie).</description>
    <link>https://dev.to/shaojie</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%2F4015883%2Ff495cde7-b3bb-432b-8da3-ff6aa5217ebe.png</url>
      <title>DEV Community: shaojie gong</title>
      <link>https://dev.to/shaojie</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/shaojie"/>
    <language>en</language>
    <item>
      <title>My extension "worked" — it just quietly saved half of every conversation</title>
      <dc:creator>shaojie gong</dc:creator>
      <pubDate>Tue, 14 Jul 2026 14:54:53 +0000</pubDate>
      <link>https://dev.to/shaojie/my-extension-worked-it-just-quietly-saved-half-of-every-conversation-3phj</link>
      <guid>https://dev.to/shaojie/my-extension-worked-it-just-quietly-saved-half-of-every-conversation-3phj</guid>
      <description>&lt;p&gt;My extension had a bug that's the worst kind: it looked like it worked.&lt;/p&gt;

&lt;p&gt;You'd save a Claude conversation, open it up, and there'd be your questions — every single one of them — and none of Claude's answers. Half the conversation, gone, but neatly. No error, no crash. Just quietly wrong.&lt;/p&gt;

&lt;p&gt;I want to walk through the day I spent chasing this, because it taught me something I keep having to relearn.&lt;/p&gt;

&lt;p&gt;Some background. My extension reads AI chat pages — ChatGPT, Claude, Gemini, Perplexity — and pulls the conversation out so you can save it. None of these sites give you an API for that. So you're reading the raw HTML, hunting for the CSS selector that wraps each message. And those selectors are not documented anywhere, because they were never meant for you. They change whenever the site ships a redesign.&lt;/p&gt;

&lt;p&gt;Here's the mistake I made, more than once: I guessed.&lt;/p&gt;

&lt;p&gt;I'd think, okay, Claude's assistant messages are probably still .font-claude-message, I remember that from a while back. Ship it. And it'd be wrong, because they'd quietly changed it, and my selector matched exactly zero elements. The user-message selector still worked, so the extension happily grabbed all the user turns and none of the assistant ones. Hence: half a conversation.&lt;/p&gt;

&lt;p&gt;I guessed for Gemini too. Wrong there as well, just in the opposite direction — I caught the AI answers and dropped the questions.&lt;/p&gt;

&lt;p&gt;At some point I got tired of being wrong and did the obvious thing I should've done first: I wrote a little probe script. Forty lines. You paste it into the browser console on the actual page, logged into your actual account, and it just tells you the truth — which selectors match, how many elements each one hits, and a snippet of the text inside so you can tell whether it grabbed a real message or the navigation bar.&lt;/p&gt;

&lt;p&gt;The output was almost funny in how clear it was. For Claude:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ 4  [data-testid='user-message'] — sample: "人工智能对未来有什么影响" (my actual question: "what's AI's impact on the future")&lt;/li&gt;
&lt;li&gt;✅ 4  [data-is-streaming] — sample: "Claude responded: 人工智能对未来的影响是多方面的…"&lt;/li&gt;
&lt;li&gt;❌ 0  .font-claude-message&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There it was. Zero. The thing I'd been confidently shipping matched nothing. The real selector for an answer was an attribute I wouldn't have guessed in a hundred years.&lt;/p&gt;

&lt;p&gt;I ran the probe on all four sites. Twenty minutes of pasting a script into a console did what a week of guessing couldn't. ChatGPT and Gemini turned out to already be fine. Claude and Perplexity were both wrong, in different ways, for different reasons.&lt;/p&gt;

&lt;p&gt;Then the images.&lt;/p&gt;

&lt;p&gt;Someone's going to ask "can it save pictures too?" so I looked into it, and this is where it gets humbling. On Gemini, one image in the conversation had a normal https link — fine, savable, might expire but savable. The next image in the same chat was a blob: URL. If you've never run into these: a blob URL is a reference to something sitting in the browser's memory for that one tab, right now. Close the tab and it's gone. There is no version of my extension, or anyone's, that can save that image, because the moment you leave the page the thing it points to stops existing.&lt;/p&gt;

&lt;p&gt;So I had a choice. Pretend, and save a link that will 100% be a broken image later. Or be honest and write, right there in the saved text, "this image couldn't be saved — go look at the original." I went with honest. A broken image icon is worse than a sentence telling you the truth.&lt;/p&gt;

&lt;p&gt;Claude does something different again — the uploaded file lives outside the message element entirely, so I can't grab the image, but I &lt;em&gt;can&lt;/em&gt; grab the filename. So at least the saved note says "attachment: dog.jpeg" instead of pretending nothing was ever there.&lt;/p&gt;

&lt;p&gt;A couple of other things broke that day, for the record, because it wasn't only selectors:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The copy button did nothing. Turned out the panel runs in an iframe, and the browser blocks the clipboard API inside iframes unless the parent hands over permission. One attribute fixed it. Took me an embarrassing while to find.&lt;/li&gt;
&lt;li&gt;"Extension context invalidated" errors everywhere — which just means I reloaded the extension while an old page still had the old code running in it. Harmless, but it looks alarming in the error log, so I made it fail quietly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's the thing I keep relearning, and the reason I'm writing this down so I maybe stop forgetting it:&lt;/p&gt;

&lt;p&gt;I cannot guess what the page looks like. I have a memory of what a selector used to be, and that memory is worse than useless, because it's confident and wrong. The page in front of the user is the only source of truth. A forty-line script that reads that page beats my best guess every single time.&lt;/p&gt;

&lt;p&gt;Still 0 users, still $0. But the extension now actually saves the whole conversation on all four sites, which it demonstrably did not do this morning. I'll take it.&lt;/p&gt;

&lt;p&gt;If you build anything that scrapes a page you don't control: write the probe first. Don't be me at 10am.&lt;/p&gt;

&lt;p&gt;— building NotebookBloom in public, #2&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>buildinpublic</category>
      <category>productivity</category>
    </item>
    <item>
      <title>My friend in med school kept complaining about NotebookLM, so I built the fix</title>
      <dc:creator>shaojie gong</dc:creator>
      <pubDate>Mon, 13 Jul 2026 13:45:54 +0000</pubDate>
      <link>https://dev.to/shaojie/my-friend-in-med-school-kept-complaining-about-notebooklm-so-i-built-the-fix-2i5e</link>
      <guid>https://dev.to/shaojie/my-friend-in-med-school-kept-complaining-about-notebooklm-so-i-built-the-fix-2i5e</guid>
      <description>&lt;p&gt;I'll be upfront: I'm not a daily NotebookLM user.&lt;/p&gt;

&lt;p&gt;I'm a developer, and what I do read constantly is papers — mostly arXiv in ML. I go hunting for new ones pretty much every day, so I know the pain of wrangling sources and references firsthand.&lt;/p&gt;

&lt;p&gt;But the actual reason I started building this is my friend in med school.&lt;/p&gt;

&lt;p&gt;She lives inside NotebookLM to study. And she kept hitting the same walls, over and over:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It generates flashcards, but she can't get them into Anki — which is where she actually reviews. Rebuilding them by hand is hours of work.&lt;/li&gt;
&lt;li&gt;The citations are vague. A number next to an answer, a highlighted chunk of a 40-page PDF, no page number, nothing clean she can drop into a reference list.&lt;/li&gt;
&lt;li&gt;Every notebook is its own island. No way to search across all of them once she had a dozen.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At first I figured someone must have solved this already. So I looked. There are a couple of extensions out there, but most of them just do plain export — nothing actually built around doing research and studying. And it's clearly not just her: dig around and you find the same complaints everywhere from students and researchers.&lt;/p&gt;

&lt;p&gt;So I started building one.&lt;/p&gt;

&lt;h3&gt;
  
  
  What it does so far
&lt;/h3&gt;

&lt;p&gt;It's a Chrome extension that sits on top of NotebookLM:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pulls citations out and turns them into real APA / MLA / Chicago / BibTeX references you can paste straight into a paper&lt;/li&gt;
&lt;li&gt;Searches across &lt;em&gt;all&lt;/em&gt; your notebooks at once&lt;/li&gt;
&lt;li&gt;Exports the AI flashcards to Anki — but with the source printed on the back of each card, so when you review you can actually check where it came from instead of blindly trusting the AI&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last one is the piece I care about most, because it came from a real person's actual problem, not a feature list.&lt;/p&gt;

&lt;h3&gt;
  
  
  The part nobody tells you
&lt;/h3&gt;

&lt;p&gt;The code was the easy part. NotebookLM has no public API, so everything works by reading the page's HTML directly. Which means the day Google redesigns something, parts of this can just break, and I'll be chasing it. Every tool in this space has the same problem — so, here we are.&lt;/p&gt;

&lt;h3&gt;
  
  
  Where I'm at
&lt;/h3&gt;

&lt;p&gt;Being honest, because that's the whole point of posting this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Users: 0&lt;/li&gt;
&lt;li&gt;Paying: 0&lt;/li&gt;
&lt;li&gt;Revenue: $0&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's still in development. I'm putting it out this early because I'd rather build in the open and get it wrong in public than polish it forever in private and never ship.&lt;/p&gt;

&lt;p&gt;So, a genuine question for anyone who uses NotebookLM for research or studying: what's the single thing that annoys you most about it? That's literally going to be my roadmap.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
