Writing

Writing Tools / Vanilla JS · July 28, 2025 · 4 min read

QuoraFormatter: A Zero-Backend Workbench for Long-Form Curation Posts

Assembling a twenty-image curation post in Quora's editor was slow and error-prone. QuoraFormatter is a single HTML file that turns a library of images, captions, and sources into a numbered, paste-ready post.

010203quill · rich HTML1.2.3.copy as HTMLone postfig. 13 — curation post workbench

Much of my writing on Quora is long-form curation: a numbered sequence of photographs or artworks, each with a caption explaining what makes it work, and a list of sources at the end. If you've read The Artful Eye, that's the format. The thinking is the fun part. The assembly is not.

Building one of these posts directly in Quora's editor meant pasting an image, typing a number and caption, adding a separator, scrolling back up to check I hadn't skipped a number, and keeping a separate note of where every image came from. Reorder two items and you renumber everything by hand. Twenty items in, it's very easy to get something wrong.

QuoraFormatter moves all of that out of the editor. You build a library of items, pick the ones you want, drag them into order, and generate the whole post in one click, ready to paste.

1
HTML file, no build step
4
steps: add, select, arrange, generate
0
servers of my own
2
shipped versions (v1.0, v1.1)

The workflow

The page is split into the same four numbered steps I was already doing by hand:

  1. Add content
    manual or Reddit
    Image URLs, caption, source.
  2. Content library
    localStorage
    Filter used/unused, sort by date.
  3. Arrange post
    drag to reorder
    Staging area for this post only.
  4. Generate & copy
    Quill editor
    Numbered post plus a sources list.

Separating the library from the staging area was the key design decision. The library holds everything I've collected, which is far more than any single post uses. The staging area is a scratch copy for the post I'm writing right now. Staging copies an item instead of moving it, so clearing a draft never touches the library.

Why a single HTML file

I wanted something I could open on any machine and start using with no install, account, or server. So the whole tool is one index.html: Tailwind from its CDN for styling, Quill 1.3.6 as the output editor, and about 400 lines of vanilla JavaScript. It's hosted on GitHub Pages. The previous version is kept beside it as v1.0.html, so I could always fall back if v1.1 broke something mid-post.

All state lives in localStorage under one key, quoraFormatterData. Before using it, the script checks that storage is actually available (some private-browsing modes throw), and if the saved blob is corrupted it says so and starts fresh instead of failing silently.

Generating the post

The output side is the reason Quill is there. Quora's editor accepts rich HTML from the clipboard, so if I can produce a rich-text document with real inline images, a normal copy-paste carries everything across. Generation rebuilds the editor contents from the staged items:

text
stagedItems.forEach((item, index) => { quill.insertText(quill.getLength() - 1, `${index + 1}. ${item.caption}\n`); item.imageUrls.forEach(url => { quill.insertEmbed(quill.getLength() - 1, 'image', url); quill.insertText(quill.getLength() - 1, '\n'); }); quill.insertText(quill.getLength() - 1, '-\n'); if (item.sourceUrl) sourcesToDisplay.push(item.sourceUrl); const originalItem = contentItems.find(ci => ci.id === item.id); if (originalItem) originalItem.used = true; });

Numbering comes from the staged order, so dragging an item from position 3 to position 15 renumbers everything with no extra work. Sources are collected separately into their own textarea with a "Copy Sources" button, because they go at the end of the post and I want to paste them as plain text. Generating also marks every included item as used in the library. The "Unused" filter then shows what I haven't published yet, which turned out to be the feature I rely on most.

"Copy Post" selects the editor's DOM contents and calls document.execCommand('copy'). That API is deprecated, but it reliably copies the rendered rich HTML, images included, which the plain-text clipboard path would not.

What v1.1 added

v1.0 was the core loop with one image per item. v1.1 came out of actually using it:

v1.0

One image URL per item. Manual entry only. Library plus staging, Quill output, JSON export/import of the library.

v1.1

Multiple images per item. Drag-and-drop image upload via ImgBB. Fetch a post straight from a Reddit URL. Used/unused tags with filters, date sort, and an edit modal.

Going to multiple images changed the data model from imageUrl to an imageUrls array. I didn't want to lose a library I'd already been building, so loading runs a small in-place migration:

text
contentItems.forEach(item => { if (item.imageUrl && !item.imageUrls) { item.imageUrls = [item.imageUrl]; delete item.imageUrl; } });

The Reddit fetcher takes a post URL, appends .json, and reads data[0].data.children[0].data. For galleries it rebuilds each image's i.redd.it URL from media_metadata (the ID plus the extension from the MIME type). For single-image posts it uses url_overridden_by_dest. The post title becomes the caption and the permalink becomes the source, so attribution is automatic. Reddit doesn't serve that JSON nicely to arbitrary browser pages, so the request goes through ScraperAPI with a key you paste in once. You can also drop a Reddit link directly onto the URL field and it fetches immediately.

Where it went next

QuoraFormatter is the ancestor of the tooling I use today. The library-of-items idea grew into a small Firestore-backed CMS that several tools write into: a right-click browser extension for reddit.com, and reddit-core, my archive search front-end. That fixed the biggest limitation here, which is that a localStorage library lives in one browser on one machine. The remaining fixes are restoring export/import, escaping captions, and moving the API keys out of the client.

github.com/shubhrgunjan
QuoraFormatter
git clone https://github.com/shubhrgunjan/QuoraFormatter

It's a small tool, but it changed how I write. Once assembly was mechanical, I could spend my time on the part that matters: choosing the images and explaining why they work.