@ewjdev/anyclick-coreCore 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-coreNote: 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 programmaticallyawait client.submitFeedback(element, 'issue', { comment: 'This button is broken', metadata: { userId: '123' },});
// Detach from DOMclient.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 elementconst selector = getUniqueSelector(element);// => "button.submit-btn[data-testid='submit']"
// Get ancestor elements with contextconst ancestors = getAncestors(element, { maxAncestors: 5 });
// Get all data-* attributesconst dataAttrs = getDataAttributes(element);// => { testid: 'submit', action: 'save' }
// Build full element contextconst 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 supportedif (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 payloadconst payload = buildFeedbackPayload({ element, type: 'issue', comment: 'Something is wrong here', metadata: { userId: '123' }, screenshots: screenshotData,});Types
FeedbackPayload
The main payload structure sent to adapters.
typeFeedbackTypeThe feedback type (e.g., 'issue', 'feature', 'like')
timestampstringISO timestamp of when feedback was captured
pagePageContextPage-level context (URL, viewport, etc.)
elementElementContextTarget element context (selector, text, etc.)
commentstring | undefinedOptional user comment
metadataRecord<string, unknown>Custom metadata from the provider
screenshotsScreenshotData | undefinedCaptured screenshots
ElementContext
Information captured about the target element.
selectorstringUnique CSS selector for the element
tagNamestringThe element's tag name (lowercase)
innerTextstringTruncated inner text content
outerHTMLstringTruncated and sanitized outer HTML
dataAttributesRecord<string, string>All data-* attributes
ancestorsAncestorInfo[]Array of ancestor element info
boundingRectDOMRectElement'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
enabledbooleanWhether screenshot capture is enabled
qualitynumberJPEG quality (0-1), default 0.9
pixelRationumberDevice pixel ratio, default 2
format'png' | 'jpeg'Image format, default 'png'
maxWidthnumberMaximum width in pixels
maxHeightnumberMaximum height in pixels
sensitiveSelectorsstring[]Selectors to blur/hide in screenshots
Building Custom Adapters
You can build custom adapters by implementing the FeedbackAdapter interface:
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', }; } }, };}