@critique.work/agentation 2.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,27 @@
1
+ PolyForm Shield License 1.0.0
2
+
3
+ Copyright (c) 2026 Benji Taylor
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to use,
7
+ copy, modify, and distribute the Software, subject to the following conditions:
8
+
9
+ 1. You may not use the Software to provide a product or service that competes
10
+ with the Software or any product or service offered by the Licensor that
11
+ includes the Software.
12
+
13
+ 2. You may not remove or obscure any licensing, copyright, or other notices
14
+ included in the Software.
15
+
16
+ 3. If you distribute the Software or any derivative works, you must include a
17
+ copy of this license.
18
+
19
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25
+ SOFTWARE.
26
+
27
+ For more information, see https://polyformproject.org/licenses/shield/1.0.0
package/README.md ADDED
@@ -0,0 +1,135 @@
1
+ <img src="https://raw.githubusercontent.com/benjitaylor/agentation/main/package/logo.svg" alt="Agentation" width="50" />
2
+
3
+ [![npm version](https://img.shields.io/npm/v/agentation)](https://www.npmjs.com/package/agentation)
4
+ [![downloads](https://img.shields.io/npm/dm/agentation)](https://www.npmjs.com/package/agentation)
5
+
6
+ **[Agentation](https://agentation.dev)** is an agent-agnostic visual feedback tool. Click elements on your page, add notes, and copy structured output that helps AI coding agents find the exact code you're referring to.
7
+
8
+ ## Install
9
+
10
+ ```bash
11
+ npm install agentation -D
12
+ ```
13
+
14
+ ## Usage
15
+
16
+ ```tsx
17
+ import { Agentation } from 'agentation';
18
+
19
+ function App() {
20
+ return (
21
+ <>
22
+ <YourApp />
23
+ <Agentation />
24
+ </>
25
+ );
26
+ }
27
+ ```
28
+
29
+ The toolbar appears in the bottom-right corner. Click to activate, then click any element to annotate it.
30
+
31
+ ## Features
32
+
33
+ - **Click to annotate** – Click any element with automatic selector identification
34
+ - **Text selection** – Select text to annotate specific content
35
+ - **Multi-select** – Drag to select multiple elements at once
36
+ - **Area selection** – Drag to annotate any region, even empty space
37
+ - **Animation pause** – Freeze all animations (CSS, JS, videos) to capture specific states
38
+ - **Structured output** – Copy markdown with selectors, positions, and context
39
+ - **Programmatic access** – Callback prop for direct integration with tools
40
+ - **Dark/light mode** – Toggle in settings, persists to localStorage
41
+ - **Zero dependencies** – Pure CSS animations, no runtime libraries
42
+
43
+ ## Props
44
+
45
+ | Prop | Type | Default | Description |
46
+ |------|------|---------|-------------|
47
+ | `onAnnotationAdd` | `(annotation: Annotation) => void` | - | Called when an annotation is created |
48
+ | `onAnnotationDelete` | `(annotation: Annotation) => void` | - | Called when an annotation is deleted |
49
+ | `onAnnotationUpdate` | `(annotation: Annotation) => void` | - | Called when an annotation is edited |
50
+ | `onAnnotationsClear` | `(annotations: Annotation[]) => void` | - | Called when all annotations are cleared |
51
+ | `onCopy` | `(markdown: string) => void` | - | Callback with markdown output when copy is clicked |
52
+ | `onSubmit` | `(output: string, annotations: Annotation[]) => void` | - | Called when "Send Annotations" is clicked |
53
+ | `copyToClipboard` | `boolean` | `true` | Set to false to prevent writing to clipboard |
54
+ | `endpoint` | `string` | - | Server URL for Agent Sync (e.g., `"http://localhost:4747"`) |
55
+ | `sessionId` | `string` | - | Pre-existing session ID to join |
56
+ | `onSessionCreated` | `(sessionId: string) => void` | - | Called when a new session is created |
57
+ | `webhookUrl` | `string` | - | Webhook URL to receive annotation events |
58
+
59
+ ### Programmatic Integration
60
+
61
+ Use callbacks to receive annotation data directly:
62
+
63
+ ```tsx
64
+ import { Agentation, type Annotation } from 'agentation';
65
+
66
+ function App() {
67
+ const handleAnnotation = (annotation: Annotation) => {
68
+ // Structured data - no parsing needed
69
+ console.log(annotation.element); // "Button"
70
+ console.log(annotation.elementPath); // "body > div > button"
71
+ console.log(annotation.boundingBox); // { x, y, width, height }
72
+ console.log(annotation.cssClasses); // "btn btn-primary"
73
+
74
+ // Send to your agent, API, etc.
75
+ sendToAgent(annotation);
76
+ };
77
+
78
+ return (
79
+ <>
80
+ <YourApp />
81
+ <Agentation
82
+ onAnnotationAdd={handleAnnotation}
83
+ copyToClipboard={false} // Don't write to clipboard
84
+ />
85
+ </>
86
+ );
87
+ }
88
+ ```
89
+
90
+ ### Annotation Type
91
+
92
+ ```typescript
93
+ type Annotation = {
94
+ id: string;
95
+ x: number; // % of viewport width
96
+ y: number; // px from top of document (absolute) OR viewport (if isFixed)
97
+ comment: string; // User's note
98
+ element: string; // e.g., "Button"
99
+ elementPath: string; // e.g., "body > div > button"
100
+ timestamp: number;
101
+
102
+ // Optional metadata (when available)
103
+ selectedText?: string;
104
+ boundingBox?: { x: number; y: number; width: number; height: number };
105
+ nearbyText?: string;
106
+ cssClasses?: string;
107
+ nearbyElements?: string;
108
+ computedStyles?: string;
109
+ fullPath?: string;
110
+ accessibility?: string;
111
+ isMultiSelect?: boolean;
112
+ isFixed?: boolean;
113
+ };
114
+ ```
115
+
116
+ > **Note:** This is a simplified type. The full type includes additional fields for Agent Sync (`url`, `status`, `thread`, `reactComponents`, etc.). See [agentation.dev/schema](https://agentation.dev/schema) for the complete schema.
117
+
118
+ ## How it works
119
+
120
+ Agentation captures class names, selectors, and element positions so AI agents can `grep` for the exact code you're referring to. Instead of describing "the blue button in the sidebar," you give the agent `.sidebar > button.primary` and your feedback.
121
+
122
+ ## Requirements
123
+
124
+ - React 18+
125
+ - Desktop browser (mobile not supported)
126
+
127
+ ## Docs
128
+
129
+ Full documentation at [agentation.dev](https://agentation.dev)
130
+
131
+ ## License
132
+
133
+ © 2026 Benji Taylor
134
+
135
+ Licensed under PolyForm Shield 1.0.0
@@ -0,0 +1,289 @@
1
+ import * as react from 'react';
2
+ import * as react_jsx_runtime from 'react/jsx-runtime';
3
+
4
+ type Annotation = {
5
+ id: string;
6
+ x: number;
7
+ y: number;
8
+ comment: string;
9
+ element: string;
10
+ elementPath: string;
11
+ timestamp: number;
12
+ selectedText?: string;
13
+ boundingBox?: {
14
+ x: number;
15
+ y: number;
16
+ width: number;
17
+ height: number;
18
+ };
19
+ nearbyText?: string;
20
+ cssClasses?: string;
21
+ nearbyElements?: string;
22
+ computedStyles?: string;
23
+ fullPath?: string;
24
+ accessibility?: string;
25
+ isMultiSelect?: boolean;
26
+ isFixed?: boolean;
27
+ reactComponents?: string;
28
+ elementBoundingBoxes?: Array<{
29
+ x: number;
30
+ y: number;
31
+ width: number;
32
+ height: number;
33
+ }>;
34
+ drawingIndex?: number;
35
+ strokeId?: string;
36
+ sessionId?: string;
37
+ url?: string;
38
+ intent?: AnnotationIntent;
39
+ severity?: AnnotationSeverity;
40
+ status?: AnnotationStatus;
41
+ thread?: ThreadMessage[];
42
+ createdAt?: string;
43
+ updatedAt?: string;
44
+ resolvedAt?: string;
45
+ resolvedBy?: "human" | "agent";
46
+ authorId?: string;
47
+ _syncedTo?: string;
48
+ };
49
+ type AnnotationIntent = "fix" | "change" | "question" | "approve";
50
+ type AnnotationSeverity = "blocking" | "important" | "suggestion";
51
+ type AnnotationStatus = "pending" | "acknowledged" | "resolved" | "dismissed";
52
+ type ThreadMessage = {
53
+ id: string;
54
+ role: "human" | "agent";
55
+ content: string;
56
+ timestamp: number;
57
+ };
58
+
59
+ type DemoAnnotation = {
60
+ selector: string;
61
+ comment: string;
62
+ selectedText?: string;
63
+ };
64
+ type PageFeedbackToolbarCSSProps = {
65
+ demoAnnotations?: DemoAnnotation[];
66
+ demoDelay?: number;
67
+ enableDemoMode?: boolean;
68
+ /** Callback fired when an annotation is added. */
69
+ onAnnotationAdd?: (annotation: Annotation) => void;
70
+ /** Callback fired when an annotation is deleted. */
71
+ onAnnotationDelete?: (annotation: Annotation) => void;
72
+ /** Callback fired when an annotation comment is edited. */
73
+ onAnnotationUpdate?: (annotation: Annotation) => void;
74
+ /** Callback fired when all annotations are cleared. Receives the annotations that were cleared. */
75
+ onAnnotationsClear?: (annotations: Annotation[]) => void;
76
+ /** Callback fired when the copy button is clicked. Receives the markdown output. */
77
+ onCopy?: (markdown: string) => void;
78
+ /** Callback fired when "Send to Agent" is clicked. Receives the markdown output and annotations. */
79
+ onSubmit?: (output: string, annotations: Annotation[]) => void;
80
+ /** Whether to copy to clipboard when the copy button is clicked. Defaults to true. */
81
+ copyToClipboard?: boolean;
82
+ /** Server URL for sync (e.g., "http://localhost:4747"). If not provided, uses localStorage only. */
83
+ endpoint?: string;
84
+ /** Pre-existing session ID to join. If not provided with endpoint, creates a new session. */
85
+ sessionId?: string;
86
+ /** Called when a new session is created (only when endpoint is provided without sessionId). */
87
+ onSessionCreated?: (sessionId: string) => void;
88
+ /** Webhook URL to receive annotation events. */
89
+ webhookUrl?: string;
90
+ };
91
+ /** Alias for PageFeedbackToolbarCSSProps */
92
+ type AgentationProps = PageFeedbackToolbarCSSProps;
93
+ declare function PageFeedbackToolbarCSS({ demoAnnotations, demoDelay, enableDemoMode, onAnnotationAdd, onAnnotationDelete, onAnnotationUpdate, onAnnotationsClear, onCopy, onSubmit, copyToClipboard, endpoint, sessionId: initialSessionId, onSessionCreated, webhookUrl, }?: PageFeedbackToolbarCSSProps): react.ReactPortal | null;
94
+
95
+ interface AnnotationPopupCSSProps {
96
+ /** Element name to display in header */
97
+ element: string;
98
+ /** Optional timestamp display (e.g., "@ 1.23s" for animation feedback) */
99
+ timestamp?: string;
100
+ /** Optional selected/highlighted text */
101
+ selectedText?: string;
102
+ /** Placeholder text for the textarea */
103
+ placeholder?: string;
104
+ /** Initial value for textarea (for edit mode) */
105
+ initialValue?: string;
106
+ /** Label for submit button (default: "Add") */
107
+ submitLabel?: string;
108
+ /** Called when annotation is submitted with text */
109
+ onSubmit: (text: string) => void;
110
+ /** Called when popup is cancelled/dismissed */
111
+ onCancel: () => void;
112
+ /** Called when delete button is clicked (only shown if provided) */
113
+ onDelete?: () => void;
114
+ /** Position styles (left, top) */
115
+ style?: React.CSSProperties;
116
+ /** Custom color for submit button and textarea focus (hex) */
117
+ accentColor?: string;
118
+ /** External exit state (parent controls exit animation) */
119
+ isExiting?: boolean;
120
+ /** Light mode styling */
121
+ lightMode?: boolean;
122
+ /** Computed styles for the selected element */
123
+ computedStyles?: Record<string, string>;
124
+ }
125
+ interface AnnotationPopupCSSHandle {
126
+ /** Shake the popup (e.g., when user clicks outside) */
127
+ shake: () => void;
128
+ }
129
+ declare const AnnotationPopupCSS: react.ForwardRefExoticComponent<AnnotationPopupCSSProps & react.RefAttributes<AnnotationPopupCSSHandle>>;
130
+
131
+ declare const IconClose: ({ size }: {
132
+ size?: number;
133
+ }) => react_jsx_runtime.JSX.Element;
134
+ declare const IconPlus: ({ size }: {
135
+ size?: number;
136
+ }) => react_jsx_runtime.JSX.Element;
137
+ declare const IconCheck: ({ size }: {
138
+ size?: number;
139
+ }) => react_jsx_runtime.JSX.Element;
140
+ declare const IconCheckSmall: ({ size }: {
141
+ size?: number;
142
+ }) => react_jsx_runtime.JSX.Element;
143
+ declare const IconListSparkle: ({ size, style, }: {
144
+ size?: number;
145
+ style?: React.CSSProperties;
146
+ }) => react_jsx_runtime.JSX.Element;
147
+ declare const IconHelp: ({ size }: {
148
+ size?: number;
149
+ }) => react_jsx_runtime.JSX.Element;
150
+ declare const IconCheckSmallAnimated: ({ size }: {
151
+ size?: number;
152
+ }) => react_jsx_runtime.JSX.Element;
153
+ declare const IconCopyAlt: ({ size }: {
154
+ size?: number;
155
+ }) => react_jsx_runtime.JSX.Element;
156
+ declare const IconCopyAnimated: ({ size, copied }: {
157
+ size?: number;
158
+ copied?: boolean;
159
+ }) => react_jsx_runtime.JSX.Element;
160
+ declare const IconSendArrow: ({ size, state }: {
161
+ size?: number;
162
+ state?: "idle" | "sending" | "sent" | "failed";
163
+ }) => react_jsx_runtime.JSX.Element;
164
+ declare const IconSendAnimated: ({ size, sent }: {
165
+ size?: number;
166
+ sent?: boolean;
167
+ }) => react_jsx_runtime.JSX.Element;
168
+ declare const IconEye: ({ size }: {
169
+ size?: number;
170
+ }) => react_jsx_runtime.JSX.Element;
171
+ declare const IconEyeAlt: ({ size }: {
172
+ size?: number;
173
+ }) => react_jsx_runtime.JSX.Element;
174
+ declare const IconEyeClosed: ({ size }: {
175
+ size?: number;
176
+ }) => react_jsx_runtime.JSX.Element;
177
+ declare const IconEyeAnimated: ({ size, isOpen }: {
178
+ size?: number;
179
+ isOpen?: boolean;
180
+ }) => react_jsx_runtime.JSX.Element;
181
+ declare const IconPausePlayAnimated: ({ size, isPaused }: {
182
+ size?: number;
183
+ isPaused?: boolean;
184
+ }) => react_jsx_runtime.JSX.Element;
185
+ declare const IconEyeMinus: ({ size }: {
186
+ size?: number;
187
+ }) => react_jsx_runtime.JSX.Element;
188
+ declare const IconGear: ({ size }: {
189
+ size?: number;
190
+ }) => react_jsx_runtime.JSX.Element;
191
+ declare const IconPauseAlt: ({ size }: {
192
+ size?: number;
193
+ }) => react_jsx_runtime.JSX.Element;
194
+ declare const IconPause: ({ size }: {
195
+ size?: number;
196
+ }) => react_jsx_runtime.JSX.Element;
197
+ declare const IconPlayAlt: ({ size }: {
198
+ size?: number;
199
+ }) => react_jsx_runtime.JSX.Element;
200
+ declare const IconTrashAlt: ({ size }: {
201
+ size?: number;
202
+ }) => react_jsx_runtime.JSX.Element;
203
+ declare const IconChatEllipsis: ({ size, style, }: {
204
+ size?: number;
205
+ style?: React.CSSProperties;
206
+ }) => react_jsx_runtime.JSX.Element;
207
+ declare const IconCheckmark: ({ size }: {
208
+ size?: number;
209
+ }) => react_jsx_runtime.JSX.Element;
210
+ declare const IconCheckmarkLarge: ({ size }: {
211
+ size?: number;
212
+ }) => react_jsx_runtime.JSX.Element;
213
+ declare const IconCheckmarkCircle: ({ size }: {
214
+ size?: number;
215
+ }) => react_jsx_runtime.JSX.Element;
216
+ declare const IconXmark: ({ size }: {
217
+ size?: number;
218
+ }) => react_jsx_runtime.JSX.Element;
219
+ declare const IconXmarkLarge: ({ size }: {
220
+ size?: number;
221
+ }) => react_jsx_runtime.JSX.Element;
222
+ declare const IconSun: ({ size }: {
223
+ size?: number;
224
+ }) => react_jsx_runtime.JSX.Element;
225
+ declare const IconMoon: ({ size }: {
226
+ size?: number;
227
+ }) => react_jsx_runtime.JSX.Element;
228
+ declare const IconEdit: ({ size }: {
229
+ size?: number;
230
+ }) => react_jsx_runtime.JSX.Element;
231
+ declare const IconTrash: ({ size }: {
232
+ size?: number;
233
+ }) => react_jsx_runtime.JSX.Element;
234
+ declare const IconChevronLeft: ({ size }: {
235
+ size?: number;
236
+ }) => react_jsx_runtime.JSX.Element;
237
+ declare const IconChevronRight: ({ size }: {
238
+ size?: number;
239
+ }) => react_jsx_runtime.JSX.Element;
240
+ declare const IconPencil: ({ size }: {
241
+ size?: number;
242
+ }) => react_jsx_runtime.JSX.Element;
243
+ declare const AnimatedBunny: ({ size, color, }: {
244
+ size?: number;
245
+ color?: string;
246
+ }) => react_jsx_runtime.JSX.Element;
247
+
248
+ /**
249
+ * Finds the closest ancestor matching a selector, crossing shadow DOM boundaries.
250
+ */
251
+ declare function closestCrossingShadow(element: Element, selector: string): Element | null;
252
+ /**
253
+ * Checks if an element is inside a shadow DOM
254
+ */
255
+ declare function isInShadowDOM(element: Element): boolean;
256
+ /**
257
+ * Gets the shadow host for an element, or null if not in shadow DOM
258
+ */
259
+ declare function getShadowHost(element: Element): Element | null;
260
+ /**
261
+ * Gets a readable path for an element (e.g., "article > section > p")
262
+ * Supports elements inside shadow DOM by crossing shadow boundaries.
263
+ */
264
+ declare function getElementPath(target: HTMLElement, maxDepth?: number): string;
265
+ /**
266
+ * Identifies an element and returns a human-readable name + path
267
+ */
268
+ declare function identifyElement(target: HTMLElement): {
269
+ name: string;
270
+ path: string;
271
+ };
272
+ /**
273
+ * Gets text content from element and siblings for context
274
+ */
275
+ declare function getNearbyText(element: HTMLElement): string;
276
+ /**
277
+ * Simplified element identifier for animation feedback (less verbose)
278
+ */
279
+ declare function identifyAnimationElement(target: HTMLElement): string;
280
+ /**
281
+ * Gets CSS class names from an element (cleaned of module hashes)
282
+ */
283
+ declare function getElementClasses(target: HTMLElement): string;
284
+
285
+ declare function getStorageKey(pathname: string): string;
286
+ declare function loadAnnotations<T = Annotation>(pathname: string): T[];
287
+ declare function saveAnnotations<T = Annotation>(pathname: string, annotations: T[]): void;
288
+
289
+ export { PageFeedbackToolbarCSS as Agentation, type AgentationProps, AnimatedBunny, type Annotation, AnnotationPopupCSS, type AnnotationPopupCSSHandle, type AnnotationPopupCSSProps, type DemoAnnotation, IconChatEllipsis, IconCheck, IconCheckSmall, IconCheckSmallAnimated, IconCheckmark, IconCheckmarkCircle, IconCheckmarkLarge, IconChevronLeft, IconChevronRight, IconClose, IconCopyAlt, IconCopyAnimated, IconEdit, IconEye, IconEyeAlt, IconEyeAnimated, IconEyeClosed, IconEyeMinus, IconGear, IconHelp, IconListSparkle, IconMoon, IconPause, IconPauseAlt, IconPausePlayAnimated, IconPencil, IconPlayAlt, IconPlus, IconSendAnimated, IconSendArrow, IconSun, IconTrash, IconTrashAlt, IconXmark, IconXmarkLarge, PageFeedbackToolbarCSS, closestCrossingShadow, getElementClasses, getElementPath, getNearbyText, getShadowHost, getStorageKey, identifyAnimationElement, identifyElement, isInShadowDOM, loadAnnotations, saveAnnotations };