Skip to content

Bruce Hart

AI Codex Personal Productivity Tools

Chrome, the Clipboard, and Two Buttons

Portrait of Bruce Hart Bruce Hart
6 min read

The AI browser future is cool. I still live in Chrome + the clipboard.

I do not use the Atlas or Comet browser yet.

Maybe one day. But right now the tech and the speed are not there (for me) in a way that beats the thing I already trust: a normal browser tab, a good prompt, and a tiny piece of JavaScript.

I am not switching browsers, I am upgrading my clipboard

The browser pitch is usually: "move your whole workflow into the AI thing."

My reality is smaller: I need one-off extraction from a page, I need it in a format I can keep, and I need it fast.

Not a new browser, but a better path from "page" to "structured data".

The Gemini Chrome plugin is basically a one-time script generator

The Gemini Chrome plugin has been surprisingly useful for this.

My move is simple:

  1. Open the page with the data.
  2. Ask Gemini to write a JavaScript snippet.
  3. Paste it into DevTools console.
  4. Let it copy the result to the clipboard.

It is not elegant. It is very effective.

The key trick is to ask Gemini for output that is immediately pasteable into Google Sheets.

Use \t between columns and \n between rows.

When you paste that into Sheets, it just lands in the grid with basically zero friction.

That is the mental model: the browser is a tiny ETL tool, and the clipboard is your API.

Tiny automation beats big workflow rewrites

This is the pattern I keep coming back to with AI tools: big rewrites feel exciting and cost attention, while tiny automations feel boring and save attention.

So I end up building little helpers.

Sometimes it is a one-off console snippet.

Sometimes it is a userscript.

Userscripts are my favorite "just enough" automation layer

Codex has been great for creating little userscripts that I run with TamperMonkey in Chrome.

The bar is low: add 1-2 buttons, read the page, copy the thing I always copy anyway.

The value is high because it removes the most annoying parts of repetitive work: selecting text, reformatting, renaming files, and double-checking numbers.

A Walgreens receipts workflow (two buttons, done)

I periodically log into our family Walgreens account and pull receipts to record for HSA reimbursement.

HSA mechanics are nice here: contributions are tax-advantaged, qualified medical expenses can be reimbursed tax-free, and even if you do not reimburse immediately, keeping good records lets you reimburse later.

(Not tax advice. Just a habit that has worked well for me.)

My personal system looks like this: Google Drive holds the receipt PDFs in a predictable folder, and Google Sheets holds a ledger of claims.

In the Sheet I keep columns like date (sometimes multiple: purchase date, service date, claim date), vendor, category, amount, receipt path/link, and reimbursed status.

The annoying part is not the accounting. It is the copy/paste and the consistent naming.

So I made a little userscript.

It adds two buttons: "Copy row" copies a tab-separated row that pastes cleanly into Google Sheets, and "Copy PDF path" copies the Drive path I want to use when saving the PDF, with a consistent filename.

Here is the script (canonical source; if you have TamperMonkey installed, opening this URL should offer an Install button):

https://github.com/brucehart/userscripts/raw/refs/heads/main/Walgreens/Walgreens%20Order%20Helpers-1.5.user.js

// ==UserScript==
// @name         Walgreens Order Helpers
// @namespace    https://github.com/brucehart/userscripts
// @version      1.5
// @description  Copy tab-separated receipt row and copy PDF path placeholder to clipboard.
// @author       Bruce J. Hart
// @match        https://www.walgreens.com/orderhistory/order/details-ui*
// @grant        GM_setClipboard
// @grant        GM_addStyle
// ==/UserScript==

(function () {
  'use strict';

  /* ------------------------------------------------------------- *
   *  Helpers                                                      *
   * ------------------------------------------------------------- */
  const $$ = (sel, ctx = document) => [...ctx.querySelectorAll(sel)];
  const firstTextMatch = (rx) => (document.body.innerText.match(rx) || [])[0] || '';

  /** Extract the transaction date as a Date object. */
  function getTransactionDate() {
    const raw = firstTextMatch(/\b(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)[a-z]*\s+\d{1,2},?\s+\d{4}\b|\d{1,2}\/\d{1,2}\/\d{4}/i);
    return raw ? new Date(raw.replace(/,/g, '')) : null;
  }

  /** Find the first "Total Rx Items:" and first "Total FSA Items:" values and return their sum. */
  function getEligibleTotal() {
    const rxMatch  = firstTextMatch(/Total\s+Rx\s+Items:\s*\$?([\d,.]+)/i);
    const fsaMatch = firstTextMatch(/Total\s+FSA[^\n]*Items?:\s*\$?([\d,.]+)/i);
    const rx  = rxMatch  ? parseFloat(rxMatch.replace(/[^\d.]/g, ''))  : 0;
    const fsa = fsaMatch ? parseFloat(fsaMatch.replace(/[^\d.]/g, '')) : 0;
    return (rx + fsa).toFixed(2); // "38.41"
  }

  /* ------------------------------------------------------------- *
   *  Button actions                                               *
   * ------------------------------------------------------------- */
  function copyRow() {
    const dt = getTransactionDate();
    if (!dt) return;
    const m = dt.getMonth() + 1;
    const d = dt.getDate();
    const y = dt.getFullYear().toString().slice(-2);
    const dateStr = `${m}/${d}/${y}`; // M/D/YY

    const total = getEligibleTotal();
    const row = `${dateStr}\t${dateStr}\t${dateStr}\t"Stef"\t"Walgreens"\t"Rx"\t${total}`;
    GM_setClipboard(row, 'text');
  }

  /** Copy the Google Drive path placeholder to clipboard. */
  function copyPdfPath() {
    const dt = getTransactionDate();
    if (!dt) return;
    const yyyy = dt.getFullYear();
    const iso  = dt.toISOString().slice(0, 10); // YYYY-MM-DD

    const [dollars, centsRaw] = getEligibleTotal().split('.');
    const cents = centsRaw.padStart(2, '0');

    const path = `G:\\My Drive\\Personal\\Health\\${yyyy}\\${iso}_Walgreens_${dollars}_${cents}.pdf`;
    GM_setClipboard(path, 'text');
  }

  /* ------------------------------------------------------------- *
   *  UI                                                           *
   * ------------------------------------------------------------- */
  function addButton(label, onclick, bottomPx) {
    const btn = document.createElement('button');
    btn.textContent = label;
    btn.onclick = onclick;
    btn.style.cssText = `
      position:fixed; right:16px; bottom:${bottomPx}px;
      z-index:9999; padding:8px 12px;
      background:#d4430b; color:#fff; border:0; border-radius:8px;
      font:600 14px/1 sans-serif; cursor:pointer;
    `;
    document.body.appendChild(btn);
  }

  addButton('Copy row', copyRow, 96);
  addButton('Copy PDF path', copyPdfPath, 48);

  GM_addStyle('button:hover{opacity:.85}');
})();

You can see what it is doing in plain English: grab the transaction date from the page text, grab the eligible totals (Rx + FSA items), build a row with tabs, put it on the clipboard, build a predictable PDF filename and Drive path, and put that on the clipboard too.

The actual workflow is almost comically low effort

When I do a Walgreens session, the loop is:

  1. Open an order detail page.
  2. Click "Copy row".
  3. Paste into Google Sheets.
  4. Download/save the PDF receipt.
  5. Click "Copy PDF path".
  6. Paste the path when saving so the file lands in the right Drive folder with the right name.

Two buttons. No fiddly formatting.

It is the kind of automation that does not feel like "automation". It just feels like removing friction.

Could Codex or Claude Cowork log into Walgreens and do this automatically? Yes probably and in the near future I'll probably automate more things this way but for the time it takes to do it and check, this simple userscript currently gives me the best return on investment.

The pattern is the point (not Walgreens)

The Walgreens part is just an example.

The deeper idea is: if you can name the repetitive part, you can usually delete it. If you always paste into Sheets, output TSV. If you always name files, encode the naming rule once. If you always do it in the browser, keep the automation in the browser.

If you want to borrow this, start by writing down the 3-5 fields you always end up copying. Then ask an AI to generate the smallest possible script that turns the page into those fields.

If you try this pattern (Gemini console snippets or TamperMonkey userscripts), I would love to hear what you automate.