context-attachment-manager
Codex and Claude Code plugin (works in Claude Desktop): choose which past images go to the model with your next message. Unchecked images become placeholders, the conversation goes on, and requests st
A multi-browser extension for capturing and inspecting every push to window.adobeDataLayer with full state snapshots.
PluginsJavaScript · CSSGitHub @andylunsfordOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
The maker's introduction consists of the line above. Read the repository README for more details. Repository README ↗
Loading the full README.
Read the full README ↗A browser extension for Chrome, Firefox and Safari that passively captures every push to window.adobeDataLayer, deep-clones the full getState() snapshot per push, surfaces the timeline in a panel (Chrome Side Panel, Firefox Sidebar, Safari toolbar popup), and exports it as a self-describing JSON file for post-recording debugging.
Built for debugging ACDL-instrumented sites.
adobeDataLayer.push(...) with a deep-cloned state snapshot per eventscope: 'all')storage.session.nvmrc)Each release attaches one zip per browser: acdl-debugger-vX.Y.Z-chrome.zip, -firefox.zip and -safari.zip. Releases also go to the Chrome Web Store and Firefox Add-ons automatically, and appear there once each store's review approves them, so the store links above are the easiest way to install and stay up to date. The zips are for loading the extension unpacked, for testing or where the stores aren't an option.
-chrome.zip and unzip itchrome://extensions and enable Developer mode (top-right toggle)To update, replace that folder's contents with the newer release and click the reload icon on the extension's card.
.xpi.about:debugging#/runtime/this-firefox, click Load Temporary Add-on, and pick the -firefox.zip (or dist/firefox/manifest.json). Temporary add-ons are removed when Firefox restarts.Safari only runs web extensions that ship inside a native macOS app, so the zip is the input to Apple's converter, not something you install directly. On a Mac with Xcode:
unzip acdl-debugger-vX.Y.Z-safari.zip -d acdl-safari # or use dist/safari from a local build
xcrun safari-web-extension-converter acdl-safari --macos-only --app-name "ACDL Debugger" --bundle-identifier com.example.acdl-debugger
Build and run the generated Xcode project, then enable the extension in Safari → Settings → Extensions. To load an unsigned build, first turn on Develop → Developer Settings → Allow unsigned extensions (resets each time Safari quits). Grant the extension access to the sites you debug when Safari asks. Distributing to other Macs requires an Apple Developer account (App Store or notarized app).
Safari has no side panel or sidebar API, so the panel opens as a toolbar popup and closes when you click back into the page. Capture keeps running in the background; reopen the popup to see new events. Safari has no downloads API, so Export saves through a regular download link.
Click the gear button in the panel header to open Settings.
Data layer names: the extension always watches window.adobeDataLayer. If a site exposes its data layer under another name, add that name here (comma-separated, e.g. myDataLayer). This covers a self-hosted or modified ACDL build, or a page that pushes into its own custom-named array. Adobe's Client Data Layer Tags extension can also rename the data layer, but it still creates adobeDataLayer, so those sites are usually captured without any setting. Adding the custom name there doesn't hurt: if two names point at the same data layer, each event is captured only once.
_ and $), up to 10 extra names.#2 · 10:14:03.512 · myDataLayer), and every exported event includes its dataLayer name.npm install
npm run icons # regenerate icons from assets/icon-source.png (already committed)
npm run build # produces dist/chrome, dist/firefox, dist/safari
Then load it into Chrome:
chrome://extensionsdist/chrome directoryFor Firefox and Safari, use dist/firefox / dist/safari as described above.
The panel always reflects the active tab. Switching tabs re-subscribes automatically.
window.adobeDataLayer; events appear in the panel.https://example.com/; the panel shows a "not detected" banner.myDataLayer, nothing appears until you add myDataLayer under Settings; then its events appear without reloading.for (let i = 0; i < 6000; i++) adobeDataLayer.push({ event: 'load.test', n: i }) — the store FIFO-trims at 5000.All non-trivial logic lives in src/lib/ as pure modules (no extension APIs) so it can be unit-tested in Node. The four entry files — inject.js, content.js, background.js, sidepanel/sidepanel.js — are the only places that touch extension APIs / window.*. They use ext from src/ext.js (browser in Firefox/Safari, chrome in Chrome) rather than chrome.* directly.
manifest.json is the Chrome manifest; src/lib/target-manifest.js derives the Firefox and Safari manifests from it at build time.
npm install # install dev dependencies (esbuild, vitest, jsdom, sharp)
npm test # run unit tests (103 tests: 11 lib modules + the store scripts)
npm run test:watch # re-run tests on change
npm run test:e2e # build, then run real-browser tests in headless Chromium + Firefox
npm run build # one-off bundle into dist/<chrome|firefox|safari>
npm run build -- --target=firefox # build a single target
npm run build:watch # rebuild dist/chrome on change (add -- --target=firefox for Firefox)
npm run icons # regenerate icons/ from assets/icon-source.png
Conventions:
tests/ before adding behavior to a src/lib/ module.inject.js must be self-contained — it runs in the page MAIN world and cannot import. esbuild emits IIFE bundles targeting chrome120 / firefox142 / safari17.After editing, run npm run build and reload the extension (chrome://extensions, or Reload in about:debugging for Firefox).
CI (.github/workflows/ci.yml) runs the tests, builds all three targets, runs Mozilla's web-ext lint (the AMO validator) on the Firefox build, and runs the e2e suite in Chromium and Firefox on every push to main and every pull request.
E2E tests (tests/e2e/) load the built extension into headless Chromium (Playwright) and Firefox (puppeteer-core over WebDriver BiDi), serve a local page running the real ACDL library, and check that captures, state snapshots, navigation, strict-CSP pages, the panel's event list and detail view, Clear, and renamed data layers all work. Chromium comes from npx playwright install chromium. Firefox must be installed on the machine; set FIREFOX_BIN if it isn't on PATH. Safari isn't covered: it only runs on macOS.
Page MAIN world Isolated world Extension
───────────────── ────────────── ─────────
inject.js ──▶ content.js ──▶ background.js ◀──▶ sidepanel/
(hooks ACDL, (bridge: (service worker: (UI: list,
deep-clones state, window.postMessage per-tab store + detail,
posts events) → chrome.runtime) session storage Clear/Export)
persistence)
ACDL lives on window and is invisible to isolated-world content scripts, so a page-world script (inject.js) hooks it via its documented addEventListener API. The isolated-world content.js bridges page messages to chrome.runtime. The service worker owns the canonical Map<tabId, capture> and persists to chrome.storage.session to survive MV3 eviction.
See CLAUDE.md for the full architecture notes, file layout, and known gotchas.
manifest.json # MV3 manifest for Chrome (Firefox/Safari variants derived at build)
build.mjs # esbuild bundle script
vitest.config.js # unit test config (vitest.e2e.config.js for e2e)
.github/workflows/ # CI (test + build) and tag-driven releases
scripts/ # icon generation, release version checks, store publishing helpers
assets/icon-source.png # source artwork (not shipped in dist/)
src/
inject.js # page-world entry (self-contained IIFE)
content.js # isolated-world bridge
background.js # service worker (event page in Firefox)
ext.js # browser/chrome namespace shim
lib/ # pure, unit-tested modules
sidepanel/ # panel UI (html/js/css): side panel / sidebar / popup
tests/ # vitest, one file per lib module; e2e/ = real-browser tests
icons/ # generated by `npm run icons` (committed)
dist/<target>/ # build output per browser — load unpacked (git-ignored)
Copyright 2026 Andy Lunsford. Licensed under the Apache License, Version 2.0.