<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>VueJS on Techformist</title>
    <link>https://techformist.com/categories/vuejs/</link>
    <description>Recent content in VueJS on Techformist</description>
    <image>
      <url>https://techformist.com/logo.svg</url>
      <link>https://techformist.com/logo.svg</link>
    </image>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-us</language>
    <lastBuildDate>Thu, 02 Oct 2025 06:30:00 +0000</lastBuildDate><atom:link href="https://techformist.com/categories/vuejs/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Notocan is a simple, local Google Keep alternative</title>
      <link>https://techformist.com/notocan-simple-local-notes/</link>
      <pubDate>Thu, 02 Oct 2025 06:30:00 +0000</pubDate>
      
      <guid>https://techformist.com/notocan-simple-local-notes/</guid>
      <description>&lt;p&gt;After spending years 😏 building applications with Nuxt.js (and occasionally dipping into Next.js), I decided to take a step back to pure Vue 3 and create a.. lo and behold - a Google Keep alternative.&lt;/p&gt;
&lt;p&gt;Just kidding - the app is far more dumb.&lt;/p&gt;
&lt;h2 id=&#34;notocan-a-dumb-google-keep-alternative&#34;&gt;Notocan: A Dumb Google Keep Alternative&lt;/h2&gt;
&lt;p&gt;Notocan is what I like to say &amp;ldquo;deliberately dumb&amp;rdquo;, local and fast. It&amp;rsquo;s not trying to be the next big productivity platform with AI features, cloud sync complexity, or fancy collaboration tools. It&amp;rsquo;s just notes, done right in a browser window.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>After spending years 😏 building applications with Nuxt.js (and occasionally dipping into Next.js), I decided to take a step back to pure Vue 3 and create a.. lo and behold - a Google Keep alternative.</p>
<p>Just kidding - the app is far more dumb.</p>
<h2 id="notocan-a-dumb-google-keep-alternative">Notocan: A Dumb Google Keep Alternative</h2>
<p>Notocan is what I like to say &ldquo;deliberately dumb&rdquo;, local and fast. It&rsquo;s not trying to be the next big productivity platform with AI features, cloud sync complexity, or fancy collaboration tools. It&rsquo;s just notes, done right in a browser window.</p>
<p><img loading="lazy" src="/2025/notocan-keep-alternative.png" type="" alt="notocan-local-notes-in-browser"  /></p>
<p>tldr;</p>
<ul>
<li>Has it been done a million times? Yes.</li>
<li>Has it been done by me in the past? Probably, yes.</li>
<li>Will it deter me from building something dumb again. Absolutely not.</li>
</ul>
<p>The core features are straightforward:</p>
<ul>
<li>Create notes with titles and rich text content</li>
<li>Color-code your notes for visual organization</li>
<li>Pin important notes to keep them at the top</li>
<li>Archive notes you don&rsquo;t need anymore</li>
<li>Search through all your notes instantly</li>
<li>Keyboard navigation</li>
</ul>
<p>Check it out here:
<a href="https://notocan.techformist.com">https://notocan.techformist.com</a></p>
<p>If you are gungho about todo&rsquo;s and notes - Notocan is open source and available on <a href="https://github.com/prashanth1k/notocan">GitHub</a>.</p>
<p>Feel free to use the app, or just poke around the code!</p>
<h2 id="back-to-the-experience">Back to the Experience</h2>
<p>The experience though has been nothing short of refreshing. No more complex configurations, no more wrestling with SSR setups and errors, just pure Vue 3 with the Composition API.</p>
<p>The dev experience is incredibly fast - hot reload is instantaneous, compilation is lightning-quick, and the build process feels almost instantaneous.</p>
<p>It&rsquo;s amazing how much mental overhead you shed when you don&rsquo;t have to think about server-side rendering for a client-side notes app. Also, there are no accounts, no subscriptions, no worrying about data privacy or service outages.</p>
<p>Your notes live in your browser&rsquo;s IndexedDB, which means:</p>
<ul>
<li>Instant loading (no network calls)</li>
<li>Works offline perfectly</li>
<li>Your data stays yours</li>
<li>Migration from localStorage is handled automatically</li>
</ul>
<p>The IndexedDB implementation includes pagination for performance, so even with hundreds of notes, the app stays snappy.</p>
<p>Also, I can&rsquo;t say enough good things about DaisyUI. Every time I use it, I&rsquo;m reminded why it was my go-to UI library. The component system is thoughtful and the default styles just work without being overwhelming.</p>
<p>Combined with Tailwind CSS, DaisyUI makes building polished interfaces feel effortless. The color system, spacing, and component variants work together seamlessly. It&rsquo;s the kind of library that gets out of your way and lets you focus on building features, not fighting CSS. And, not having all the code in my repo mocking my design skills. I would like that mocking to be deep insite the node_modules thank you very much.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Building Notocan reinforced some important lessons:</p>
<ol>
<li>Simple is indeed beautiful</li>
<li>I continue to be undeterred by no one using the applications I build</li>
</ol>
<p>If you need a simple notes app, check out Notocan - it might just become your new favorite way to jot down thoughts.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Introducing utils@techformist - A Developer&#39;s Swiss Army Knife</title>
      <link>https://techformist.com/free-developer-tools/</link>
      <pubDate>Tue, 25 Mar 2025 06:30:00 +0000</pubDate>
      
      <guid>https://techformist.com/free-developer-tools/</guid>
      <description>&lt;p&gt;Today, I&amp;rsquo;m excited to introduce &lt;strong&gt;utils.techformist.com&lt;/strong&gt; – a collection of client-side web utilities designed to make developers&amp;rsquo; lives easier. Built with speed, simplicity, and privacy in mind, this tool suite aims to be your go-to resource for common development tasks.&lt;/p&gt;
&lt;h2 id=&#34;why-another-utility-site&#34;&gt;Why Another Utility Site?&lt;/h2&gt;
&lt;p&gt;The web is full of utility sites, but many come with drawbacks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Intrusive ads and analytics&lt;/li&gt;
&lt;li&gt;Server-side processing of potentially sensitive data&lt;/li&gt;
&lt;li&gt;Slow loading times&lt;/li&gt;
&lt;li&gt;Inconsistent UI/UX&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href=&#34;https://utils.techformist.com&#34;&gt;utils.techformist.com&lt;/a&gt; addresses these issues by keeping everything client-side, ensuring your data never leaves your browser. The site loads quickly, works offline, and offers a clean, consistent interface for all tools.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>Today, I&rsquo;m excited to introduce <strong>utils.techformist.com</strong> – a collection of client-side web utilities designed to make developers&rsquo; lives easier. Built with speed, simplicity, and privacy in mind, this tool suite aims to be your go-to resource for common development tasks.</p>
<h2 id="why-another-utility-site">Why Another Utility Site?</h2>
<p>The web is full of utility sites, but many come with drawbacks:</p>
<ul>
<li>Intrusive ads and analytics</li>
<li>Server-side processing of potentially sensitive data</li>
<li>Slow loading times</li>
<li>Inconsistent UI/UX</li>
</ul>
<p><a href="https://utils.techformist.com">utils.techformist.com</a> addresses these issues by keeping everything client-side, ensuring your data never leaves your browser. The site loads quickly, works offline, and offers a clean, consistent interface for all tools.</p>
<p><img loading="lazy" src="/2025/great-developer-tools-free.png" type="" alt="great-developer-tools-free"  /></p>
<h2 id="key-features">Key Features</h2>
<h3 id="1-tools-that-you-want-to-use">1. Tools that you want to use!</h3>
<p>Our collection includes essential utilities that solve real problems :)</p>
<ul>
<li><strong>Calculator</strong>: A simple yet powerful calculator for quick arithmetic operations.</li>
<li><strong>QR Code Generator</strong>: Create QR codes for URLs, WiFi networks, contact details, and more with customizable colors.</li>
<li><strong>Password Generator</strong>: Generate secure, random passwords with options for length, character types, and complexity.</li>
<li><strong>Barcode Generator</strong>: Create various barcode formats with customizable settings for professional or personal use.</li>
<li><strong>Image Converter</strong>: Convert images between different formats without uploading them to any server.</li>
<li><strong>Favicon Generator</strong>: Quickly create favicons for your website from any image.</li>
<li><strong>Color Picker</strong>: Extract and analyze colors from images with support for multiple color formats.</li>
<li><strong>JSON Formatter</strong>: Format, validate, and beautify JSON data with easy-to-use controls and copy functionality.</li>
<li><strong>Base64 Converter</strong>: Encode and decode text or files to and from Base64 format right in your browser.</li>
</ul>
<p>.. and so on. Check out the site for the latest updates.</p>
<h3 id="2-fast-search-and-category-based-organization">2. Fast Search and Category-Based Organization</h3>
<p>A simple tool search is at the heart of the site!
At the same time, tools are organized into logical categories for easy discovery:</p>
<ul>
<li><strong>Code</strong>: Developer tools like JSON Formatter and Base64 Converter for everyday coding tasks</li>
<li><strong>Image</strong>: Tools for working with images, including QR Code Generator, Barcode Generator, Image Converter, and Favicon Generator</li>
<li><strong>Text</strong>: Text manipulation utilities like the Password Generator</li>
<li><strong>Design</strong>: Creative tools such as the Color Picker for design work</li>
<li><strong>File</strong>: File processing tools like the PDF Compressor</li>
<li><strong>Other</strong>: Miscellaneous utilities including the Calculator</li>
</ul>
<p>This categorization ensures you can quickly find the right tool for your current task.</p>
<h3 id="3-responsive-and-accessible-design">3. Responsive and Accessible Design</h3>
<p>All tools feature:</p>
<ul>
<li>Fully responsive layouts that work on devices of any size</li>
<li>Dark/light mode support</li>
</ul>
<h3 id="4-privacy-first-approach">4. Privacy-First Approach</h3>
<p>What sets util.techformist.com apart is its commitment to privacy:</p>
<ul>
<li>No tracking (except essentials)</li>
<li>No server-side processing</li>
<li>No data storage outside of the browser</li>
<li>No data transmission to any external computer outside of your own browser</li>
</ul>
<h2 id="technology-stack">Technology Stack</h2>
<p>Utils.techformist.com is built on modern web technologies:</p>
<ul>
<li><strong>Nuxt 3</strong>: For its excellent developer experience, automatic routing, and performance optimizations</li>
<li><strong>NuxtUI</strong>: Providing a consistent design system and accessible components</li>
<li><strong>Tailwind CSS</strong>: For rapid styling and responsive design</li>
</ul>
<p>The app is client-side only - no database or server processing.</p>
<h2 id="why-client-side-only">Why Client-Side Only?</h2>
<p>The decision to make util.techformist.com entirely client-side was deliberate:</p>
<ol>
<li><strong>Privacy</strong>: Your data never leaves your browser, eliminating privacy concerns.</li>
<li><strong>Performance</strong>: No server roundtrips means instant processing of your tasks.</li>
<li><strong>Availability</strong>: The site works offline once loaded.</li>
<li><strong>Scalability</strong>: Without server costs, the site can scale infinitely.</li>
<li><strong>Simplicity</strong>: The architecture remains clean without databases or APIs.</li>
</ol>
<h2 id="development-approach">Development Approach</h2>
<p>Building the site was straightforward with Nuxt&rsquo;s module system and NuxtUI&rsquo;s component library. The development workflow included:</p>
<ol>
<li>Setting up a Nuxt 3 project with the content and UI modules</li>
<li>Creating a consistent layout and navigation system</li>
<li>Implementing individual utility components</li>
<li>Ensuring responsive design with Tailwind</li>
<li>Optimizing for performance</li>
</ol>
<p>The entire project required no backend infrastructure, making deployment simple through static site hosting.
The site is hosted on Cloudflare Pages.</p>
<h2 id="looking-ahead">Looking Ahead</h2>
<p>Util.techformist.com is just getting started. Future plans include:</p>
<ul>
<li>Adding more utility tools based on user feedback!</li>
<li>Implementing more advanced features like tool chaining :)</li>
<li>Improving keyboard shortcuts and accessibility</li>
</ul>
<h2 id="conclusion">Conclusion</h2>
<p>Util.techformist.com represents my philosophy that web tools should be fast, private, and straightforward. By keeping everything in the browser, I&rsquo;ve created a resource that respects your data while providing genuinely useful functionality.</p>
<p>Give it a try at <a href="https://utils.techformist.com">utils.techformist.com</a> and let me know what you think.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Create a Client-Side PDF Generator with Nuxt 3</title>
      <link>https://techformist.com/create-pdf-json-nuxt/</link>
      <pubDate>Sat, 01 Mar 2025 06:30:00 +0000</pubDate>
      
      <guid>https://techformist.com/create-pdf-json-nuxt/</guid>
      <description>&lt;p&gt;We will walk through the steps to build a document analysis web application using Nuxt 3 and Azure Document Intelligence. This application allows users to -&lt;/p&gt;
&lt;p&gt;Features will include -&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;🔍 Create/pass a template in HTML and placeholders for values&lt;/li&gt;
&lt;li&gt;🕶️ Real-time document analysis status&lt;/li&gt;
&lt;li&gt;💾 Download merged content as PDF&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here&amp;rsquo;s a quick demo.
&lt;img loading=&#34;lazy&#34; src=&#34;https://techformist.com/2025/docgen-pdf-generator-json-nuxt.gif&#34; type=&#34;&#34; alt=&#34;docgen-pdf-generator-json-nuxt&#34;  /&gt;&lt;/p&gt;
&lt;p&gt;While the demo showcases invoice generation, the same program can be extended to any document type - filled application forms, documents, etc.&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>We will walk through the steps to build a document analysis web application using Nuxt 3 and Azure Document Intelligence. This application allows users to -</p>
<p>Features will include -</p>
<ul>
<li>🔍 Create/pass a template in HTML and placeholders for values</li>
<li>🕶️ Real-time document analysis status</li>
<li>💾 Download merged content as PDF</li>
</ul>
<p>Here&rsquo;s a quick demo.
<img loading="lazy" src="/2025/docgen-pdf-generator-json-nuxt.gif" type="" alt="docgen-pdf-generator-json-nuxt"  /></p>
<p>While the demo showcases invoice generation, the same program can be extended to any document type - filled application forms, documents, etc.</p>
<p>Technology stack -</p>
<ol>
<li>NuxtJS</li>
<li>Tailwind CSS</li>
<li>PDFJS</li>
<li>CKEditor</li>
</ol>
<h2 id="why-client-side-pdf-generation">Why Client-Side PDF Generation?</h2>
<p>Generating PDFs on the client-side offers several benefits:</p>
<ul>
<li><strong>Performance:</strong> Offloading PDF generation to the client&rsquo;s device reduces server load and leads to faster response times.</li>
<li><strong>User Experience:</strong> Users can preview changes in real-time, adjust their inputs, and generate PDFs without a trip to the server.</li>
<li><strong>Cost-Effective:</strong> Avoid additional server infrastructure by leveraging modern browsers and their capabilities.</li>
<li><strong>Flexibility:</strong> Easily integrate rich text editing and custom templates to create personalized documents.</li>
</ul>
<p>Who is this post for?</p>
<ul>
<li>You are trying to roll-out a PDF generator for invoices or filled applications</li>
<li>You are using an external service to generate PDFs</li>
<li>You are not satisfied with server-side PDF generators</li>
</ul>
<h2 id="setting-up-the-project">Setting Up the Project</h2>
<p>Before we start, make sure you have Node.js 18.x or later installed in your computer.</p>
<p>Create a blank Nuxt app.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmd" data-lang="cmd"><span class="line"><span class="cl">bunx nuxi@latest init docgen
</span></span></code></pre></div><p>A key component of our PDF generator is a rich text editor to create templates. We will use CKEditor to achieve just that. We will also use a JSON editor to enable editing JSON data.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmd" data-lang="cmd"><span class="line"><span class="cl">bun i ckeditor/ckeditor5-vue ckeditor5 vue3-json-editor
</span></span></code></pre></div><p>We will use pdfjs to preview and generate PDF, and DomPurify to make sure we are rendering things safer. Also, install types while at it.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmd" data-lang="cmd"><span class="line"><span class="cl">bun i dompurify html2canvas jspdf pdfjs-dist pdfmake
</span></span><span class="line"><span class="cl">bun i -d @types/pdfmake
</span></span></code></pre></div><p>Add styling and icons.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmd" data-lang="cmd"><span class="line"><span class="cl">bunx nuxi@latest module add tailwindcss lucide-vue-next
</span></span></code></pre></div><h2 id="develop-the-app">Develop the app</h2>
<h3 id="rich-text-editor">Rich Text Editor</h3>
<p>Rich text editor is one of the main components that enables users to edit templates.
You will not need the rich text editor if you don&rsquo;t plan to edit templates within the app (the template is plain HTML anyway.)</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-vue" data-lang="vue"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;richtext-editor&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">ckeditor</span>
</span></span><span class="line"><span class="cl">      <span class="nt">v-if</span><span class="o">=</span><span class="s">&#34;editor &amp;&amp; config &amp;&amp; modelValue&#34;</span><span class="p">
</span></span></span><span class="line"><span class="cl">      <span class="nt">:editor</span><span class="o">=</span><span class="s">&#34;editor&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="nt">v-model</span><span class="o">=</span><span class="s">&#34;modelValue&#34;</span><span class="p">
</span></span></span><span class="line"><span class="cl">      <span class="nt">:config</span><span class="o">=</span><span class="s">&#34;config&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">div</span>
</span></span><span class="line"><span class="cl">      <span class="nt">v-if</span><span class="o">=</span><span class="s">&#34;previewContent&#34;</span><span class="p">
</span></span></span><span class="line"><span class="cl">      <span class="na">class</span><span class="o">=</span><span class="s">&#34;preview-overlay prose&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="nt">v-html</span><span class="o">=</span><span class="s">&#34;previewContent&#34;</span><span class="p">
</span></span></span><span class="line"><span class="cl">    <span class="p">&gt;&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">setup</span> <span class="na">lang</span><span class="o">=</span><span class="s">&#34;ts&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">onMounted</span><span class="p">,</span> <span class="nx">ref</span> <span class="p">}</span> <span class="nx">from</span> <span class="s2">&#34;vue&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">ClassicEditor</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">Autoformat</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">AutoImage</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">Autosave</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">Base64UploadAdapter</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">Bold</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">Essentials</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// other items - see GitHub link
</span></span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="nx">from</span> <span class="s2">&#34;ckeditor5&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="s2">&#34;ckeditor5/ckeditor5.css&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">Ckeditor</span> <span class="p">}</span> <span class="nx">from</span> <span class="s2">&#34;@ckeditor/ckeditor5-vue&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="s2">&#34;ckeditor5/ckeditor5.css&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">isLayoutReady</span> <span class="o">=</span> <span class="nx">ref</span><span class="p">(</span><span class="kc">false</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">editor</span> <span class="o">=</span> <span class="nx">ClassicEditor</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">LICENSE_KEY</span> <span class="o">=</span> <span class="s2">&#34;GPL&#34;</span><span class="p">;</span> <span class="c1">// or &lt;YOUR_LICENSE_KEY&gt;.
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">config</span> <span class="o">=</span> <span class="nx">computed</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">isLayoutReady</span><span class="p">.</span><span class="nx">value</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="kc">null</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">toolbar</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">items</span><span class="o">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;sourceEditing&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;|&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;heading&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;|&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;fontSize&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;fontFamily&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// other toolbar items. See GitHub link for all buttons
</span></span></span><span class="line"><span class="cl">      <span class="p">],</span>
</span></span><span class="line"><span class="cl">      <span class="nx">shouldNotGroupWhenFull</span><span class="o">:</span> <span class="kc">false</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nx">plugins</span><span class="o">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">      <span class="c1">// relevant plugins - see GitHub link
</span></span></span><span class="line"><span class="cl">    <span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="nx">fontFamily</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">supportAllValues</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nx">fontSize</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">options</span><span class="o">:</span> <span class="p">[</span><span class="mi">10</span><span class="p">,</span> <span class="mi">12</span><span class="p">,</span> <span class="mi">14</span><span class="p">,</span> <span class="s2">&#34;default&#34;</span><span class="p">,</span> <span class="mi">18</span><span class="p">,</span> <span class="mi">20</span><span class="p">,</span> <span class="mi">22</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">      <span class="nx">supportAllValues</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nx">heading</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">options</span><span class="o">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="nx">model</span><span class="o">:</span> <span class="s2">&#34;paragraph&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">title</span><span class="o">:</span> <span class="s2">&#34;Paragraph&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="kr">class</span><span class="o">:</span> <span class="s2">&#34;ck-heading_paragraph&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="nx">model</span><span class="o">:</span> <span class="s2">&#34;heading1&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">view</span><span class="o">:</span> <span class="s2">&#34;h1&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">title</span><span class="o">:</span> <span class="s2">&#34;Heading 1&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="kr">class</span><span class="o">:</span> <span class="s2">&#34;ck-heading_heading1&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="nx">model</span><span class="o">:</span> <span class="s2">&#34;heading2&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">view</span><span class="o">:</span> <span class="s2">&#34;h2&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">title</span><span class="o">:</span> <span class="s2">&#34;Heading 2&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="kr">class</span><span class="o">:</span> <span class="s2">&#34;ck-heading_heading2&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="nx">model</span><span class="o">:</span> <span class="s2">&#34;heading3&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">view</span><span class="o">:</span> <span class="s2">&#34;h3&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">title</span><span class="o">:</span> <span class="s2">&#34;Heading 3&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="kr">class</span><span class="o">:</span> <span class="s2">&#34;ck-heading_heading3&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="nx">model</span><span class="o">:</span> <span class="s2">&#34;heading4&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">view</span><span class="o">:</span> <span class="s2">&#34;h4&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">title</span><span class="o">:</span> <span class="s2">&#34;Heading 4&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="kr">class</span><span class="o">:</span> <span class="s2">&#34;ck-heading_heading4&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="nx">model</span><span class="o">:</span> <span class="s2">&#34;heading5&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">view</span><span class="o">:</span> <span class="s2">&#34;h5&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">title</span><span class="o">:</span> <span class="s2">&#34;Heading 5&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="kr">class</span><span class="o">:</span> <span class="s2">&#34;ck-heading_heading5&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="nx">model</span><span class="o">:</span> <span class="s2">&#34;heading6&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">view</span><span class="o">:</span> <span class="s2">&#34;h6&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">title</span><span class="o">:</span> <span class="s2">&#34;Heading 6&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="kr">class</span><span class="o">:</span> <span class="s2">&#34;ck-heading_heading6&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nx">image</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">toolbar</span><span class="o">:</span> <span class="p">[</span><span class="s2">&#34;imageTextAlternative&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="nx">initialData</span><span class="o">:</span> <span class="s2">&#34;..&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">licenseKey</span><span class="o">:</span> <span class="nx">LICENSE_KEY</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">link</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">addTargetToExternalLinks</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nx">defaultProtocol</span><span class="o">:</span> <span class="s2">&#34;https://&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nx">decorators</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">toggleDownloadable</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="nx">mode</span><span class="o">:</span> <span class="s2">&#34;manual&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">label</span><span class="o">:</span> <span class="s2">&#34;Downloadable&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">attributes</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nx">download</span><span class="o">:</span> <span class="s2">&#34;file&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="p">},</span>
</span></span><span class="line"><span class="cl">        <span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">placeholder</span><span class="o">:</span> <span class="s2">&#34;Type or paste your content here!&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">table</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">contentToolbar</span><span class="o">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;tableColumn&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;tableRow&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;mergeTableCells&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;tableProperties&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;tableCellProperties&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">modelValue</span> <span class="o">=</span> <span class="nx">defineModel</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">props</span> <span class="o">=</span> <span class="nx">defineProps</span><span class="o">&lt;</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">previewContent</span><span class="o">?:</span> <span class="nx">string</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span><span class="o">&gt;</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">onMounted</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">isLayoutReady</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">style</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="cm">/* include styles */</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">style</span><span class="p">&gt;</span>
</span></span></code></pre></div><h3 id="json-editor">JSON Editor</h3>
<p>Create the JSON editor that is used to edit JSON data.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-vue" data-lang="vue"><span class="line"><span class="cl"><span class="c">&lt;!--</span> <span class="nx">components</span><span class="o">/</span><span class="nx">DataEditor</span><span class="p">.</span><span class="nx">vue</span> <span class="o">--&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">ClientOnly</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">div</span>
</span></span><span class="line"><span class="cl">      <span class="na">class</span><span class="o">=</span><span class="s">&#34;h-[300px] border border-white/20 rounded-lg overflow-hidden bg-white/5&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">textarea</span>
</span></span><span class="line"><span class="cl">        <span class="nt">v-model</span><span class="o">=</span><span class="s">&#34;jsonString&#34;</span><span class="p">
</span></span></span><span class="line"><span class="cl">        <span class="na">class</span><span class="o">=</span><span class="s">&#34;w-full h-full p-4 font-mono text-sm bg-transparent text-black dark:text-white/90 focus:outline-none&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="nt">@input</span><span class="s">=&#34;handleInput&#34;</span><span class="p">
</span></span></span><span class="line"><span class="cl">        <span class="na">spellcheck</span><span class="o">=</span><span class="s">&#34;false&#34;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&gt;&lt;/</span><span class="nt">textarea</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">div</span> <span class="nt">v-if</span><span class="o">=</span><span class="s">&#34;error&#34; class=&#34;mt-2 text-red-400 text-sm&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">{{</span> <span class="na">error</span> <span class="p">}}</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">ClientOnly</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">setup</span> <span class="na">lang</span><span class="o">=</span><span class="s">&#34;ts&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">ref</span><span class="p">,</span> <span class="nx">watch</span> <span class="p">}</span> <span class="nx">from</span> <span class="s2">&#34;vue&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">useDocumentStore</span> <span class="p">}</span> <span class="nx">from</span> <span class="s2">&#34;~/stores/document&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">props</span> <span class="o">=</span> <span class="nx">defineProps</span><span class="o">&lt;</span><span class="p">{</span> <span class="nx">modelValue</span><span class="o">:</span> <span class="nx">Record</span><span class="p">&lt;</span><span class="nt">string</span><span class="err">,</span> <span class="na">any</span><span class="p">&gt;</span> <span class="p">}</span><span class="o">&gt;</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">emit</span> <span class="o">=</span> <span class="nx">defineEmits</span><span class="o">&lt;</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="s2">&#34;update:modelValue&#34;</span><span class="o">:</span> <span class="p">[</span><span class="nx">value</span><span class="o">:</span> <span class="nx">Record</span><span class="p">&lt;</span><span class="nt">string</span><span class="err">,</span> <span class="na">any</span><span class="p">&gt;];</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span><span class="o">&gt;</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">documentStore</span> <span class="o">=</span> <span class="nx">useDocumentStore</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">error</span> <span class="o">=</span> <span class="nx">ref</span><span class="p">(</span><span class="s2">&#34;&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">jsonString</span> <span class="o">=</span> <span class="nx">ref</span><span class="p">(</span><span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="nx">props</span><span class="p">.</span><span class="nx">modelValue</span><span class="p">,</span> <span class="kc">null</span><span class="p">,</span> <span class="mi">2</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">watch</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">  <span class="p">()</span> <span class="p">=&gt;</span> <span class="nx">props</span><span class="p">.</span><span class="nx">modelValue</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="p">(</span><span class="nx">newVal</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">newString</span> <span class="o">=</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="nx">newVal</span><span class="p">,</span> <span class="kc">null</span><span class="p">,</span> <span class="mi">2</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nx">jsonString</span><span class="p">.</span><span class="nx">value</span> <span class="o">!==</span> <span class="nx">newString</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">jsonString</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="nx">newString</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">handleInput</span> <span class="o">=</span> <span class="p">(</span><span class="nx">event</span><span class="o">:</span> <span class="nx">Event</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">target</span> <span class="o">=</span> <span class="nx">event</span><span class="p">.</span><span class="nx">target</span> <span class="nx">as</span> <span class="nx">HTMLTextAreaElement</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">try</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">parsed</span> <span class="o">=</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">parse</span><span class="p">(</span><span class="nx">target</span><span class="p">.</span><span class="nx">value</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nx">error</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="s2">&#34;&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nx">documentStore</span><span class="p">.</span><span class="nx">isDirty</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nx">emit</span><span class="p">(</span><span class="s2">&#34;update:modelValue&#34;</span><span class="p">,</span> <span class="nx">parsed</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">e</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">error</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="s2">&#34;Invalid JSON format&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">style</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="cm">/* include styles */</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">style</span><span class="p">&gt;</span>
</span></span></code></pre></div><h3 id="create-pdf-viewer">Create PDF Viewer</h3>
<p>PDF viewer shows the PDF after merging data in template.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-vue" data-lang="vue"><span class="line"><span class="cl"><span class="c">&lt;!--</span> <span class="nx">components</span><span class="o">/</span><span class="nx">PdfViewer</span><span class="p">.</span><span class="nx">vue</span> <span class="o">--&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;pdf-viewer&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">div</span> <span class="nt">v-if</span><span class="o">=</span><span class="s">&#34;loading&#34; class=&#34;loading-indicator&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;spinner&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">div</span><span class="p">&gt;</span><span class="nx">Generating</span> <span class="nx">PDF</span> <span class="nx">preview</span><span class="p">...&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">div</span> <span class="nt">v-else-if</span><span class="o">=</span><span class="s">&#34;error&#34; class=&#34;error-message&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">{{</span> <span class="na">error</span> <span class="p">}}</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">div</span> <span class="nt">v-else</span><span class="p"> </span><span class="na">ref</span><span class="o">=</span><span class="s">&#34;canvasContainer&#34;</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;canvas-container&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">setup</span> <span class="na">lang</span><span class="o">=</span><span class="s">&#34;ts&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">ref</span><span class="p">,</span> <span class="nx">onMounted</span><span class="p">,</span> <span class="nx">watch</span><span class="p">,</span> <span class="nx">onBeforeUnmount</span> <span class="p">}</span> <span class="nx">from</span> <span class="s2">&#34;vue&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">props</span> <span class="o">=</span> <span class="nx">defineProps</span><span class="o">&lt;</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">content</span><span class="o">:</span> <span class="nx">string</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span><span class="o">&gt;</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">canvasContainer</span> <span class="o">=</span> <span class="nx">ref</span><span class="p">&lt;</span><span class="nt">HTMLElement</span> <span class="err">|</span> <span class="na">null</span><span class="p">&gt;(</span><span class="kc">null</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">loading</span> <span class="o">=</span> <span class="nx">ref</span><span class="p">(</span><span class="kc">true</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">error</span> <span class="o">=</span> <span class="nx">ref</span><span class="p">&lt;</span><span class="nt">string</span> <span class="err">|</span> <span class="na">null</span><span class="p">&gt;(</span><span class="kc">null</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">pdfDoc</span><span class="o">:</span> <span class="nx">any</span> <span class="o">=</span> <span class="kc">null</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">currentPage</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">pageRendering</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">pageNumPending</span><span class="o">:</span> <span class="nx">number</span> <span class="o">|</span> <span class="kc">null</span> <span class="o">=</span> <span class="kc">null</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nx">pdfjsLib</span><span class="o">:</span> <span class="nx">any</span> <span class="o">=</span> <span class="kc">null</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Clean up any resources when component is unmounted
</span></span></span><span class="line"><span class="cl"><span class="nx">onBeforeUnmount</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="nx">pdfDoc</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">pdfDoc</span> <span class="o">=</span> <span class="kc">null</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Initialize PDF.js
</span></span></span><span class="line"><span class="cl"><span class="nx">onMounted</span><span class="p">(</span><span class="nx">async</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="nx">process</span><span class="p">.</span><span class="nx">client</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">try</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="c1">// Import PDF.js dynamically
</span></span></span><span class="line"><span class="cl">      <span class="kr">const</span> <span class="nx">pdfjsModule</span> <span class="o">=</span> <span class="nx">await</span> <span class="kr">import</span><span class="p">(</span><span class="s2">&#34;pdfjs-dist&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="nx">pdfjsLib</span> <span class="o">=</span> <span class="nx">pdfjsModule</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="c1">// Set worker source - using CDN for the worker
</span></span></span><span class="line"><span class="cl">      <span class="nx">pdfjsLib</span><span class="p">.</span><span class="nx">GlobalWorkerOptions</span><span class="p">.</span><span class="nx">workerSrc</span> <span class="o">=</span>
</span></span><span class="line"><span class="cl">        <span class="s2">&#34;https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.worker.min.js&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="c1">// Generate and render PDF
</span></span></span><span class="line"><span class="cl">      <span class="nx">await</span> <span class="nx">generatePdf</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="s2">&#34;Error initializing PDF.js:&#34;</span><span class="p">,</span> <span class="nx">err</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="nx">error</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="s2">&#34;Failed to initialize PDF viewer. Please try again.&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="nx">loading</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Watch for content changes
</span></span></span><span class="line"><span class="cl"><span class="nx">watch</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">  <span class="p">()</span> <span class="p">=&gt;</span> <span class="nx">props</span><span class="p">.</span><span class="nx">content</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">async</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nx">pdfjsLib</span> <span class="o">&amp;&amp;</span> <span class="nx">process</span><span class="p">.</span><span class="nx">client</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">loading</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="nx">error</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="kc">null</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="nx">await</span> <span class="nx">generatePdf</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Generate PDF from content
</span></span></span><span class="line"><span class="cl"><span class="nx">async</span> <span class="kd">function</span> <span class="nx">generatePdf</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">pdfjsLib</span> <span class="o">||</span> <span class="o">!</span><span class="nx">process</span><span class="p">.</span><span class="nx">client</span><span class="p">)</span> <span class="k">return</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">try</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// Import pdfMake dynamically
</span></span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">pdfMakeModule</span> <span class="o">=</span> <span class="nx">await</span> <span class="kr">import</span><span class="p">(</span><span class="s2">&#34;pdfmake/build/pdfmake&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">pdfFontsModule</span> <span class="o">=</span> <span class="nx">await</span> <span class="kr">import</span><span class="p">(</span><span class="s2">&#34;pdfmake/build/vfs_fonts&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Get the default export or the module itself
</span></span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">pdfMake</span> <span class="o">=</span> <span class="nx">pdfMakeModule</span><span class="p">.</span><span class="k">default</span> <span class="o">||</span> <span class="nx">pdfMakeModule</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Manually set up the vfs with a basic font
</span></span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">pdfMake</span><span class="p">.</span><span class="nx">vfs</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="c1">// @ts-ignore - We know this property might exist
</span></span></span><span class="line"><span class="cl">      <span class="nx">pdfMake</span><span class="p">.</span><span class="nx">vfs</span> <span class="o">=</span> <span class="nx">pdfFontsModule</span><span class="p">.</span><span class="nx">pdfMake</span><span class="o">?</span><span class="p">.</span><span class="nx">vfs</span> <span class="o">||</span> <span class="p">{};</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Create a temporary element to parse the HTML
</span></span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">tempDiv</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="s2">&#34;div&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nx">tempDiv</span><span class="p">.</span><span class="nx">innerHTML</span> <span class="o">=</span> <span class="nx">props</span><span class="p">.</span><span class="nx">content</span> <span class="o">||</span> <span class="s2">&#34;&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Extract text content and structure
</span></span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">pdfContent</span> <span class="o">=</span> <span class="nx">extractPdfContent</span><span class="p">(</span><span class="nx">tempDiv</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Define the document with basic fonts
</span></span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">docDefinition</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">content</span><span class="o">:</span> <span class="nx">pdfContent</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nx">defaultStyle</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">font</span><span class="o">:</span> <span class="s2">&#34;Roboto&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">fontSize</span><span class="o">:</span> <span class="mi">12</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">        <span class="nx">lineHeight</span><span class="o">:</span> <span class="mf">1.5</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="p">},</span>
</span></span><span class="line"><span class="cl">      <span class="nx">pageSize</span><span class="o">:</span> <span class="s2">&#34;A4&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nx">pageMargins</span><span class="o">:</span> <span class="p">[</span><span class="mi">40</span><span class="p">,</span> <span class="mi">60</span><span class="p">,</span> <span class="mi">40</span><span class="p">,</span> <span class="mi">60</span><span class="p">]</span> <span class="nx">as</span> <span class="p">[</span><span class="nx">number</span><span class="p">,</span> <span class="nx">number</span><span class="p">,</span> <span class="nx">number</span><span class="p">,</span> <span class="nx">number</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Generate PDF as Blob
</span></span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">pdfDocGenerator</span> <span class="o">=</span> <span class="nx">pdfMake</span><span class="p">.</span><span class="nx">createPdf</span><span class="p">(</span><span class="nx">docDefinition</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">pdfDocGenerator</span><span class="p">.</span><span class="nx">getBlob</span><span class="p">(</span><span class="nx">async</span> <span class="p">(</span><span class="nx">blob</span><span class="o">:</span> <span class="nx">Blob</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">try</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// Convert blob to array buffer
</span></span></span><span class="line"><span class="cl">        <span class="kr">const</span> <span class="nx">arrayBuffer</span> <span class="o">=</span> <span class="nx">await</span> <span class="nx">blob</span><span class="p">.</span><span class="nx">arrayBuffer</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1">// Load PDF document
</span></span></span><span class="line"><span class="cl">        <span class="kr">const</span> <span class="nx">loadingTask</span> <span class="o">=</span> <span class="nx">pdfjsLib</span><span class="p">.</span><span class="nx">getDocument</span><span class="p">({</span> <span class="nx">data</span><span class="o">:</span> <span class="nx">arrayBuffer</span> <span class="p">});</span>
</span></span><span class="line"><span class="cl">        <span class="nx">pdfDoc</span> <span class="o">=</span> <span class="nx">await</span> <span class="nx">loadingTask</span><span class="p">.</span><span class="nx">promise</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="c1">// Initial/first page rendering
</span></span></span><span class="line"><span class="cl">        <span class="nx">await</span> <span class="nx">renderPage</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nx">loading</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="s2">&#34;Error rendering PDF:&#34;</span><span class="p">,</span> <span class="nx">err</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="nx">error</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="s2">&#34;Failed to render PDF preview.&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="nx">loading</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">});</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="s2">&#34;Error generating PDF:&#34;</span><span class="p">,</span> <span class="nx">err</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nx">error</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="s2">&#34;Failed to generate PDF preview.&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nx">loading</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Render the specified page
</span></span></span><span class="line"><span class="cl"><span class="nx">async</span> <span class="kd">function</span> <span class="nx">renderPage</span><span class="p">(</span><span class="nx">num</span><span class="o">:</span> <span class="nx">number</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">pdfDoc</span><span class="p">)</span> <span class="k">return</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nx">pageRendering</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">try</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// Get page
</span></span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">page</span> <span class="o">=</span> <span class="nx">await</span> <span class="nx">pdfDoc</span><span class="p">.</span><span class="nx">getPage</span><span class="p">(</span><span class="nx">num</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Clear previous content
</span></span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nx">canvasContainer</span><span class="p">.</span><span class="nx">value</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">canvasContainer</span><span class="p">.</span><span class="nx">value</span><span class="p">.</span><span class="nx">innerHTML</span> <span class="o">=</span> <span class="s2">&#34;&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Calculate scale to fit the container width
</span></span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">containerWidth</span> <span class="o">=</span> <span class="nx">canvasContainer</span><span class="p">.</span><span class="nx">value</span><span class="o">?</span><span class="p">.</span><span class="nx">clientWidth</span> <span class="o">||</span> <span class="mi">800</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">viewport</span> <span class="o">=</span> <span class="nx">page</span><span class="p">.</span><span class="nx">getViewport</span><span class="p">({</span> <span class="nx">scale</span><span class="o">:</span> <span class="mi">1</span> <span class="p">});</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">scale</span> <span class="o">=</span> <span class="nx">containerWidth</span> <span class="o">/</span> <span class="nx">viewport</span><span class="p">.</span><span class="nx">width</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">scaledViewport</span> <span class="o">=</span> <span class="nx">page</span><span class="p">.</span><span class="nx">getViewport</span><span class="p">({</span> <span class="nx">scale</span> <span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Create canvas for each page
</span></span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">canvas</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="s2">&#34;canvas&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">ctx</span> <span class="o">=</span> <span class="nx">canvas</span><span class="p">.</span><span class="nx">getContext</span><span class="p">(</span><span class="s2">&#34;2d&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nx">canvas</span><span class="p">.</span><span class="nx">height</span> <span class="o">=</span> <span class="nx">scaledViewport</span><span class="p">.</span><span class="nx">height</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nx">canvas</span><span class="p">.</span><span class="nx">width</span> <span class="o">=</span> <span class="nx">scaledViewport</span><span class="p">.</span><span class="nx">width</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nx">canvas</span><span class="p">.</span><span class="nx">className</span> <span class="o">=</span> <span class="s2">&#34;pdf-page&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Add canvas to container
</span></span></span><span class="line"><span class="cl">    <span class="nx">canvasContainer</span><span class="p">.</span><span class="nx">value</span><span class="o">?</span><span class="p">.</span><span class="nx">appendChild</span><span class="p">(</span><span class="nx">canvas</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// Render PDF page into canvas context
</span></span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">renderContext</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">canvasContext</span><span class="o">:</span> <span class="nx">ctx</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nx">viewport</span><span class="o">:</span> <span class="nx">scaledViewport</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">await</span> <span class="nx">page</span><span class="p">.</span><span class="nx">render</span><span class="p">(</span><span class="nx">renderContext</span><span class="p">).</span><span class="nx">promise</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nx">pageRendering</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// If there&#39;s a pending page, render it
</span></span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nx">pageNumPending</span> <span class="o">!==</span> <span class="kc">null</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">renderPage</span><span class="p">(</span><span class="nx">pageNumPending</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="nx">pageNumPending</span> <span class="o">=</span> <span class="kc">null</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// If there are more pages, render them too
</span></span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nx">num</span> <span class="o">&lt;</span> <span class="nx">pdfDoc</span><span class="p">.</span><span class="nx">numPages</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">renderPage</span><span class="p">(</span><span class="nx">num</span> <span class="o">+</span> <span class="mi">1</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">err</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="s2">&#34;Error rendering page:&#34;</span><span class="p">,</span> <span class="nx">err</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nx">pageRendering</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nx">error</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="s2">&#34;Failed to render PDF page.&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Extract content from HTML for PDF generation
</span></span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">extractPdfContent</span><span class="p">(</span><span class="nx">element</span><span class="o">:</span> <span class="nx">HTMLElement</span><span class="p">)</span><span class="o">:</span> <span class="nx">any</span><span class="p">[]</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">result</span><span class="o">:</span> <span class="nx">any</span><span class="p">[]</span> <span class="o">=</span> <span class="p">[];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// Process child nodes
</span></span></span><span class="line"><span class="cl">  <span class="nb">Array</span><span class="p">.</span><span class="nx">from</span><span class="p">(</span><span class="nx">element</span><span class="p">.</span><span class="nx">childNodes</span><span class="p">).</span><span class="nx">forEach</span><span class="p">((</span><span class="nx">node</span><span class="o">:</span> <span class="nx">Node</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nx">node</span><span class="p">.</span><span class="nx">nodeType</span> <span class="o">===</span> <span class="nx">Node</span><span class="p">.</span><span class="nx">TEXT_NODE</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="kr">const</span> <span class="nx">text</span> <span class="o">=</span> <span class="nx">node</span><span class="p">.</span><span class="nx">textContent</span><span class="o">?</span><span class="p">.</span><span class="nx">trim</span><span class="p">()</span> <span class="o">||</span> <span class="s2">&#34;&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="p">(</span><span class="nx">text</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">result</span><span class="p">.</span><span class="nx">push</span><span class="p">({</span> <span class="nx">text</span> <span class="p">});</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">node</span><span class="p">.</span><span class="nx">nodeType</span> <span class="o">===</span> <span class="nx">Node</span><span class="p">.</span><span class="nx">ELEMENT_NODE</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="kr">const</span> <span class="nx">elementNode</span> <span class="o">=</span> <span class="nx">node</span> <span class="nx">as</span> <span class="nx">HTMLElement</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">      <span class="kr">const</span> <span class="nx">nodeName</span> <span class="o">=</span> <span class="nx">elementNode</span><span class="p">.</span><span class="nx">nodeName</span><span class="p">.</span><span class="nx">toLowerCase</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="c1">// Handle different element types
</span></span></span><span class="line"><span class="cl">      <span class="k">if</span> <span class="p">(</span><span class="nx">nodeName</span> <span class="o">===</span> <span class="s2">&#34;p&#34;</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">result</span><span class="p">.</span><span class="nx">push</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">          <span class="nx">text</span><span class="o">:</span> <span class="nx">elementNode</span><span class="p">.</span><span class="nx">textContent</span><span class="o">?</span><span class="p">.</span><span class="nx">trim</span><span class="p">()</span> <span class="o">||</span> <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">margin</span><span class="o">:</span> <span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">10</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">        <span class="p">});</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">nodeName</span> <span class="o">===</span> <span class="s2">&#34;h1&#34;</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">result</span><span class="p">.</span><span class="nx">push</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">          <span class="nx">text</span><span class="o">:</span> <span class="nx">elementNode</span><span class="p">.</span><span class="nx">textContent</span><span class="o">?</span><span class="p">.</span><span class="nx">trim</span><span class="p">()</span> <span class="o">||</span> <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">fontSize</span><span class="o">:</span> <span class="mi">24</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">bold</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">margin</span><span class="o">:</span> <span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="mi">10</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">10</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">        <span class="p">});</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">nodeName</span> <span class="o">===</span> <span class="s2">&#34;h2&#34;</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">result</span><span class="p">.</span><span class="nx">push</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">          <span class="nx">text</span><span class="o">:</span> <span class="nx">elementNode</span><span class="p">.</span><span class="nx">textContent</span><span class="o">?</span><span class="p">.</span><span class="nx">trim</span><span class="p">()</span> <span class="o">||</span> <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">fontSize</span><span class="o">:</span> <span class="mi">20</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">bold</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">margin</span><span class="o">:</span> <span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="mi">10</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">5</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">        <span class="p">});</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">nodeName</span> <span class="o">===</span> <span class="s2">&#34;h3&#34;</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nx">result</span><span class="p">.</span><span class="nx">push</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">          <span class="nx">text</span><span class="o">:</span> <span class="nx">elementNode</span><span class="p">.</span><span class="nx">textContent</span><span class="o">?</span><span class="p">.</span><span class="nx">trim</span><span class="p">()</span> <span class="o">||</span> <span class="s2">&#34;&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">fontSize</span><span class="o">:</span> <span class="mi">16</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">bold</span><span class="o">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">margin</span><span class="o">:</span> <span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="mi">10</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">5</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">        <span class="p">});</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">nodeName</span> <span class="o">===</span> <span class="s2">&#34;ul&#34;</span> <span class="o">||</span> <span class="nx">nodeName</span> <span class="o">===</span> <span class="s2">&#34;ol&#34;</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="kr">const</span> <span class="nx">items</span> <span class="o">=</span> <span class="nb">Array</span><span class="p">.</span><span class="nx">from</span><span class="p">(</span><span class="nx">elementNode</span><span class="p">.</span><span class="nx">children</span><span class="p">).</span><span class="nx">map</span><span class="p">((</span><span class="nx">li</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="k">return</span> <span class="p">{</span> <span class="nx">text</span><span class="o">:</span> <span class="nx">li</span><span class="p">.</span><span class="nx">textContent</span><span class="o">?</span><span class="p">.</span><span class="nx">trim</span><span class="p">()</span> <span class="o">||</span> <span class="s2">&#34;&#34;</span><span class="p">,</span> <span class="nx">margin</span><span class="o">:</span> <span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">2</span><span class="p">]</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl">        <span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="nx">result</span><span class="p">.</span><span class="nx">push</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">          <span class="nx">ul</span><span class="o">:</span> <span class="nx">nodeName</span> <span class="o">===</span> <span class="s2">&#34;ul&#34;</span> <span class="o">?</span> <span class="nx">items</span> <span class="o">:</span> <span class="kc">undefined</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">ol</span><span class="o">:</span> <span class="nx">nodeName</span> <span class="o">===</span> <span class="s2">&#34;ol&#34;</span> <span class="o">?</span> <span class="nx">items</span> <span class="o">:</span> <span class="kc">undefined</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">          <span class="nx">margin</span><span class="o">:</span> <span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">10</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">        <span class="p">});</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">nodeName</span> <span class="o">===</span> <span class="s2">&#34;table&#34;</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// Extract table data
</span></span></span><span class="line"><span class="cl">        <span class="kr">const</span> <span class="nx">tableData</span><span class="o">:</span> <span class="nx">string</span><span class="p">[][]</span> <span class="o">=</span> <span class="p">[];</span>
</span></span><span class="line"><span class="cl">        <span class="nb">Array</span><span class="p">.</span><span class="nx">from</span><span class="p">(</span><span class="nx">elementNode</span><span class="p">.</span><span class="nx">querySelectorAll</span><span class="p">(</span><span class="s2">&#34;tr&#34;</span><span class="p">)).</span><span class="nx">forEach</span><span class="p">((</span><span class="nx">row</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="kr">const</span> <span class="nx">rowData</span><span class="o">:</span> <span class="nx">string</span><span class="p">[]</span> <span class="o">=</span> <span class="p">[];</span>
</span></span><span class="line"><span class="cl">          <span class="nb">Array</span><span class="p">.</span><span class="nx">from</span><span class="p">(</span><span class="nx">row</span><span class="p">.</span><span class="nx">querySelectorAll</span><span class="p">(</span><span class="s2">&#34;td, th&#34;</span><span class="p">)).</span><span class="nx">forEach</span><span class="p">((</span><span class="nx">cell</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nx">rowData</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">cell</span><span class="p">.</span><span class="nx">textContent</span><span class="o">?</span><span class="p">.</span><span class="nx">trim</span><span class="p">()</span> <span class="o">||</span> <span class="s2">&#34;&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">          <span class="p">});</span>
</span></span><span class="line"><span class="cl">          <span class="k">if</span> <span class="p">(</span><span class="nx">rowData</span><span class="p">.</span><span class="nx">length</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="nx">tableData</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">rowData</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">          <span class="p">}</span>
</span></span><span class="line"><span class="cl">        <span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="nx">tableData</span><span class="p">.</span><span class="nx">length</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="nx">result</span><span class="p">.</span><span class="nx">push</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">            <span class="nx">table</span><span class="o">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">              <span class="nx">body</span><span class="o">:</span> <span class="nx">tableData</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">              <span class="nx">widths</span><span class="o">:</span> <span class="nb">Array</span><span class="p">(</span><span class="nx">tableData</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">length</span><span class="p">).</span><span class="nx">fill</span><span class="p">(</span><span class="s2">&#34;*&#34;</span><span class="p">),</span>
</span></span><span class="line"><span class="cl">            <span class="p">},</span>
</span></span><span class="line"><span class="cl">            <span class="nx">margin</span><span class="o">:</span> <span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">10</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">          <span class="p">});</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// Recursively process other elements
</span></span></span><span class="line"><span class="cl">        <span class="kr">const</span> <span class="nx">childContent</span> <span class="o">=</span> <span class="nx">extractPdfContent</span><span class="p">(</span><span class="nx">elementNode</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="nx">childContent</span><span class="p">.</span><span class="nx">length</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">          <span class="nx">result</span><span class="p">.</span><span class="nx">push</span><span class="p">(...</span><span class="nx">childContent</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">      <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="nx">result</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>Include this in a <code>DocumentPreview.vue</code> component that can toggle between PDF viewer and a rich text viewer - if needed, of course.</p>
<h3 id="layout-and-page">Layout and Page</h3>
<p>Update layout -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-vue" data-lang="vue"><span class="line"><span class="cl"><span class="c">&lt;!--</span> <span class="nx">layouts</span><span class="o">/</span><span class="k">default</span><span class="p">.</span><span class="nx">vue</span> <span class="o">--&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;min-h-screen bg-surface-ground&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">nav</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;bg-surface-card border-b border-surface-border md:px-12 px-6&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34; h-16 flex items-center justify-between&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">h1</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;text-xl font-bold text-black&#34;</span><span class="p">&gt;</span><span class="nx">docgen</span><span class="p">&lt;/</span><span class="nt">h1</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;flex items-center gap-4&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;</span><span class="nt">button</span>
</span></span><span class="line"><span class="cl">            <span class="na">variant</span><span class="o">=</span><span class="s">&#34;secondary&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="o">:</span><span class="na">icon</span><span class="o">=</span><span class="s">&#34;isDark ? &#39;sun&#39; : &#39;moon&#39;&#34;</span>
</span></span><span class="line"><span class="cl">            <span class="nt">@click</span><span class="s">=&#34;toggleTheme&#34;</span><span class="p">
</span></span></span><span class="line"><span class="cl">            <span class="o">:</span><span class="na">title</span><span class="o">=</span><span class="s">&#34;isDark ? &#39;Switch to light mode&#39; : &#39;Switch to dark mode&#39;&#34;</span>
</span></span><span class="line"><span class="cl">          <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">nav</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">main</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34; py-6&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">slot</span> <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">main</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">setup</span> <span class="na">lang</span><span class="o">=</span><span class="s">&#34;ts&#34;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">style</span><span class="p">&gt;&lt;/</span><span class="nt">style</span><span class="p">&gt;</span>
</span></span></code></pre></div><p>&hellip; and the page.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-vue" data-lang="vue"><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;mx-auto px-12&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;grid grid-cols-1 lg:grid-cols-2 gap-6&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;flex flex-col gap-6&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;card&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;toolbar&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">&lt;</span><span class="nt">h2</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;section-title&#34;</span><span class="p">&gt;</span><span class="nx">Template</span><span class="p">&lt;/</span><span class="nt">h2</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;h-[600px] overflow-hidden&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">&lt;</span><span class="nt">ClientOnly</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">              <span class="p">&lt;</span><span class="nt">RichTextEditor</span> <span class="nt">v-model</span><span class="o">=</span><span class="s">&#34;template&#34; class=&#34;h-full&#34;</span><span class="p"> /&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">&lt;/</span><span class="nt">ClientOnly</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;card&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="c">&lt;!--</span> <span class="nx">Removed</span> <span class="nx">flex</span><span class="o">-</span><span class="mi">1</span> <span class="nx">from</span> <span class="nx">card</span> <span class="o">--&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;toolbar&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">&lt;</span><span class="nt">h2</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;section-title&#34;</span><span class="p">&gt;</span><span class="nx">Data</span> <span class="p">(</span><span class="nx">JSON</span><span class="p">)&lt;/</span><span class="nt">h2</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;editor-container h-[400px] overflow-hidden&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="p">&lt;</span><span class="nt">DataEditor</span> <span class="nt">v-model</span><span class="o">=</span><span class="s">&#34;jsonData&#34; class=&#34;h-full&#34;</span><span class="p"> /&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;card&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="c">&lt;!--</span> <span class="nx">Removed</span> <span class="nx">flex</span><span class="o">-</span><span class="mi">1</span> <span class="nx">from</span> <span class="nx">card</span> <span class="o">--&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;toolbar&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;</span><span class="nt">h2</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;section-title&#34;</span><span class="p">&gt;</span><span class="nx">Generated</span> <span class="nx">Document</span><span class="p">&lt;/</span><span class="nt">h2</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;toolbar-actions&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">            <span class="c">&lt;!--</span> <span class="p">&lt;</span><span class="nt">Button</span> <span class="na">variant</span><span class="o">=</span><span class="s">&#34;secondary&#34;</span> <span class="na">icon</span><span class="o">=</span><span class="s">&#34;refresh&#34;</span><span class="p">&gt;</span><span class="nx">Auto</span><span class="o">-</span><span class="nx">refresh</span><span class="p">&lt;/</span><span class="nt">Button</span><span class="p">&gt;</span> <span class="o">--&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;h-[1024px] overflow-hidden&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">          <span class="p">&lt;</span><span class="nt">DocumentPreview</span> <span class="nt">:data</span><span class="o">=</span><span class="s">&#34;jsonData&#34;</span> <span class="na">class</span><span class="o">=</span><span class="s">&#34;h-full&#34;</span> <span class="p">/&gt;</span>
</span></span><span class="line"><span class="cl">        <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">      <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">    <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="p">&lt;/</span><span class="nt">div</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">setup</span> <span class="na">lang</span><span class="o">=</span><span class="s">&#34;ts&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">useDocumentStore</span> <span class="p">}</span> <span class="nx">from</span> <span class="s2">&#34;~/stores/document&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">storeToRefs</span> <span class="p">}</span> <span class="nx">from</span> <span class="s2">&#34;pinia&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">documentStore</span> <span class="o">=</span> <span class="nx">useDocumentStore</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="p">{</span> <span class="nx">template</span><span class="p">,</span> <span class="nx">jsonData</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">storeToRefs</span><span class="p">(</span><span class="nx">documentStore</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">style</span> <span class="na">scoped</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="cm">/* styles */</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">style</span><span class="p">&gt;</span>
</span></span></code></pre></div><h2 id="run-the-app">Run the app!</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmd" data-lang="cmd"><span class="line"><span class="cl">bun dev
</span></span></code></pre></div><p>This should populate some default template and data, and show you a nice preview of the merged content.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Generating PDF on client is not hard and keeps your server load low.</p>
<p>While generating PDFs client-side can certainly help in most use cases (especially for enterprise customers), it may not be a pleasant experience for mobile users. Also, features like emailing PDF, supporting non-PDF formats, etc. are hard to achieve.</p>
<p>See complete code on <a href="http://github.com/prashanth1k/docgen">GitHub</a>.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Document Analysis App with Azure Document Intelligence</title>
      <link>https://techformist.com/document-analysis-app-nuxt/</link>
      <pubDate>Sun, 23 Feb 2025 06:30:00 +0000</pubDate>
      
      <guid>https://techformist.com/document-analysis-app-nuxt/</guid>
      <description>&lt;p&gt;We will walk through the steps to build a document analysis web application using Nuxt 3 and Azure Document Intelligence. This application allows users to -&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;upload PDF or images&lt;/li&gt;
&lt;li&gt;specify fields (if needed)&lt;/li&gt;
&lt;li&gt;extract specific fields of information&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Features will include -&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;🔍 Extract custom fields from identification documents&lt;/li&gt;
&lt;li&gt;🔄 Real-time document analysis status&lt;/li&gt;
&lt;li&gt;💾 Download extracted data as JSON&lt;/li&gt;
&lt;li&gt;🎨 UI with Shadcn-vue /Tailwind CSS&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here&amp;rsquo;s a quick demo.
&lt;div style=&#34;position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;&#34;&gt;
      &lt;iframe allow=&#34;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen&#34; loading=&#34;eager&#34; referrerpolicy=&#34;strict-origin-when-cross-origin&#34; src=&#34;https://www.youtube.com/embed/_FLmnAfiqUo?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0&#34; style=&#34;position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;&#34; title=&#34;YouTube video&#34;&gt;&lt;/iframe&gt;
    &lt;/div&gt;
&lt;/p&gt;</description>
      <content:encoded><![CDATA[<p>We will walk through the steps to build a document analysis web application using Nuxt 3 and Azure Document Intelligence. This application allows users to -</p>
<ul>
<li>upload PDF or images</li>
<li>specify fields (if needed)</li>
<li>extract specific fields of information</li>
</ul>
<p>Features will include -</p>
<ul>
<li>🔍 Extract custom fields from identification documents</li>
<li>🔄 Real-time document analysis status</li>
<li>💾 Download extracted data as JSON</li>
<li>🎨 UI with Shadcn-vue /Tailwind CSS</li>
</ul>
<p>Here&rsquo;s a quick demo.
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/_FLmnAfiqUo?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>
</p>
<p>While the demo covers only identification documents, the same program can be extended to any document type - application forms, invoices, etc.</p>
<h2 id="prerequisites">Prerequisites</h2>
<p>Before we start, make sure you have the following:</p>
<ul>
<li>Node.js 18.x or later</li>
<li>An Azure account with a Document Intelligence resource</li>
<li>Azure Document Intelligence API key and endpoint</li>
</ul>
<h2 id="step-1-setting-up-the-project">Step 1: Setting Up the Project</h2>
<p>Create a blank Nuxt app..</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmd" data-lang="cmd"><span class="line"><span class="cl">pnpx nuxi@latest init docformist
</span></span></code></pre></div><p>Get your API key and environment details from <a href="https://azure.microsoft.com/products/ai-services/ai-document-intelligence">Azure site</a>. You can sign up for free and use a Free plan to test out the service.</p>
<p>Create a <code>.env</code> file in the root directory with your Azure credentials:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-env" data-lang="env"><span class="line"><span class="cl"><span class="nv">AZURE_DOCUMENT_INTELLIGENCE_KEY</span><span class="o">=</span>your_key_here
</span></span><span class="line"><span class="cl"><span class="nv">AZURE_DOCUMENT_INTELLIGENCE_ENDPOINT</span><span class="o">=</span>your_endpoint_here
</span></span></code></pre></div><p>Add Nuxt modules -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmd" data-lang="cmd"><span class="line"><span class="cl">pnpx nuxi@latest module add shadcn-nuxt
</span></span><span class="line"><span class="cl">pnpx nuxi@latest module add tailwindcss
</span></span></code></pre></div><p>Initialize shadcn and tailwind -</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-cmd" data-lang="cmd"><span class="line"><span class="cl">pnpx tailwindcss init
</span></span><span class="line"><span class="cl">pnpx shadcn-vue@latest init
</span></span></code></pre></div><p>Accept sensible defaults.</p>
<h2 id="step-2-configuring-nuxt">Step 2: Configuring Nuxt</h2>
<p>Ensure <code>nuxt.config.ts</code> is updated. Add the CSS file reference:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-ts" data-lang="ts"><span class="line"><span class="cl"><span class="c1">// ...existing code...
</span></span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="nx">defineNuxtConfig</span><span class="p">({</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// ...existing code...
</span></span></span><span class="line"><span class="cl">  <span class="nx">modules</span><span class="o">:</span> <span class="p">[</span><span class="s2">&#34;shadcn-nuxt&#34;</span><span class="p">,</span> <span class="s2">&#34;@nuxt/icon&#34;</span><span class="p">,</span> <span class="s2">&#34;@nuxtjs/tailwindcss&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">  <span class="nx">css</span><span class="o">:</span> <span class="p">[</span><span class="s2">&#34;~/assets/css/tailwind.css&#34;</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// ...existing code...
</span></span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span></code></pre></div><p>Add shadcn-vue components.</p>
<pre tabindex="0"><code>pnpx shadcn-vue@latest add label button card input separator
</code></pre><h2 id="step-3-creating-the-document-uploader-component">Step 3: Creating the Document Uploader Component</h2>
<p>Create <code>DocumentUploader.vue</code> in the components folder to handle file uploads:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-vue" data-lang="vue"><span class="line"><span class="cl"><span class="c1">// filepath: /c:/dev/1p/nuxt/docformist/components/DocumentUploader.vue
</span></span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="c">&lt;!--</span> <span class="p">...</span><span class="nx">existing</span> <span class="nx">code</span><span class="p">...</span> <span class="o">--&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">setup</span> <span class="na">lang</span><span class="o">=</span><span class="s">&#34;ts&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">emit</span> <span class="o">=</span> <span class="nx">defineEmits</span><span class="p">([</span><span class="s2">&#34;file-selected&#34;</span><span class="p">]);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">handleFileSelect</span> <span class="o">=</span> <span class="p">(</span><span class="nx">event</span><span class="o">:</span> <span class="nx">Event</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">file</span> <span class="o">=</span> <span class="p">(</span><span class="nx">event</span><span class="p">.</span><span class="nx">target</span> <span class="nx">as</span> <span class="nx">HTMLInputElement</span><span class="p">).</span><span class="nx">files</span><span class="o">?</span><span class="p">.[</span><span class="mi">0</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="nx">file</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">emit</span><span class="p">(</span><span class="s2">&#34;file-selected&#34;</span><span class="p">,</span> <span class="nx">file</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">handleDrop</span> <span class="o">=</span> <span class="p">(</span><span class="nx">event</span><span class="o">:</span> <span class="nx">DragEvent</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">file</span> <span class="o">=</span> <span class="nx">event</span><span class="p">.</span><span class="nx">dataTransfer</span><span class="o">?</span><span class="p">.</span><span class="nx">files</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="nx">file</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">emit</span><span class="p">(</span><span class="s2">&#34;file-selected&#34;</span><span class="p">,</span> <span class="nx">file</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span></code></pre></div><h2 id="step-4-creating-the-document-viewer-component">Step 4: Creating the Document Viewer Component</h2>
<p>Create <code>DocumentViewer.vue</code> to preview the uploaded document:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-vue" data-lang="vue"><span class="line"><span class="cl"><span class="c1">// filepath: /c:/dev/1p/nuxt/docformist/components/DocumentViewer.vue
</span></span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="c">&lt;!--</span> <span class="p">...</span><span class="nx">existing</span> <span class="nx">code</span><span class="p">...</span> <span class="o">--&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">setup</span> <span class="na">lang</span><span class="o">=</span><span class="s">&#34;ts&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">props</span> <span class="o">=</span> <span class="nx">defineProps</span><span class="o">&lt;</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">file</span><span class="o">:</span> <span class="nx">File</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span><span class="o">&gt;</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">fileUrl</span> <span class="o">=</span> <span class="nx">ref</span><span class="p">&lt;</span><span class="nt">string</span> <span class="err">|</span> <span class="na">null</span><span class="p">&gt;(</span><span class="kc">null</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">isPDF</span> <span class="o">=</span> <span class="nx">computed</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="nx">props</span><span class="p">.</span><span class="nx">file</span><span class="p">.</span><span class="nx">type</span> <span class="o">===</span> <span class="s2">&#34;application/pdf&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">watch</span><span class="p">(</span>
</span></span><span class="line"><span class="cl">  <span class="p">()</span> <span class="p">=&gt;</span> <span class="nx">props</span><span class="p">.</span><span class="nx">file</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="p">(</span><span class="nx">newFile</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="nx">fileUrl</span><span class="p">.</span><span class="nx">value</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">URL</span><span class="p">.</span><span class="nx">revokeObjectURL</span><span class="p">(</span><span class="nx">fileUrl</span><span class="p">.</span><span class="nx">value</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="nx">fileUrl</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="nx">URL</span><span class="p">.</span><span class="nx">createObjectURL</span><span class="p">(</span><span class="nx">newFile</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span> <span class="nx">immediate</span><span class="o">:</span> <span class="kc">true</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">onBeforeUnmount</span><span class="p">(()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="nx">fileUrl</span><span class="p">.</span><span class="nx">value</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">URL</span><span class="p">.</span><span class="nx">revokeObjectURL</span><span class="p">(</span><span class="nx">fileUrl</span><span class="p">.</span><span class="nx">value</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span></code></pre></div><h2 id="step-5-creating-the-api-endpoint">Step 5: Creating the API Endpoint</h2>
<p>Create an API endpoint <code>analyze.post.ts</code> for document analysis with Azure Document Intelligence:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">DocumentIntelligence</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">getLongRunningPoller</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">isUnexpected</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span> <span class="kr">from</span> <span class="s2">&#34;@azure-rest/ai-document-intelligence&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">AzureKeyCredential</span> <span class="p">}</span> <span class="kr">from</span> <span class="s2">&#34;@azure/core-auth&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="nx">formidable</span> <span class="kr">from</span> <span class="s2">&#34;formidable&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="kr">import</span> <span class="p">{</span> <span class="nx">readFile</span> <span class="p">}</span> <span class="kr">from</span> <span class="s2">&#34;fs/promises&#34;</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">export</span> <span class="k">default</span> <span class="nx">defineEventHandler</span><span class="p">(</span><span class="kr">async</span> <span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// ...existing code...
</span></span></span><span class="line"><span class="cl"><span class="p">});</span>
</span></span></code></pre></div><h2 id="step-6-building-the-main-page">Step 6: Building the Main Page</h2>
<p>Integrate the components and API in the main page <code>index.vue</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-vue" data-lang="vue"><span class="line"><span class="cl"><span class="c1">// filepath: /c:/dev/1p/nuxt/docformist/pages/index.vue
</span></span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">  <span class="c">&lt;!--</span> <span class="p">...</span><span class="nx">existing</span> <span class="nx">code</span><span class="p">...</span> <span class="o">--&gt;</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">template</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">&lt;</span><span class="nt">script</span> <span class="na">setup</span> <span class="na">lang</span><span class="o">=</span><span class="s">&#34;ts&#34;</span><span class="p">&gt;</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">selectedFile</span> <span class="o">=</span> <span class="nx">ref</span><span class="p">&lt;</span><span class="nt">File</span> <span class="err">|</span> <span class="na">null</span><span class="p">&gt;(</span><span class="kc">null</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">extractedData</span> <span class="o">=</span> <span class="nx">ref</span><span class="p">&lt;</span><span class="nt">Record</span><span class="p">&lt;</span><span class="nt">string</span><span class="err">,</span> <span class="na">any</span><span class="p">&gt;</span> <span class="o">|</span> <span class="kc">null</span><span class="o">&gt;</span><span class="p">(</span><span class="kc">null</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">loading</span> <span class="o">=</span> <span class="nx">ref</span><span class="p">(</span><span class="kc">false</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">selectedFields</span> <span class="o">=</span> <span class="nx">ref</span><span class="p">&lt;</span><span class="nt">string</span><span class="err">[]</span><span class="p">&gt;([]);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">handleFileSelect</span> <span class="o">=</span> <span class="p">(</span><span class="nx">file</span><span class="o">:</span> <span class="nx">File</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">selectedFile</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="nx">file</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="nx">extractedData</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="kc">null</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">handleExtract</span> <span class="o">=</span> <span class="nx">async</span> <span class="p">()</span> <span class="p">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">selectedFields</span><span class="p">.</span><span class="nx">value</span><span class="p">.</span><span class="nx">length</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="s2">&#34;No fields selected&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nx">loading</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="k">try</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">formData</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">FormData</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">    <span class="nx">formData</span><span class="p">.</span><span class="nx">append</span><span class="p">(</span><span class="s2">&#34;file&#34;</span><span class="p">,</span> <span class="nx">selectedFile</span><span class="p">.</span><span class="nx">value</span><span class="o">!</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nx">formData</span><span class="p">.</span><span class="nx">append</span><span class="p">(</span><span class="s2">&#34;fields&#34;</span><span class="p">,</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="nx">selectedFields</span><span class="p">.</span><span class="nx">value</span><span class="p">));</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">response</span> <span class="o">=</span> <span class="nx">await</span> <span class="nx">fetch</span><span class="p">(</span><span class="s2">&#34;/api/analyze&#34;</span><span class="p">,</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">method</span><span class="o">:</span> <span class="s2">&#34;POST&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nx">body</span><span class="o">:</span> <span class="nx">formData</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">});</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">response</span><span class="p">.</span><span class="nx">ok</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">throw</span> <span class="k">new</span> <span class="nb">Error</span><span class="p">(</span><span class="s2">&#34;Failed to analyze document&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">extractedData</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="nx">await</span> <span class="nx">response</span><span class="p">.</span><span class="nx">json</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="nx">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="s2">&#34;Error analyzing document:&#34;</span><span class="p">,</span> <span class="nx">error</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// TODO: Show error toast
</span></span></span><span class="line"><span class="cl">  <span class="p">}</span> <span class="k">finally</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">loading</span><span class="p">.</span><span class="nx">value</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span>
</span></span></code></pre></div><h2 id="step-7-run-the-application">Step 7: Run the Application</h2>
<p>Start the development server:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">pnpm dev
</span></span></code></pre></div><p>Visit <code>http://localhost:3000</code> to upload a document and see the results.</p>
<h2 id="conclusion">Conclusion</h2>
<p>That was a quick demo of how we build a document analysis / identification app using Azure Document Intelligence. Customize the application further to meet your needs.</p>
<p>Here&rsquo;s the Github repo - <a href="https://github.com/prashanth1k/docformist">https://github.com/prashanth1k/docformist</a>.</p>
<p>All of this was done within 2 hours - thanks to AI. The era of personal applications is here.</p>
]]></content:encoded>
    </item>
    
  </channel>
</rss>
