# UI Context Capture

[简体中文](./README.zh-CN.md)

`ui-context-capture` is a framework-neutral Vite development plugin for collecting UI context directly from the page. Select an element to inspect its DOM details, framework component hierarchy, source location, and matching CSS rules without repeatedly tracing it through DevTools.

The plugin is active only while the Vite development server is running. It does not inject its client runtime into production builds.

## Features

- Toggleable floating trigger and keyboard shortcut
- Element highlighting on hover
- Compact element signatures built from the tag, ID, and up to two classes
- Optional framework integrations for component names, stacks, and source locations
- DOM metadata, selector paths, HTML snippets, and matching CSS rules in the console
- Compact context popover with notes and clipboard support
- Optional on-demand element screenshots saved under `.ucc/screenshots`
- Separate Vue 3 and Vue 2.7 integrations with optional external source metadata
- Framework-neutral core with a public adapter/integration protocol
- Vite 4, 5, 6, 7, and 8 support

## Installation

```bash
pnpm add -D ui-context-capture
```

Install `vite-plugin-vue-inspector` separately when using the precise Vue source-location setup shown below:

```bash
pnpm add -D vite-plugin-vue-inspector
```

You can also install it with npm or Yarn:

```bash
npm install -D ui-context-capture
yarn add -D ui-context-capture
```

## Usage

Add the plugin to your Vite configuration:

```ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import Inspector from 'vite-plugin-vue-inspector'
import { uiContextCapture } from 'ui-context-capture/vite'
import { vueIntegration } from 'ui-context-capture/vue'

export default defineConfig({
  plugins: [
    vue(),
    Inspector({
      enabled: false,
      toggleButtonVisibility: 'never',
      toggleComboKey: false,
      cleanHtml: true,
    }),
    uiContextCapture({
      integrations: [vueIntegration()],
    }),
  ],
})
```

### Vue 2.7

Vue 2.7 applications use the dedicated integration and Vite's Vue 2 plugin:

```bash
pnpm add -D @vitejs/plugin-vue2
```

```ts
import vue2 from '@vitejs/plugin-vue2'
import Inspector from 'vite-plugin-vue-inspector'
import { defineConfig } from 'vite'
import { uiContextCapture } from 'ui-context-capture/vite'
import { vue2Integration } from 'ui-context-capture/vue2'

export default defineConfig({
  plugins: [
    vue2(),
    Inspector({
      vue: 2,
      enabled: false,
      toggleButtonVisibility: 'never',
      toggleComboKey: false,
      cleanHtml: false,
    }),
    uiContextCapture({
      integrations: [vue2Integration()],
    }),
  ],
})
```

The Inspector plugin is optional. Without it, the Vue 2.7 adapter still resolves component names, files, and the `$parent` stack from runtime metadata; precise element line and column numbers require the visible `data-v-inspector` attribute.

Start the development server, then use the floating button in the lower-right corner to enable selection mode. Hover an element to highlight it and click it to collect its context. While selection mode is active, the plugin suppresses native pointer, mouse, and click behavior on inspected page elements. Capture listeners that run earlier on `window` or `document` cannot be retroactively blocked.

Keyboard shortcuts:

- `Shift + C`: toggle selection mode
- `Escape`: exit selection mode

After selecting an element, the highlight and popover follow it as the page scrolls or changes layout. Close the popover to exit selection mode, or click **Copy** to copy the context and exit.

When screenshots are enabled, click **Capture** after selecting an element. The PNG is saved by the Vite development server under `.ucc/screenshots` and shown as a thumbnail in the popover; **Retake** updates the preview and path for the current selection, while **Remove** excludes it from the copied prompt. Screenshots are never captured automatically. Consider adding `.ucc/` to `.gitignore`.

Clicking **Copy** produces a compact text summary:

```text
You are a senior frontend engineer. Use the UI context below to implement the requested change. Treat the `request` field as the user's primary instruction. Preserve unrelated behavior. If information is insufficient, explain what is missing.

element: article#featured-card.card.card-featured
componentName: ExampleCard
componentStack: ExampleCard > HomePage > App
sourceLocation: src/components/ExampleCard.vue:12:5
screenshotPath: .ucc/screenshots/example-card-20260711-143052-a3f9.png
request: Adjust the spacing on mobile
```

Empty built-in context fields are omitted. Configured custom attributes that exist on the element are retained even when their values are empty. The browser console receives the full context, including DOM paths, an HTML snippet, and matching CSS rules.

The element signature is limited to 80 characters and includes at most the first two classes. It is intended as a compact human-readable identifier rather than a reusable CSS selector; the full collected class list remains available in the console context.

## Options

```ts
uiContextCapture({
  enabled: true,
  integrations: [vueIntegration()],
  contextPrompt: 'You are a senior frontend engineer. Use the UI context below to implement the requested change. Treat the `request` field as the user\'s primary instruction. Preserve unrelated behavior. If information is insufficient, explain what is missing.',
  customAttributes: ['data-scope', 'aa-bb'],
  shortcuts: {
    toggleKey: 'c',
    exitKey: 'Escape',
  },
  initialActive: false,
  trigger: {
    visible: true,
    position: 'bottom-right',
    offset: 16,
    size: 40,
  },
  popover: {
    width: 320,
  },
  screenshot: {
    enabled: false,
    cacheDir: '.ucc/screenshots',
    maxSizeMb: 10,
    retention: 50,
    backgroundColor: '#ffffff',
    // token: 'local-dev-token',
  },
  appearance: {
    theme: 'dark',
    accentColor: '#22c55e',
  },
  consoleLog: true,
})
```

| Option | Type | Default | Description |
| --- | --- | --- | --- |
| `enabled` | `boolean` | `true` | Inject the client runtime during Vite development. |
| `integrations` | `UiContextCaptureIntegration[]` | `[]` | Explicit framework integrations, evaluated in configuration order. |
| `contextPrompt` | `string` | See above | Prefix the clipboard text. A non-empty value replaces the default prompt; set it to `''` to omit the prompt. |
| `customAttributes` | `string[]` | `[]` | Include matching element attributes in the popover, clipboard text, and console context. |
| `shortcuts` | `ShortcutOptions` | `{ toggleKey: 'c', exitKey: 'Escape' }` | Replace or disable the toggle and exit keys. Toggle always uses Shift. |
| `initialActive` | `boolean` | `false` | Start in selection mode immediately after initialization. |
| `trigger` | `TriggerOptions` | See below | Configure floating trigger visibility, corner, offset, and size. |
| `popover` | `PopoverOptions` | `{ width: 320 }` | Configure the popover width in pixels. |
| `screenshot` | `ScreenshotOptions` | `{ enabled: false, cacheDir: '.ucc/screenshots', maxSizeMb: 10, retention: 50, backgroundColor: '#ffffff' }` | Configure manual PNG capture, canvas background, and oldest-file cleanup. Use `transparent` to preserve PNG transparency. |
| `screenshot.token` | `string` | disabled | Optional fixed token for screenshot uploads. When omitted, screenshot uploads do not require a token. |
| `appearance` | `AppearanceOptions` | `{ theme: 'dark', accentColor: '#22c55e' }` | Configure theme and shared accent color. |
| `consoleLog` | `boolean` | `true` | Log successfully collected context to the browser console. Errors are still logged. |

Trigger positions are `top-left`, `top-right`, `bottom-left`, and `bottom-right`. The trigger docks to the corresponding left or right viewport edge; `offset.y` controls its distance from the top or bottom, while the horizontal offset is ignored. `size` is the expanded hover, focus, and active size and is constrained to 32–64px. The idle trigger is 70% of that size, with a 24px minimum. Popover width is constrained to 240–480px and offsets to 0–256px.

Set either shortcut key to `false` to disable it. Themes are `dark`, `light`, and `auto`; `auto` follows live `prefers-color-scheme` changes. The accent color applies to the active trigger, highlight, field labels, Copy button, and focus ring.
The client automatically chooses black or white text for accent-colored controls. If the requested accent has insufficient contrast against a light or dark theme surface, that theme uses a visible default accent.

The core does not auto-detect or load a framework. Use `vueIntegration()` for Vue 3 and `vue2Integration()` for Vue 2.7. Each integration reads its framework's runtime metadata but does not install or configure a source-inspection plugin.

For precise element line and column numbers, install and register `vite-plugin-vue-inspector` separately as shown above. The Vue adapter supports both visible `data-v-inspector` attributes and the hidden VNode metadata produced by `cleanHtml: true`. Without build-time source metadata, Vue runtime information generally provides the component file but not an element's exact template line.

Integration names must be unique. Runtime adapters are evaluated in configuration order and the first adapter returning a non-null context wins. Adapter failures are logged and do not prevent framework-neutral DOM collection.

Custom attributes are matched by exact name and displayed in configuration order. Missing attributes are omitted, while attributes that exist with an empty value are retained.

When screenshots are enabled, `screenshot.token` can be set to require a fixed token on uploads. Empty or whitespace-only values disable token validation. The token is exposed to the development page, so use it as a local development access gate rather than a production secret.

`contextPrompt` is added only to the copied text; it is not displayed in the inspector or included in the browser console context. Set it to `''` (or whitespace only) to copy context without a prompt. The inspector's `request` input is copied as the primary user instruction for the model.

Screenshot saving is available only through the Vite plugin integration, not manual client initialization. The server accepts PNG uploads, optionally validates the configured token, generates filenames itself, restricts storage to the project root, and keeps only the configured number of newest screenshots. `screenshotPath` is project-relative so local coding agents can inspect it without exposing an absolute machine path.

## Manual initialization

The client entry is exported for custom injection scenarios:

```ts
import { init } from 'ui-context-capture'

init({
  customAttributes: ['data-scope', 'aa-bb'],
  shortcuts: { toggleKey: 'g', exitKey: 'Escape' },
  appearance: { theme: 'auto', accentColor: '#2563eb' },
})
```

Normal Vite integrations should use the plugin instead, because it handles development-only injection automatically.

To include Vue context during manual initialization:

```ts
import { init } from 'ui-context-capture'
import { createVueAdapter } from 'ui-context-capture/vue/runtime'

init({}, {
  adapters: [createVueAdapter()],
})
```

For Vue 2.7, import `createVue2Adapter` from `ui-context-capture/vue2/runtime` instead.

## Custom integrations

An integration connects build-time Vite plugins with a browser adapter module. The module named by `clientModule` must default-export a synchronous factory that returns a `ContextAdapter`. `clientOptions` must be JSON-serializable.

```ts
import type { ContextAdapter } from 'ui-context-capture'

export default function createAdapter(): ContextAdapter {
  return {
    name: 'my-framework',
    resolve(element) {
      return element.hasAttribute('data-component')
        ? {
            componentName: element.getAttribute('data-component'),
            componentStack: [],
            sourceStack: [],
          }
        : null
    },
  }
}
```

```ts
uiContextCapture({
  integrations: [{
    name: 'my-framework',
    clientModule: '/src/my-framework-adapter.ts',
  }],
})
```

## Requirements

- Vite `^4.0.0`, `^5.0.0`, `^6.0.0`, `^7.0.0`, or `^8.0.0`
- Vue is optional; Vue `^3.0.0` is supported by `vueIntegration()` and Vue `^2.7.0` by `vue2Integration()`
- `vite-plugin-vue-inspector` is not a runtime or peer dependency; applications may install and register it separately for precise Vue element locations

## Development

Repository setup, local examples, build commands, and release notes are documented in [Development Guide](./docs/development.md).

## License

[MIT](./LICENSE)
