Skip to content

Acelab · Material Hub

AceClipper and an internal collector: turning web pages into product data

AceClipper, public on the Chrome Web Store, turns manufacturer pages into products architects paste into a schedule. The internal extension collects product files for reviewers without AI; replayed offline on 23 reviewed products, it found 71% of the files they approved, up 14 points from 57% before a round of fixes.

Problem
Product data lives on third-party web pages that each hide it differently, and it has to reach an application as structured records that people can trust.
My part
I built both extensions end to end and the pieces they connect to: token handoff and LLM parsing in the .NET API, the web app’s import flow, and the review platform’s paste dialogs.
Result
Architects paste products from manufacturer pages into a project schedule with a public Chrome extension; in an offline replay of 23 reviewed products, the internal one’s recall of reviewer-approved files rose 14 points, to 71% (117 of 164).

The problem

Manufacturers’ product data sits in text, structured data and scripts, behind tabs and dropdowns, in frames and linked PDFs, and every site differs. At Acelab, whose Material Hub is a materials-management platform for architects, owners and manufacturers, I built two Chrome extensions to bring it in:

  • AceClipper, public on the Chrome Web Store (opens in a new tab). An architect opens it on a manufacturer’s page and pastes the products it finds into a project schedule, the spreadsheet-style product list for a drawing set.
  • An internal extension for product-data reviewers. Automated scraping misses swatches on many manufacturer pages, so it collects swatches, finish photos and linked documents for a reviewer to check.

How it works

Both extensions use Manifest V3, read a page only after a click, and hand over a versioned JSON clip on the clipboard. They differ in where extraction happens. The demo below follows one clip from page to schedule.

Illustration · made-up data

  1. 1 Capture
  2. 2 Review
  3. 3 Paste

tiles.example/terrazzo

Terrazzo collection

  • Terrazzo Field Tile24 × 24 in
  • Terrazzo Bullnose Trim3 × 24 in
  • Fog
  • Ember
  • Moss

Project schedule · 2 products added

Schedule rows created from the clip
Image Tag Product Size
TZ1Terrazzo Field Tile24 × 24 in
TZ2Terrazzo Bullnose Trim3 × 24 in
The clip behind it (simplified JSON)
{
  "version": 1,
  "source": "https://tiles.example/terrazzo",
  "products": [
    {
      "name": "Terrazzo Field Tile",
      "size": "24 × 24 in",
      "colors": ["Fog", "Ember", "Moss"],
      "images": ["…/field.jpg"]
    },
    {
      "name": "Terrazzo Bullnose Trim",
      "size": "3 × 24 in",
      "colors": ["Fog", "Ember", "Moss"],
      "images": ["…/trim.jpg"]
    }
  ]
}

Pasted into a project schedule: two new products, each with its image, ready to specify.

How the public extension’s hand-off works, redrawn with made-up products and a simplified clip, not the extension’s real format or interface. Both extensions read a page only after a click and hand over a versioned clip; neither writes into the application, and the schedule decides what to create when you paste.
Two extraction strategies with one hand-offBoth extensions start from a manufacturer’s product page, which is third-party and untrusted. Public extension: on a click, a content script captures the page’s text, JSON-LD and images; the API, called with a short-lived scoped token, parses the capture with an LLM and validates the result; the popup shows the products for review and copies them. Internal extension: on a click, a page reader runs in each frame and collects with rules and no AI; the side panel runs the collect and reads linked PDFs; the reviewer checks the status, picks anything missed and copies the result. Both hand off the same way, highlighted: a versioned JSON clip, copied and pasted into the web app’s schedule, which imports and matches the products, or into the review platform, where the reviewer adds them.PUBLIC EXTENSIONINTERNAL EXTENSIONManufacturer product pagethird-party markup · untrusted inputon clickon clickContent scripttext · JSON-LD · imagesPage readerrules, no AI · per framecaptureresultsAPI + LLMscoped token · validatedSide panelruns the collect · PDFsproductsitemsPopupreview · copyReviewstatus · pick · copyWeb app scheduleimport · matchReview platformpaste · review · addcopy · pasteversioned JSON clip
Same input, two ways to extract, one hand-off: the public extension parses on the server with an LLM, the internal one reads the page with rules, and both pass a versioned JSON clip (highlighted).

AceClipper is a popup, a service worker and a content script injected only when needed, on https pages. Its capture holds the URL, title, up to 10,000 characters of visible text, meta tags, JSON-LD and up to 50 image URLs, each part guarded so one failure can’t sink the rest. In the .NET API, my endpoint asks OpenAI for products in a fixed shape and validates the answer, and the API drops any link from the model that isn’t http(s). Pasted into a schedule, the clip opens my import modal; the server creates the products, downloads and checks each image, and can match just the new ones.

The internal extension runs everything from Chrome’s side panel. Its service worker only opens the panel and holds no state, so suspension loses nothing. The panel injects a reader into the page and each readable frame, and results come back in an envelope that separates a value from an error, because a frame whose code throws otherwise returns nothing. The reader opens tabs, dropdowns and closed sections, never a product option, a form or a link off the page. Linked PDFs are fetched without the reviewer’s cookies and read with pdf.js. A run ends with a status: “complete” needs a count the page itself states that matches what was found, with nothing failed, cut short or unreadable.

On the receiving side, I built the review platform’s paste dialogs for images and documents and changed the Python workers so reviewer-added document links survive a rescrape. I later re-implemented the extension’s rules as a page scan in the server-side scraper, with no LLM, tested against the extension’s frozen ground truth.

Decisions

Decision

Keep secrets and the model on the server; give the extension a short-lived token.

Why
The extension is an untrusted client, so it holds no model key, no service credential and no password prompt. It opens the web app’s connect page, which signs the user in or reuses their session, gets a token from the API and sends it over. The token is user-scoped, limited to the extension’s scopes and lasts an hour by default.
Cost
Connecting is a trip through a browser tab, and returning the user to their page with the popup reopened depends on Chrome’s APIs, so it’s best effort. Every capture is a server round trip.

Decision

Collect with rules, not AI, and score every change against frozen answers.

Instead of
A model reading the page, as in the public extension.
Why
Reviewers approve what the tool finds, so every item records where it came from, and where sources disagree the panel shows both values. Rules can be rerun on saved pages, and a Playwright harness loads the built extension into Chromium, collects on each live example page, scores it against ground truth frozen on a set date and reports site drift apart from regressions. A false “complete” fails the run.
Cost
Coverage grows one page pattern at a time, each needing a generic rule, a saved page and a test, and the harness is a second codebase. Live runs still need judgment: when old and new builds ran side by side on 200 pages, every difference turned out to be a site change or a bot block, which took reruns to confirm.

Decision

Hand off through a versioned clipboard format, and ship the reader first.

Why
Neither extension writes into the application. A schedule recognizes AceClipper’s clip on paste. The internal extension has no server and sends nothing; the review platform’s paste dialog matches the clip against what the product already has and saves nothing until the reviewer clicks Add. The documents format is an identical copy in both codebases, with golden tests, and the reader went live before the release that writes it.
Cost
What the format can’t carry doesn’t arrive. A swatch picture read from a PDF has no web address, so it pastes as a name and code. Color-only swatches were refused until I made the review platform draw them as images, and unnamed ones were dropped until the dialog asked for a name.

What I’d change

The model failed quietly. A reported page with 16 color variants came back empty: the model listed each variant as a product, hit the output limit, and the endpoint returned an empty list. I changed the prompt to treat schema.org JSON-LD as strong evidence and fold variants into one product, raised the limit, and made a truncated answer return a clear error. I’d give the LLM path a frozen test set from day one.

Outcome

Architects can paste products from manufacturer pages into schedules with AceClipper, public on the Chrome Web Store.

For the internal extension, the number that matters is recall: of the files reviewers ended up approving, how many the tool had already found. Replayed offline against reviewers’ decisions on 23 products, it found 117 of 164 (71%), up 14 points from 94 (57%) before a round of fixes. Higher recall means more of the files reviewers keep are already collected when they open a product; the replay measured recall, not reviewer time. Recall isn’t precision either: it doesn’t count the wrong files the tool also suggests, which is why reviewers still approve or reject every one. On a separate 11-page benchmark of the pages it was developed against, it found all 245 files those pages link to directly, against a 95% target, and suggested the right file type 96% of the time, against 90%.

What’s left out

Acelab’s code and data are private, so the diagrams are redrawn and any interactive demo is a rebuild with made-up data. Names of colleagues, customers and internal tools are left out on purpose. I’m glad to go deeper on any of it in an interview.