<?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: Abubakr Alsheikh</title>
    <description>The latest articles on DEV Community by Abubakr Alsheikh (@abubakr_alsheikh).</description>
    <link>https://dev.to/abubakr_alsheikh</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1917386%2F1732e3ce-9e6b-457b-88b6-5cc54a300f91.jpeg</url>
      <title>DEV Community: Abubakr Alsheikh</title>
      <link>https://dev.to/abubakr_alsheikh</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/abubakr_alsheikh"/>
    <language>en</language>
    <item>
      <title>From "Lazy Script" to "Local AI Agent": Max CLI is now a Media &amp; Document Powerhouse</title>
      <dc:creator>Abubakr Alsheikh</dc:creator>
      <pubDate>Tue, 10 Feb 2026 15:04:12 +0000</pubDate>
      <link>https://dev.to/abubakr_alsheikh/from-lazy-script-to-local-ai-agent-max-cli-is-now-a-media-document-powerhouse-44ad</link>
      <guid>https://dev.to/abubakr_alsheikh/from-lazy-script-to-local-ai-agent-max-cli-is-now-a-media-document-powerhouse-44ad</guid>
      <description>&lt;p&gt;A few months ago, I shared the first version of &lt;strong&gt;Max CLI&lt;/strong&gt;—a tool I built because I was tired of Googling &lt;code&gt;ffmpeg&lt;/code&gt; flags and uploading private PDFs to sketchy websites. &lt;/p&gt;

&lt;p&gt;The response was great, but the feedback was clear: &lt;em&gt;"Make it do more. Make it smarter."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Today, I’m excited to share the major evolution of &lt;strong&gt;Max&lt;/strong&gt;. It’s no longer just a "flag-saver"—it’s a local-first, context-aware AI agent for media, documents, and web automation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Repo:&lt;/strong&gt; &lt;a href="https://github.com/Abubakr-Alsheikh/max-cli" rel="noopener noreferrer"&gt;github.com/Abubakr-Alsheikh/max-cli&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What’s New in Max? 🚀
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. The Local Media Engine (FFmpeg on Steroids)
&lt;/h3&gt;

&lt;p&gt;I finally integrated a full Video/Audio suite. You get the power of FFmpeg with "lazy" human-readable commands.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Smart Compression:&lt;/strong&gt; &lt;code&gt;max video compress&lt;/code&gt; with quality levels (&lt;code&gt;balanced&lt;/code&gt;, &lt;code&gt;high&lt;/code&gt;, &lt;code&gt;max&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Quick Fixes:&lt;/strong&gt; &lt;code&gt;max video louder&lt;/code&gt; (boost audio), &lt;code&gt;max video cut&lt;/code&gt; (frame-perfect trimming), and &lt;code&gt;max video snap&lt;/code&gt; (high-res thumbnails).&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;High-Quality GIFs:&lt;/strong&gt; Uses a 2-pass palette generator to avoid that grainy 1990s GIF look.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. The PDF "Bundle" Pipeline
&lt;/h3&gt;

&lt;p&gt;Document management is now professional-grade. The standout feature is the &lt;strong&gt;Bundle&lt;/strong&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;# Merges a folder of PDFs, compresses the result, and cleans up temp files in one go.&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;max pdf bundle ./Invoices &lt;span class="nt"&gt;-o&lt;/span&gt; 2024_Tax_Report.pdf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also &lt;strong&gt;Lock&lt;/strong&gt; PDFs with AES-256, &lt;strong&gt;Stamp&lt;/strong&gt; watermarks, and &lt;strong&gt;Rip&lt;/strong&gt; embedded images out of documents.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Universal "Smart" Downloader (&lt;code&gt;max grab&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;I wrapped &lt;code&gt;yt-dlp&lt;/code&gt; to create a downloader that understands your intent. Instead of 20-character strings, use &lt;strong&gt;Quality Presets&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;s&lt;/code&gt; (Small): 480p / 64k audio (Data saver)&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;x&lt;/code&gt; (Xtreme): 4K / 320k audio (Archival)&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Smart Playlists:&lt;/strong&gt; Max now peeks at URLs, counts items, and asks for confirmation before filling your drive.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. AI Vision &amp;amp; "Nano Banana" (Image Gen)
&lt;/h3&gt;

&lt;p&gt;Max now has eyes. Since integrating with &lt;strong&gt;Google Gemini (Nano Banana)&lt;/strong&gt; and OpenAI, Max can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Analyze:&lt;/strong&gt; &lt;code&gt;max ai analyze error.png&lt;/code&gt; — troubleshoot terminal errors from a screenshot.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Create/Edit:&lt;/strong&gt; &lt;code&gt;max ai create "A pixel art cabin"&lt;/code&gt; or &lt;code&gt;max ai edit photo.jpg "Make the sky purple"&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The "Killer" Upgrade: Context Awareness 🧠
&lt;/h2&gt;

&lt;p&gt;In the previous version, the AI was just a translator. In the new Max, the AI is &lt;strong&gt;Context-Aware&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;When you run &lt;code&gt;max ai ask&lt;/code&gt;, Max scans your current directory and injects the file list into the prompt. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Old Way:&lt;/strong&gt; &lt;code&gt;max ai ask "compress the video named final_v2_edit.mp4"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;The Max Way:&lt;/strong&gt; &lt;code&gt;max ai ask "compress the video"&lt;/code&gt; 

&lt;ul&gt;
&lt;li&gt;  &lt;em&gt;Max sees &lt;code&gt;final_v2_edit.mp4&lt;/code&gt; in your folder and resolves the intent automatically.&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;h2&gt;
  
  
  New Developer Utilities 🛠️
&lt;/h2&gt;

&lt;p&gt;I added features specifically for my own dev workflow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;&lt;code&gt;max share&lt;/code&gt;&lt;/strong&gt;: Generates an ASCII QR code in your terminal. I use this daily to send &lt;code&gt;localhost&lt;/code&gt; URLs to my phone for testing.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;&lt;code&gt;max paste&lt;/code&gt;&lt;/strong&gt;: Take an image from your system clipboard and save it directly to a file.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;&lt;code&gt;max smart-sort&lt;/code&gt;&lt;/strong&gt;: AI-powered semantic organization. It reads your filenames and groups them into folders like &lt;code&gt;/Invoices&lt;/code&gt;, &lt;code&gt;/Personal&lt;/code&gt;, or &lt;code&gt;/Code&lt;/code&gt; based on meaning, not just extension.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Refined Tech Stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Provider Agnostic:&lt;/strong&gt; Full support for Google Gemini via the OpenAI Compatibility layer.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Speed:&lt;/strong&gt; Added Node.js/Deno detection for faster YouTube signature decoding.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;UI:&lt;/strong&gt; Switched to a "Transient" progress bar system—it shows beautiful progress during the task and disappears when done to keep your terminal clean.&lt;/li&gt;
&lt;/ul&gt;




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

&lt;p&gt;I’m currently exploring &lt;strong&gt;Local Transcription (Whisper)&lt;/strong&gt; and &lt;strong&gt;Semantic Search&lt;/strong&gt; (RAG) so you can ask questions about your local documents directly.&lt;/p&gt;

&lt;p&gt;Max is Open Source and built for the community. If you hate browser-based converters and love the terminal, I’d love for you to try it out.&lt;/p&gt;

&lt;p&gt;⭐ &lt;strong&gt;Check out the code:&lt;/strong&gt; &lt;a href="https://github.com/Abubakr-Alsheikh/max-cli" rel="noopener noreferrer"&gt;github.com/Abubakr-Alsheikh/max-cli&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is the one browser-based tool you still use that you wish lived in your terminal? Let's build a command for it!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>ai</category>
      <category>opensource</category>
      <category>programming</category>
    </item>
    <item>
      <title>I built a "Lazy" AI Terminal Assistant because I hate memorizing flags</title>
      <dc:creator>Abubakr Alsheikh</dc:creator>
      <pubDate>Wed, 26 Nov 2025 23:40:11 +0000</pubDate>
      <link>https://dev.to/abubakr_alsheikh/i-built-a-lazy-ai-terminal-assistant-because-i-hate-memorizing-flags-262l</link>
      <guid>https://dev.to/abubakr_alsheikh/i-built-a-lazy-ai-terminal-assistant-because-i-hate-memorizing-flags-262l</guid>
      <description>&lt;h2&gt;
  
  
  The Problem: I love the terminal, but I hate the friction.
&lt;/h2&gt;

&lt;p&gt;I live in the terminal. But let's be honest: doing simple things often feels like solving a puzzle.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Want to compress a folder of images? Time to look up ImageMagick flags.&lt;/li&gt;
&lt;li&gt;  Want to merge PDFs? Time to upload your private docs to some random "Free PDF Merger" website.&lt;/li&gt;
&lt;li&gt;  Want to rename 100 files? Time to write a throwaway Python script that you’ll delete 5 minutes later.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I wanted a tool that was &lt;strong&gt;Fast&lt;/strong&gt;, &lt;strong&gt;Local&lt;/strong&gt;, and unapologetically &lt;strong&gt;Lazy&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;So, I built &lt;strong&gt;Max&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Meet Max CLI ⚡
&lt;/h2&gt;

&lt;p&gt;Max is an open-source, modular command-line tool built with Python. It automates the mundane, local tasks we deal with daily, and it includes an &lt;strong&gt;AI Copilot&lt;/strong&gt; to handle the rest.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Repo:&lt;/strong&gt; &lt;a href="https://github.com/Abubakr-Alsheikh/max-cli" rel="noopener noreferrer"&gt;github.com/Abubakr-Alsheikh/max-cli&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is why it's different: &lt;strong&gt;It prioritizes local execution.&lt;/strong&gt; When you compress images or order files, it happens on your machine. No cloud uploads. Speed is the priority.&lt;/p&gt;

&lt;h2&gt;
  
  
  What can it do? (The "Lazy" Features)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Smart Image Compression
&lt;/h3&gt;

&lt;p&gt;Stop Googling compression algorithms. Max recursively scans folders, detects images, and optimizes them.&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;# Compress an entire folder of photos to JPEGs (Quality 80)&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;max images compress ./VacationPhotos &lt;span class="nt"&gt;--quality&lt;/span&gt; 80 &lt;span class="nt"&gt;--jpeg&lt;/span&gt;

&lt;span class="c"&gt;# Resize a huge banner to 50% scale&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;max img compress banner.png &lt;span class="nt"&gt;--scale&lt;/span&gt; 50
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Sanity-Saving File Ordering
&lt;/h3&gt;

&lt;p&gt;I constantly have folders full of &lt;code&gt;scan.pdf&lt;/code&gt;, &lt;code&gt;scan(1).pdf&lt;/code&gt;, &lt;code&gt;document.docx&lt;/code&gt;. Max cleans this up by adding numerical ordering, with a safety-first "Dry Run" mode.&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;# Preview what will happen&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;max files order ./Downloads &lt;span class="nt"&gt;--dry-run&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;DRY RUN] Would rename &lt;span class="s1"&gt;'report.pdf'&lt;/span&gt; -&amp;gt; &lt;span class="s1"&gt;'1_report.pdf'&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;DRY RUN] Would rename &lt;span class="s1"&gt;'thesis.docx'&lt;/span&gt; -&amp;gt; &lt;span class="s1"&gt;'2_thesis.docx'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. The AI Copilot 🤖
&lt;/h3&gt;

&lt;p&gt;This is the killer feature. Max uses the OpenAI SDK to translate &lt;em&gt;Natural Language&lt;/em&gt; into &lt;em&gt;CLI Arguments&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;I didn't want a chatbot that just talks. I wanted a chatbot that &lt;strong&gt;does&lt;/strong&gt;. Max reads its own documentation, understands the available tools, and constructs the command for you.&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="nv"&gt;$ &lt;/span&gt;max ai ask &lt;span class="s2"&gt;"Combine all PDFs in the 'Contracts' folder"&lt;/span&gt;

&lt;span class="c"&gt;# Max thinks... and suggests:&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; max pdf merge ./Contracts
&lt;span class="c"&gt;# Run this command? [y/N]: &lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Architecture (For the Nerds 🤓)
&lt;/h2&gt;

&lt;p&gt;I didn't want a messy script. Max is built as a &lt;strong&gt;Modular Monolith&lt;/strong&gt; using modern Python standards.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Framework:&lt;/strong&gt; &lt;a href="https://typer.tiangolo.com/" rel="noopener noreferrer"&gt;Typer&lt;/a&gt; (for that sweet CLI developer experience).&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;UI:&lt;/strong&gt; &lt;a href="https://github.com/Textualize/rich" rel="noopener noreferrer"&gt;Rich&lt;/a&gt; (for beautiful progress bars, tables, and error handling).&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Engine:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;Pillow&lt;/code&gt; for image logic.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;PyMuPDF&lt;/code&gt; for PDF manipulation.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;OpenAI&lt;/code&gt; for the intent classification layer.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;The project structure separates &lt;strong&gt;Core Logic&lt;/strong&gt; (pure Python) from the &lt;strong&gt;Interface&lt;/strong&gt; (CLI commands), making it easy to test and scalable.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Roadmap 🔮
&lt;/h2&gt;

&lt;p&gt;Max is just getting started. I am actively working on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;🎥 Local Media Engine:&lt;/strong&gt; Wrapping &lt;code&gt;ffmpeg&lt;/code&gt; to allow natural language video/audio conversion (&lt;code&gt;max ai ask "Convert this MKV to MP4"&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;🧠 Conversation Memory:&lt;/strong&gt; A persistent chat mode so Max remembers context between commands.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;🎨 Generative AI:&lt;/strong&gt; Creating assets locally (&lt;code&gt;max create image "Cyberpunk city"&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;🛡 Sandboxing:&lt;/strong&gt; Scoped permissions so the AI can't touch system files without approval.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Contribute!
&lt;/h2&gt;

&lt;p&gt;This is an open-source project, and I want it to be the ultimate developer companion.&lt;/p&gt;

&lt;p&gt;If you hate memorizing flags or just want to see how to build a production-grade CLI in Python, check out the code.&lt;/p&gt;

&lt;p&gt;⭐ &lt;strong&gt;Star the repo:&lt;/strong&gt; &lt;a href="https://github.com/Abubakr-Alsheikh/max-cli" rel="noopener noreferrer"&gt;github.com/Abubakr-Alsheikh/max-cli&lt;/a&gt;&lt;br&gt;
🙌 &lt;strong&gt;Pull Requests are welcome!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let me know in the comments: &lt;strong&gt;What is the one terminal command you always forget and wish an AI could handle for you?&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>opensource</category>
      <category>productivity</category>
      <category>cli</category>
    </item>
    <item>
      <title>I Built an AI Manga Creator with Next.js and Gemini's "Visual Memory"</title>
      <dc:creator>Abubakr Alsheikh</dc:creator>
      <pubDate>Sun, 14 Sep 2025 12:27:17 +0000</pubDate>
      <link>https://dev.to/abubakr_alsheikh/i-built-an-ai-manga-creator-with-nextjs-and-geminis-visual-memory-14md</link>
      <guid>https://dev.to/abubakr_alsheikh/i-built-an-ai-manga-creator-with-nextjs-and-geminis-visual-memory-14md</guid>
      <description>&lt;p&gt;I just wrapped up my submission for the Google Nano Banana Hackathon, and I'm incredibly excited to share what I built: &lt;strong&gt;NanoManga Studio&lt;/strong&gt;. It's an AI-powered web app that lets you generate entire, visually-consistent manga stories from a simple idea.&lt;/p&gt;

&lt;p&gt;The biggest problem with AI image generation for storytelling is consistency. How do you make sure your hero has the same hairstyle on page 3 as they did on page 1? I decided to tackle this head-on.&lt;/p&gt;

&lt;p&gt;🚀 &lt;strong&gt;Live Demo:&lt;/strong&gt; &lt;a href="https://nanomanga-studio.vercel.app/" rel="noopener noreferrer"&gt;nanomanga-studio.vercel.app&lt;/a&gt;&lt;br&gt;
💻 &lt;strong&gt;GitHub Repo (Stars are appreciated! ⭐):&lt;/strong&gt; &lt;a href="https://github.com/Abubakr-Alsheikh/nanomanga-studio" rel="noopener noreferrer"&gt;github.com/Abubakr-Alsheikh/nanomanga-studio&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  The Tech Stack
&lt;/h2&gt;

&lt;p&gt;I wanted a modern, fast, and type-safe stack that would let me iterate quickly for the hackathon.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Framework:&lt;/strong&gt; Next.js 15 (App Router)&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;UI:&lt;/strong&gt; shadcn/ui &amp;amp; Tailwind CSS&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;State Management:&lt;/strong&gt; Simple React &lt;code&gt;useState&lt;/code&gt; lifted to the root component.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;AI:&lt;/strong&gt; Google AI JavaScript SDK (&lt;code&gt;@google/generative-ai&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Deployment:&lt;/strong&gt; Vercel&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  The Core Innovation: Giving the AI a "Visual Memory"
&lt;/h2&gt;

&lt;p&gt;The magic of this project is in the multi-modal prompting. Instead of just sending text, I created a rich context package for the &lt;code&gt;gemini-2.5-flash-image-preview&lt;/code&gt; (or "Nano Banana") model for every new page generation.&lt;/p&gt;

&lt;p&gt;Here's the breakdown of the &lt;code&gt;fetch&lt;/code&gt; call from the &lt;code&gt;PageGenerator&lt;/code&gt; component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// file: app/components/page-generator.tsx&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleGeneratePage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &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;// ... state checks and loading indicators&lt;/span&gt;

  &lt;span class="c1"&gt;// 1. Get previous pages and selected assets for this scene&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;previousPages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;currentPageNumber&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&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;selectedAssets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;allAssets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;asset&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;selectedAssetIds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;asset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

  &lt;span class="c1"&gt;// 2. Craft a highly specific text prompt&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fullPrompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`
    **Manga Page Generation**
    **Page Number:** &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;currentPageNumber&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;
    **Page Description:** &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;pagePrompt&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; // e.g., "Panel 1: Close-up on Kenji..."

    **INSTRUCTIONS FOR IMAGE REFERENCES:**
    - The FIRST &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;previousPages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; images are previous pages for continuity.
    - The REMAINING &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;selectedAssets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; images are specific assets for THIS page.
  `&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;// 3. Assemble the visual context array (THE KEY PART!)&lt;/span&gt;
  &lt;span class="c1"&gt;// We extract the base64 data from our data URLs&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pageImages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;previousPages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;imageUrl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;1&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;assetImages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;selectedAssets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;asset&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;asset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;imageUrl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="c1"&gt;// Previous pages go FIRST to establish context&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;baseImages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;pageImages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;assetImages&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="c1"&gt;// 4. Make the API call&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/generate&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;fullPrompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;baseImages&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// ... handle response&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By explicitly telling the AI how to interpret the sequence of images, it can maintain character appearance, clothing, and even damage across multiple pages.&lt;/p&gt;

&lt;h2&gt;
  
  
  AI as an Art Director and Story Editor
&lt;/h2&gt;

&lt;p&gt;Before even generating images, I use &lt;code&gt;gemini-2.5-flash&lt;/code&gt; with persona-based prompting to structure the entire project.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Story Planning:&lt;/strong&gt; I ask the AI to act as a "master manga editor" and return a complete story plan in a strict JSON format. This plan includes character descriptions, environments, and a page-by-page plot that follows a classic narrative arc.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Asset Design:&lt;/strong&gt; When inspiring asset ideas, the AI takes on two roles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  A &lt;strong&gt;"character concept artist"&lt;/strong&gt; that designs a full-body character sheet on a neutral background.&lt;/li&gt;
&lt;li&gt;  A &lt;strong&gt;"background artist"&lt;/strong&gt; that designs an atmospheric, character-free environment shot.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;This ensures the generated assets are clean and perfect for use as references later on.&lt;/p&gt;

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

&lt;p&gt;This project was a blast. It hammered home that the future of generative AI isn't just about single, powerful prompts. It's about building systems that maintain context, create feedback loops, and allow for true human-AI collaboration. The multi-modal capabilities of models like Gemini are the key to unlocking this.&lt;/p&gt;

&lt;p&gt;I'd love for you to try it out and see what you can create! Let me know what you think in the comments. What would you build with this kind of "visual memory"?&lt;/p&gt;

&lt;p&gt;If you want to read my full technical write-up on Kaggle:&lt;br&gt;
&lt;a href="https://www.kaggle.com/competitions/banana/writeups/nanomanga-studio" rel="noopener noreferrer"&gt;https://www.kaggle.com/competitions/banana/writeups/nanomanga-studio&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Happy coding!&lt;/p&gt;

&lt;h1&gt;
  
  
  react #nextjs #ai #google #webdev #typescript #hackathon #generativeai
&lt;/h1&gt;

</description>
      <category>webdev</category>
      <category>ai</category>
      <category>nextjs</category>
      <category>gemini</category>
    </item>
    <item>
      <title>I Built the Markdown Editor I Always Wanted with Next.js and AI</title>
      <dc:creator>Abubakr Alsheikh</dc:creator>
      <pubDate>Wed, 13 Aug 2025 13:49:39 +0000</pubDate>
      <link>https://dev.to/abubakr_alsheikh/i-built-the-markdown-editor-i-always-wanted-with-nextjs-and-ai-20bd</link>
      <guid>https://dev.to/abubakr_alsheikh/i-built-the-markdown-editor-i-always-wanted-with-nextjs-and-ai-20bd</guid>
      <description>&lt;p&gt;We've all been there. You have an idea, you start a side project with excitement, and then... it ends up in the GitHub graveyard, untouched for months.&lt;/p&gt;

&lt;p&gt;This time, I was determined to break the cycle. I decided to build a tool that solved a set of real, frustrating problems I faced almost daily as a developer.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Problem: A Lack of Control
&lt;/h3&gt;

&lt;p&gt;My workflow often involves generating text with LLMs, which usually comes out as an unstructured mess. But the core frustration wasn't just the formatting; it was the &lt;strong&gt;lack of control&lt;/strong&gt; in every step that followed.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; ** inflexible Previews:** When working with Arabic text, I couldn't find a live markdown preview that properly supported RTL.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;"One-Size-Fits-All" Styling:&lt;/strong&gt; I wanted to change the preview's font size for readability or switch to a different font for a presentation, but most tools are static.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Manual Formatting:&lt;/strong&gt; The tedious process of manually adding headers, lists, and code blocks was a constant time sink.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I realized I didn't just want a markdown editor; I wanted a fully customizable, intelligent workstation. So, I decided to build it myself.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Solution: Smart Markdown Pro
&lt;/h3&gt;

&lt;p&gt;I'm excited to share &lt;strong&gt;&lt;a href="https://smart-markdown-pro.vercel.app/" rel="noopener noreferrer"&gt;Smart Markdown Pro&lt;/a&gt;&lt;/strong&gt;, a local-first, AI-powered markdown editor built on a modern stack: Next.js (App Router), Tailwind CSS, and TypeScript.&lt;/p&gt;

&lt;p&gt;It's open source, and I built it to be the tool I wish I had from the start.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://smart-markdown-pro.vercel.app/" rel="noopener noreferrer"&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%2Fc03a6acq5woq7i7huvil.png" alt="Smart Markdown Pro Screenshot" width="512" height="512"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The Features That Mattered to Me
&lt;/h3&gt;

&lt;h4&gt;
  
  
  1. Total Preview Customization
&lt;/h4&gt;

&lt;p&gt;This was the core idea. I wanted a preview pane that adapted to my needs. In the settings, you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Toggle RTL/LTR direction instantly.&lt;/li&gt;
&lt;li&gt;  Adjust the base font size with a slider.&lt;/li&gt;
&lt;li&gt;  Use &lt;strong&gt;any&lt;/strong&gt; Google Font by pasting its link and CSS value. The app saves your previously used fonts for quick access.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  2. AI-Powered Formatting
&lt;/h4&gt;

&lt;p&gt;To solve the messy text problem, I integrated the OpenAI API. There's a single "Format with AI" button that takes your unstructured text and turns it into clean, well-formatted GFM. Because let's be honest, you can't ship an app in 2025 without a little AI, right? 😉&lt;/p&gt;

&lt;p&gt;The backend is a simple Next.js API route that securely handles the API key and prompt.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// /src/app/api/format-with-ai/route.ts&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;SYSTEM_PROMPT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`
You are an expert markdown formatter... 
Your response must ONLY be the formatted markdown content.
`&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;completion&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;completions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;gpt-4o&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;messages&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="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;system&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SYSTEM_PROMPT&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  3. Local-First File Management
&lt;/h4&gt;

&lt;p&gt;I hate creating accounts for simple tools. So, I used &lt;code&gt;Zustand&lt;/code&gt; with its &lt;code&gt;persist&lt;/code&gt; middleware to save everything directly to the user's &lt;code&gt;localStorage&lt;/code&gt;. It supports creating, opening, searching, and deleting multiple documents, and it's incredibly fast.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// /src/lib/store/useDocumentStore.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;create&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;zustand&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;persist&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;zustand/middleware&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;useDocumentStore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;create&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;DocumentState&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()(&lt;/span&gt;
  &lt;span class="nf"&gt;persist&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;set&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;get&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;// ... all the state and actions ...&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;smart-markdown-pro-storage-v2&lt;/span&gt;&lt;span class="dl"&gt;'&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="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  4. High-Fidelity PDF Exports
&lt;/h4&gt;

&lt;p&gt;This was the hardest technical challenge. Client-side libraries like &lt;code&gt;html2canvas&lt;/code&gt; kept failing because of modern CSS color formats (&lt;code&gt;lab()&lt;/code&gt;, &lt;code&gt;oklch()&lt;/code&gt;) used by Tailwind/Shadcn.&lt;/p&gt;

&lt;p&gt;The solution? A serverless function using &lt;strong&gt;Puppeteer&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The frontend sends the preview's HTML to an API route. The backend launches a headless Chrome instance, injects the HTML and a minimal set of CSS, and uses Chrome's own print-to-PDF engine to generate a perfect PDF.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// /src/app/api/export/pdf/route.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;puppeteer&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;puppeteer-core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;chromium&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@sparticuz/chromium&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// For serverless environments&lt;/span&gt;

&lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="nx"&gt;browser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;puppeteer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;chromium&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;executablePath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;chromium&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;executablePath&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;headless&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;new&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&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;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newPage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setContent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;finalHtml&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;waitUntil&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;networkidle0&lt;/span&gt;&lt;span class="dl"&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;pdfBuffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pdf&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;A4&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;printBackground&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Building What You Need is Rewarding
&lt;/h3&gt;

&lt;p&gt;This project was a fantastic learning experience and a powerful reminder that the best projects often come from solving your own problems. It forces you to think through every detail and build something you genuinely want to use.&lt;/p&gt;

&lt;p&gt;I'd love for you to check it out!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Live App:&lt;/strong&gt; &lt;a href="https://smart-markdown-pro.vercel.app/" rel="noopener noreferrer"&gt;https://smart-markdown-pro.vercel.app/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;GitHub Repo (Stars are super appreciated!):&lt;/strong&gt; &lt;a href="https://github.com/Abubakr-Alsheikh/smart-markdown-pro" rel="noopener noreferrer"&gt;https://github.com/Abubakr-Alsheikh/smart-markdown-pro&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What are some daily annoyances you've been tempted to turn into a side project? I'd love to hear about them in the comments&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>react</category>
      <category>nextjs</category>
      <category>frontend</category>
    </item>
  </channel>
</rss>
