{"_id":"@01.works/diff-layer","name":"@01.works/diff-layer","dist-tags":{"dev":"0.0.1-dev.0","latest":"0.0.1-dev.0"},"versions":{"0.0.1-dev.0":{"name":"@01.works/diff-layer","version":"0.0.1-dev.0","description":"Event-driven browser overlay for visualizing AI-authored UI changes.","type":"module","main":"dist/index.cjs","module":"dist/index.js","types":"dist/index.d.ts","exports":{"./package.json":"./package.json",".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}},"./client":{"import":{"types":"./dist/client.d.ts","default":"./dist/client.js"},"require":{"types":"./dist/client.d.cts","default":"./dist/client.cjs"}}},"scripts":{"build":"rm -rf dist && NODE_ENV=production tsup","dev":"tsup --watch","pack:local":"mkdir -p ../../.context && pnpm build && pnpm pack --pack-destination ../../.context","prepublishOnly":"pnpm test && pnpm typecheck && pnpm test:packaged","publish:dev":"npm publish --tag dev --access public","test":"vitest run --environment happy-dom","test:packaged":"pnpm build && node scripts/smoke-package.mjs","typecheck":"tsc --noEmit","version:dev":"npm version prerelease --preid dev --no-git-tag-version","lint":"tsc --noEmit","format":"tsc --noEmit"},"keywords":["diff-layer","ai","overlay","change-visualization"],"author":{"name":"01.works"},"license":"MIT","homepage":"https://github.com/01-office/diff-layer#readme","bugs":{"url":"https://github.com/01-office/diff-layer/issues"},"repository":{"type":"git","url":"git+https://github.com/01-office/diff-layer.git","directory":"packages/ai-change-overlay"},"devDependencies":{"happy-dom":"^20.0.10","tsup":"^8.5.1","typescript":"latest","vitest":"^3.0.0"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/","tag":"dev"},"_id":"@01.works/diff-layer@0.0.1-dev.0","_nodeVersion":"24.15.0","_npmVersion":"11.12.1","dist":{"integrity":"sha512-ovHwZ7tiWZyTmRv9WwlyJWWt3TS5guuFwU2ls+UCJDfKk6UY8xgCAc9ph8osNmJ2dYnxBZZcZtCytondzH5IPg==","shasum":"47976734e5dd479d9fc06afa956e2c09a9f217e6","tarball":"https://registry.npmjs.org/@01.works/diff-layer/-/diff-layer-0.0.1-dev.0.tgz","fileCount":13,"unpackedSize":131568,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIFGFKlBuK0w2e0pitRYPgPEOY2zucSebMI43w9pNmoOzAiBxnqRpsnGzVBhUI96uupN5bMjf40nN7xHyY0QGLxEaSg=="}]},"_npmUser":{"name":"kdnhyk","email":"kdnhyk@gmail.com"},"directories":{},"maintainers":[{"name":"kdnhyk","email":"kdnhyk@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/diff-layer_0.0.1-dev.0_1779949695200_0.27453031418074625"},"_hasShrinkwrap":false}},"time":{"created":"2026-05-28T06:28:14.920Z","0.0.1-dev.0":"2026-05-28T06:28:15.330Z","modified":"2026-05-28T06:28:15.529Z"},"maintainers":[{"name":"kdnhyk","email":"kdnhyk@gmail.com"}],"description":"Event-driven browser overlay for visualizing AI-authored UI changes.","homepage":"https://github.com/01-office/diff-layer#readme","keywords":["diff-layer","ai","overlay","change-visualization"],"repository":{"type":"git","url":"git+https://github.com/01-office/diff-layer.git","directory":"packages/ai-change-overlay"},"author":{"name":"01.works"},"bugs":{"url":"https://github.com/01-office/diff-layer/issues"},"license":"MIT","readme":"# @01.works/diff-layer\n\nEvent-driven browser overlay for visualizing UI changes authored by AI agents.\n\nThis package does not track React renders. It renders explicit change events\nprovided by an app, AI tool, browser extension, or test harness.\n\nResolved changes render as persistent target outlines with compact `AI` markers.\nClick a marker or timeline item to inspect one selected change in the timeline\npanel. This keeps multiple nearby changes visible without stacking detail\ncards over the app.\n\n## Quick Start In Your App\n\nInstall the dogfood channel:\n\n```bash\npnpm add @01.works/diff-layer@dev\n```\n\n### 1. Install the overlay in development\n\n```ts\nimport { installAIChangeOverlay } from '@01.works/diff-layer/client';\n\ninstallAIChangeOverlay({\n  mutationRetryMs: 1000,\n});\n```\n\nWithout an explicit `enabled` option, the overlay installs only when runtime\n`NODE_ENV` is exactly `development`. Browser runtimes with no detectable\n`NODE_ENV` default to disabled. For those bundlers, pass the bundler's own\ndevelopment flag, such as `enabled: import.meta.env.DEV` in Vite.\n\n### 2. Mark the UI you want to review\n\n```tsx\nexport function BillingSummary() {\n  return (\n    <section data-ai-id=\"billing-summary\">\n      <h2 data-ai-id=\"billing-title\">Current plan</h2>\n      <button data-ai-id=\"upgrade-cta\">Upgrade</button>\n    </section>\n  );\n}\n```\n\nStable `data-ai-id` hooks make explicit AI change events easy to map back to\nvisible UI without depending on brittle generated class names.\n\n### 3. Push a change from an AI tool, extension, or test harness\n\n```ts\nwindow.__AI_CHANGE_OVERLAY__?.push({\n  id: crypto.randomUUID(),\n  source: 'ai',\n  label: 'Upgrade CTA copy changed',\n  selector: \"[data-ai-id='upgrade-cta']\",\n  before: 'Upgrade',\n  after: 'Compare plans',\n  reason: 'Clarified that the next step shows plan options before purchase.',\n  filePath: 'app/billing/page.tsx',\n  line: 38,\n  timestamp: Date.now(),\n});\n```\n\nThe event is explicit: the overlay shows what changed, where it changed, why it\nchanged, and which source location is associated with the update.\n\n```ts\nwindow.dispatchEvent(\n  new CustomEvent('ai-change-overlay:push', {\n    detail: {\n      id: crypto.randomUUID(),\n      source: 'ai',\n      label: 'Billing summary risk note added',\n      selector: \"[data-ai-id='billing-summary']\",\n      reason: 'Called out a plan limit that reviewers should verify.',\n      filePath: 'app/billing/page.tsx',\n      line: 52,\n      timestamp: Date.now(),\n    },\n  }),\n);\n```\n\nUse this path when the producer cannot import the package directly.\n\n## Public API\n\nRoot export:\n\n```ts\nimport {\n  CHANGE_OVERLAY_EVENT_NAME,\n  CHANGE_OVERLAY_GLOBAL_NAME,\n  getAIChangeOverlay,\n  installAIChangeOverlay,\n  registerChangeTarget,\n  unregisterChangeTarget,\n  type AIChangeOverlayGlobal,\n  type ChangeEvent,\n  type ChangeOverlayOptions,\n  type ChangeSource,\n  type CreateChangeEventInput,\n  type PushChangeInput,\n  type SourceLocation,\n} from '@01.works/diff-layer';\n```\n\nClient export:\n\n```ts\nimport {\n  getAIChangeOverlay,\n  installAIChangeOverlay,\n} from '@01.works/diff-layer/client';\n```\n\n## Options\n\n| Option | Default | Description |\n| --- | --- | --- |\n| `enabled` | `NODE_ENV === 'development'` | Enables the overlay client. |\n| `maxHistory` | `20` | Maximum changes kept in the timeline. |\n| `mutationRetryMs` | `0` | How long to watch for targets that render after the event. |\n| `root` | `document.body` | DOM node that receives overlay and timeline elements. |\n| `onOpenSource` | `undefined` | Callback for the timeline source action. |\n\nChange outlines and markers stay visible until the reviewer clears active\noverlays from the timeline or destroys the client. The selected change remains\navailable in the timeline inspector while it is still in history. This package\nis a review aid for explicit AI-authored changes, not a transient render flash.\n\n## Change Event\n\n```ts\ntype ChangeEvent = {\n  id: string;\n  source: 'ai' | 'manual' | 'system';\n  label: string;\n  selector?: string;\n  componentName?: string;\n  filePath?: string;\n  line?: number;\n  before?: string;\n  after?: string;\n  reason?: string;\n  timestamp: number;\n};\n```\n\nMalformed global or event payloads are ignored at runtime.\n\n## CustomEvent API\n\nNon-importing producers can dispatch the event name directly:\n\n```ts\nwindow.dispatchEvent(\n  new CustomEvent('ai-change-overlay:push', {\n    detail: {\n      id: crypto.randomUUID(),\n      source: 'ai',\n      label: 'Pricing card layout changed',\n      selector: \"[data-ai-id='pricing-card']\",\n      reason: 'Improved scanability of the primary plan',\n      timestamp: Date.now(),\n    },\n  }),\n);\n```\n\n## Component Targets\n\nUse component names when a component boundary is easier to identify than a\nselector:\n\n```ts\nimport { registerChangeTarget } from '@01.works/diff-layer';\n\nregisterChangeTarget('BillingSummary', \"[data-ai-id='billing-summary']\");\n```\n\nRemove registrations when the target is no longer valid:\n\n```ts\nimport { unregisterChangeTarget } from '@01.works/diff-layer';\n\nunregisterChangeTarget('BillingSummary');\n```\n\n## Queue Before Install\n\nEvents can be queued before installation by assigning the global to an array:\n\n```ts\nwindow.__AI_CHANGE_OVERLAY__ = window.__AI_CHANGE_OVERLAY__ ?? [];\n\nif (Array.isArray(window.__AI_CHANGE_OVERLAY__)) {\n  window.__AI_CHANGE_OVERLAY__.push({\n    id: crypto.randomUUID(),\n    source: 'ai',\n    label: 'Billing title changed',\n    selector: \"[data-ai-id='billing-title']\",\n    before: 'Plan',\n    after: 'Current plan',\n    timestamp: Date.now(),\n  });\n}\n```\n\n## Late Targets\n\nEnable `mutationRetryMs` when events can arrive before the target is mounted:\n\n```ts\ninstallAIChangeOverlay({\n  enabled: true,\n  mutationRetryMs: 1000,\n});\n```\n\n## Source Hook\n\nClicking a rendered source action copies `filePath` or `filePath:line` to the\nclipboard by default. Provide `onOpenSource` when you also want the source\naction to open a trusted editor link.\n\nKeep editor opening enabled only in development and only for trusted\nworkspace-relative paths. Only a trusted local bridge that validates workspace\ncontainment should handle absolute paths.\n\n```ts\ninstallAIChangeOverlay({\n  onOpenSource: (change) => {\n    if (change.filePath === undefined) return;\n\n    const workspaceRoot = '/absolute/path/to/your/project';\n    const relativePath = change.filePath.replaceAll('\\\\', '/');\n\n    if (\n      relativePath.startsWith('/') ||\n      relativePath.split('/').includes('..') ||\n      /^[a-z][a-z0-9+.-]*:/i.test(relativePath)\n    ) {\n      return;\n    }\n\n    const absolutePath = `${workspaceRoot}/${relativePath}`;\n    const suffix = change.line === undefined ? '' : `:${change.line}`;\n\n    window.location.href = `vscode://file/${encodeURI(absolutePath)}${suffix}`;\n  },\n});\n```\n\nThe callback runs after the copy attempt. Clipboard failures are ignored so a\ntrusted editor-opening callback can still run.\n\n## Verification\n\n```bash\npnpm --filter @01.works/diff-layer test\npnpm --filter @01.works/diff-layer typecheck\npnpm --filter @01.works/diff-layer build\npnpm --filter @01.works/diff-layer test:packaged\n```\n\n## Dev-Tag Publishing\n\nThis package is configured for dogfood releases on npm's `dev` dist-tag:\n\n```bash\npnpm version:dev\npnpm publish:dev\n```\n\nEvery npm publish still needs a unique version. Consumers install the dogfood\nchannel explicitly:\n\n```bash\npnpm add @01.works/diff-layer@dev\n```\n\nDo not publish dogfood builds with the `latest` dist-tag.\n","readmeFilename":"README.md","_rev":"1-8fb307be57fb683f8be53e54c09be6e3"}