@ewjdev/anyclick-core

Core Library

Framework-agnostic core library for UI feedback capture. Provides DOM utilities, payload building, screenshot capture, and the adapter interface.

Installation

npm install @ewjdev/anyclick-core

Note: If you're using @ewjdev/anyclick-react, the core library is included as a dependency.

Main Exports

FeedbackClient

The main client class that handles context menu events, DOM capture, and feedback submission.

import { createFeedbackClient } from '@ewjdev/anyclick-core';
const client = createFeedbackClient({
adapter: myAdapter,
targetFilter: (event, target) => true, // Optional filter
maxInnerTextLength: 500,
maxOuterHTMLLength: 2000,
maxAncestors: 5,
cooldownMs: 1000,
});
// Attach to DOM (starts listening for context menu events)
client.attach();
// Submit feedback programmatically
await client.submitFeedback(element, 'issue', {
comment: 'This button is broken',
metadata: { userId: '123' },
});
// Detach from DOM
client.detach();

DOM Utilities

Helper functions for extracting information from DOM elements.

import {
getUniqueSelector,
getAncestors,
getDataAttributes,
buildElementContext,
} from '@ewjdev/anyclick-core';
// Get a unique CSS selector for an element
const selector = getUniqueSelector(element);
// => "button.submit-btn[data-testid='submit']"
// Get ancestor elements with context
const ancestors = getAncestors(element, { maxAncestors: 5 });
// Get all data-* attributes
const dataAttrs = getDataAttributes(element);
// => { testid: 'submit', action: 'save' }
// Build full element context
const context = buildElementContext(element, {
maxInnerTextLength: 500,
maxOuterHTMLLength: 2000,
});

Screenshot Utilities

Functions for capturing screenshots of elements and the page.

import {
captureScreenshot,
captureAllScreenshots,
isScreenshotSupported,
DEFAULT_SCREENSHOT_CONFIG,
} from '@ewjdev/anyclick-core';
// Check if screenshots are supported
if (isScreenshotSupported()) {
// Capture a single element
const screenshot = await captureScreenshot(element, {
quality: 0.9,
pixelRatio: 2,
format: 'png',
});
// Capture target, container, and full page
const screenshots = await captureAllScreenshots(
targetElement,
containerElement,
{ ...DEFAULT_SCREENSHOT_CONFIG }
);
}

Payload Building

Build the full feedback payload with page and element context.

import { buildFeedbackPayload, buildPageContext } from '@ewjdev/anyclick-core';
// Build page context (URL, viewport, etc.)
const pageContext = buildPageContext();
// Build complete feedback payload
const payload = buildFeedbackPayload({
element,
type: 'issue',
comment: 'Something is wrong here',
metadata: { userId: '123' },
screenshots: screenshotData,
});

Types

FeedbackPayload

The main payload structure sent to adapters.

typeFeedbackType

The feedback type (e.g., 'issue', 'feature', 'like')

timestampstring

ISO timestamp of when feedback was captured

pagePageContext

Page-level context (URL, viewport, etc.)

elementElementContext

Target element context (selector, text, etc.)

commentstring | undefined

Optional user comment

metadataRecord<string, unknown>

Custom metadata from the provider

screenshotsScreenshotData | undefined

Captured screenshots

ElementContext

Information captured about the target element.

selectorstring

Unique CSS selector for the element

tagNamestring

The element's tag name (lowercase)

innerTextstring

Truncated inner text content

outerHTMLstring

Truncated and sanitized outer HTML

dataAttributesRecord<string, string>

All data-* attributes

ancestorsAncestorInfo[]

Array of ancestor element info

boundingRectDOMRect

Element's bounding rectangle

FeedbackAdapter

Interface that adapters must implement.

interface FeedbackAdapter {
submit(payload: FeedbackPayload): Promise<FeedbackResult>;
}
interface FeedbackResult {
success: boolean;
id?: string; // Created resource ID (e.g., issue number)
url?: string; // URL to the created resource
error?: string; // Error message if failed
}

Screenshot Configuration

ScreenshotConfig

enabledboolean

Whether screenshot capture is enabled

qualitynumber

JPEG quality (0-1), default 0.9

pixelRationumber

Device pixel ratio, default 2

format'png' | 'jpeg'

Image format, default 'png'

maxWidthnumber

Maximum width in pixels

maxHeightnumber

Maximum height in pixels

sensitiveSelectorsstring[]

Selectors to blur/hide in screenshots

Building Custom Adapters

You can build custom adapters by implementing the FeedbackAdapter interface:

my-custom-adapter.ts
import type { FeedbackAdapter, FeedbackPayload, FeedbackResult } from '@ewjdev/anyclick-core';
export function createCustomAdapter(config: MyConfig): FeedbackAdapter {
return {
async submit(payload: FeedbackPayload): Promise<FeedbackResult> {
try {
// Your custom logic here
const response = await fetch(config.endpoint, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
});
const data = await response.json();
return {
success: true,
id: data.id,
url: data.url,
};
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error',
};
}
},
};
}

Related