@useclickly/core
v1.3.0
Published
Framework-agnostic engine powering Clickly — selection state machine, overlay renderer, metadata collectors, and the AFS 1.1 annotation schema.
Maintainers
Readme
@useclickly/core
Framework-agnostic engine powering
@useclickly/react. Selection state machine, overlay renderer, metadata collectors, React Fiber walker, AFS 1.1 schema.
Most people want @useclickly/react — it bundles this engine with a complete UI. Install @useclickly/core directly if you want to:
- Build your own UI on top of the selection + overlay engine
- Consume the AFS 1.1 schema in non-React tooling
- Write a framework adapter (
@useclickly/vue,@useclickly/svelte, etc.)
Install
npm install @useclickly/coreWhat's inside
| Module | Exports |
|---|---|
| Selection engine | SelectionEngine, EngineState, EngineEvent, Selection, reduce, initialState, DRAG_THRESHOLD_PX |
| Geometry | intersects, contains, rectFromPoints, manhattan, area, fromDOMRect |
| Hit testing | pickElementAt, pickElementsInRect |
| Overlay | Overlay, OverlayRenderer, createShadowHost, emptyRenderState |
| Metadata | collectMetadata, buildSelector, collectComputedStyles, collectAccessibility, collectNearbyText, collectSelectedText, isUseableClass |
| React mapping | getFiber, getComponentChain, getComponentName, getSourceInfo |
| Schema | Annotation (AFS 1.1), AFS_VERSION |
Minimal usage
import {
SelectionEngine,
Overlay,
createShadowHost,
collectMetadata,
} from "@useclickly/core";
const shadow = createShadowHost();
const engine = new SelectionEngine({ document, host: shadow.host });
const overlay = new Overlay({
engine,
root: shadow.root,
excludeHost: shadow.host,
});
engine.activate();
engine.subscribe((state) => {
if (state.kind === "annotating") {
const sel = engine.resolveSelection();
if (sel?.kind === "single") {
const md = collectMetadata(sel.element, { detail: "detailed" });
console.log(md);
}
engine.commit();
}
});Architecture
- Pure-reducer state machine — testable without a DOM, exhaustively type-checked.
- Dependency-injected DOM access — pass
{ document, host, raf }so unit tests run in jsdom with synchronous fake schedulers. - External-store
Subscribable<T>shape — composes directly with React'suseSyncExternalStore(no adapter). - Imperative shadow-DOM overlay — no React reconciliation on the hot path.
- Event-driven repositioning — scroll + resize listeners trigger renders; no tail-recursive RAF loops.
See the per-phase design docs in the main repo.
Annotation Format Schema 1.1
Clickly's annotation shape is wire-compatible with AFS 1.1 and adds one non-breaking field: an optional screenshot.
import type { Annotation } from "@useclickly/core";
const a: Annotation = {
id: "ann_1",
comment: "Button looks off",
elementPath: "section.hero > button.cta",
element: "button",
timestamp: Date.now(),
x: 120,
y: 480,
// ...optional fields
};Requirements
- Browser environment (DOM required)
- Node ≥ 18.17 for typecheck/build
License
MIT
