@mikuexe/annotator-react 0.1.0
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/CHANGELOG.md +25 -0
- package/LICENSE +21 -0
- package/README.md +161 -0
- package/dist/index.cjs +749 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +48 -0
- package/dist/index.d.ts +48 -0
- package/dist/index.js +716 -0
- package/dist/index.js.map +1 -0
- package/dist/register.cjs +21 -0
- package/dist/register.cjs.map +1 -0
- package/dist/register.d.cts +2 -0
- package/dist/register.d.ts +2 -0
- package/dist/register.js +5 -0
- package/dist/register.js.map +1 -0
- package/package.json +103 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/SourceAnnotator.tsx","../src/capture.ts","../src/clipboard.ts","../src/format.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport type { CSSProperties } from \"react\";\nimport { Toaster, toast } from \"sonner\";\nimport { captureElementAnnotation } from \"./capture\";\nimport { copyTextToClipboard } from \"./clipboard\";\nimport { createAnnotationCollection, formatAnnotationCollection } from \"./format\";\nimport type { Annotation, SourceAnnotatorOutput, SourceAnnotatorProps } from \"./types\";\n\ntype Rect = {\n top: number;\n left: number;\n width: number;\n height: number;\n};\n\ntype SelectedElement = {\n element: Element;\n rect: Rect;\n annotation: Annotation | null;\n loading: boolean;\n};\n\ntype StoredAnnotation = Annotation & {\n targetElement: Element;\n rect: Rect;\n};\n\nconst ROOT_ATTR = \"data-mikuexe-annotator-root\";\nconst DEFAULT_HOTKEY = \"alt+a\";\nconst DEFAULT_OUTPUT: SourceAnnotatorOutput = \"markdown\";\n\nexport function SourceAnnotator({\n enabled = true,\n hotkey = DEFAULT_HOTKEY,\n output = DEFAULT_OUTPUT,\n onCollect,\n renderToaster = true,\n}: SourceAnnotatorProps) {\n const [isAnnotating, setIsAnnotating] = useState(false);\n const [hoverRect, setHoverRect] = useState<Rect | null>(null);\n const [selected, setSelected] = useState<SelectedElement | null>(null);\n const [note, setNote] = useState(\"\");\n const [annotations, setAnnotations] = useState<StoredAnnotation[]>([]);\n const [status, setStatus] = useState<string | null>(null);\n const selectedRef = useRef<SelectedElement | null>(null);\n\n selectedRef.current = selected;\n\n const collection = useMemo(\n () => createAnnotationCollection(annotations.map(({ rect: _rect, targetElement: _targetElement, ...annotation }) => annotation)),\n [annotations],\n );\n\n const refreshTrackedRects = useCallback(() => {\n setHoverRect(null);\n setSelected((current) => (current ? { ...current, rect: getRect(current.element) } : current));\n setAnnotations((existing) => existing.map((annotation) => ({ ...annotation, rect: getRect(annotation.targetElement) })));\n }, []);\n\n useEffect(() => {\n if (!enabled) {\n setIsAnnotating(false);\n }\n }, [enabled]);\n\n useEffect(() => {\n const onKeyDown = (event: KeyboardEvent) => {\n if (!matchesHotkey(event, hotkey)) {\n return;\n }\n\n event.preventDefault();\n setIsAnnotating((current) => enabled && !current);\n };\n\n document.addEventListener(\"keydown\", onKeyDown);\n return () => document.removeEventListener(\"keydown\", onKeyDown);\n }, [enabled, hotkey]);\n\n useEffect(() => {\n if (!enabled || !isAnnotating) {\n setHoverRect(null);\n return;\n }\n\n const onPointerOver = (event: PointerEvent) => {\n const target = getAnnotatableTarget(event.target);\n if (!target) {\n setHoverRect(null);\n return;\n }\n\n setHoverRect(getRect(target));\n };\n\n const onClick = (event: MouseEvent) => {\n const target = getAnnotatableTarget(event.target);\n if (!target) {\n return;\n }\n\n event.preventDefault();\n event.stopPropagation();\n\n const rect = getRect(target);\n setSelected({ element: target, rect, annotation: null, loading: true });\n setNote(\"\");\n setStatus(\"Resolving source…\");\n\n captureElementAnnotation(target, \"\")\n .then((annotation) => {\n setSelected((current) => {\n if (current?.element !== target) {\n return current;\n }\n\n return { ...current, annotation, loading: false };\n });\n setStatus(annotation.source ? \"Source captured.\" : \"Element captured without source info.\");\n })\n .catch(() => {\n setSelected((current) => (current?.element === target ? { ...current, loading: false } : current));\n setStatus(\"Element captured without source info.\");\n });\n };\n\n document.addEventListener(\"pointerover\", onPointerOver, true);\n document.addEventListener(\"click\", onClick, true);\n\n return () => {\n document.removeEventListener(\"pointerover\", onPointerOver, true);\n document.removeEventListener(\"click\", onClick, true);\n };\n }, [enabled, isAnnotating]);\n\n useEffect(() => {\n if (!enabled || !isAnnotating) {\n return;\n }\n\n document.addEventListener(\"scroll\", refreshTrackedRects, true);\n window.addEventListener(\"resize\", refreshTrackedRects);\n\n return () => {\n document.removeEventListener(\"scroll\", refreshTrackedRects, true);\n window.removeEventListener(\"resize\", refreshTrackedRects);\n };\n }, [enabled, isAnnotating, refreshTrackedRects]);\n\n const addAnnotation = useCallback(async () => {\n const current = selectedRef.current;\n if (!current || current.loading) {\n return;\n }\n\n const trimmedNote = note.trim();\n if (!trimmedNote) {\n setStatus(\"Add a note before saving this annotation.\");\n return;\n }\n\n const annotation = current.annotation\n ? { ...current.annotation, note: trimmedNote }\n : await captureElementAnnotation(current.element, trimmedNote);\n\n setAnnotations((existing) => [...existing, { ...annotation, targetElement: current.element, rect: getRect(current.element) }]);\n setSelected(null);\n setNote(\"\");\n setStatus(\"Annotation saved.\");\n }, [note]);\n\n const collect = useCallback(async () => {\n const payload = createAnnotationCollection(annotations.map(({ rect: _rect, targetElement: _targetElement, ...annotation }) => annotation));\n const text = formatAnnotationCollection(payload, output);\n\n try {\n await copyTextToClipboard(text);\n onCollect?.(payload);\n setIsAnnotating(false);\n setSelected(null);\n setHoverRect(null);\n setNote(\"\");\n toast.success(\"Annotations copied\", { description: `${payload.annotations.length} copied to clipboard.` });\n setStatus(`Copied ${payload.annotations.length} annotation${payload.annotations.length === 1 ? \"\" : \"s\"}.`);\n } catch (error) {\n toast.error(\"Copy failed\", { description: error instanceof Error ? error.message : \"Clipboard copy failed.\" });\n setStatus(error instanceof Error ? error.message : \"Clipboard copy failed.\");\n }\n }, [annotations, onCollect, output]);\n\n if (!enabled) {\n return null;\n }\n\n return (\n <div {...{ [ROOT_ATTR]: \"\" }} style={styles.root} aria-live=\"polite\">\n {renderToaster ? <Toaster position=\"bottom-right\" richColors /> : null}\n <button\n type=\"button\"\n onClick={() => setIsAnnotating((current) => !current)}\n style={{ ...styles.floatingButton, ...(isAnnotating ? styles.floatingButtonActive : null) }}\n aria-pressed={isAnnotating}\n title={`Toggle annotator (${hotkey})`}\n >\n {isAnnotating ? \"Annotating\" : \"Annotate\"}\n </button>\n\n {isAnnotating && hoverRect ? <Box rect={hoverRect} kind=\"hover\" /> : null}\n {selected ? <Box rect={selected.rect} kind=\"selected\" /> : null}\n {isAnnotating\n ? annotations.map((annotation, index) => (\n <Pin key={annotation.id} annotation={annotation} index={index} />\n ))\n : null}\n\n {selected ? (\n <div style={getPopoverStyle(selected.rect)} role=\"dialog\" aria-label=\"Add source annotation\">\n <div style={styles.popoverTitle}>Annotation</div>\n <div style={styles.metaText}>{selected.loading ? \"Resolving source…\" : formatSelectedSource(selected.annotation)}</div>\n <textarea\n value={note}\n onChange={(event) => setNote(event.target.value)}\n placeholder=\"What should change here?\"\n style={styles.textarea}\n rows={4}\n autoFocus\n />\n <div style={styles.popoverActions}>\n <button type=\"button\" onClick={() => setSelected(null)} style={styles.secondaryButton}>\n Cancel\n </button>\n <button type=\"button\" onClick={addAnnotation} style={styles.primaryButton} disabled={selected.loading}>\n Save note\n </button>\n </div>\n </div>\n ) : null}\n\n {isAnnotating ? (\n <section style={styles.panel} aria-label=\"Collected annotations\">\n <div style={styles.panelHeader}>\n <strong>Annotations</strong>\n <span style={styles.badge}>{collection.annotations.length}</span>\n </div>\n {annotations.length ? (\n <ol style={styles.annotationList}>\n {annotations.map((annotation) => (\n <li key={annotation.id} style={styles.annotationItem}>\n <div style={styles.noteText}>{annotation.note}</div>\n <div style={styles.metaText}>{formatSelectedSource(annotation)}</div>\n </li>\n ))}\n </ol>\n ) : (\n <p style={styles.emptyText}>Hover an element, click it, then add a note.</p>\n )}\n <button type=\"button\" onClick={collect} style={styles.collectButton} disabled={!annotations.length}>\n Collect\n </button>\n {status ? <div style={styles.status}>{status}</div> : null}\n </section>\n ) : null}\n </div>\n );\n}\n\nfunction Box({ rect, kind }: { rect: Rect; kind: \"hover\" | \"selected\" }) {\n return (\n <div\n style={{\n ...styles.box,\n ...(kind === \"selected\" ? styles.selectedBox : styles.hoverBox),\n top: rect.top,\n left: rect.left,\n width: rect.width,\n height: rect.height,\n }}\n />\n );\n}\n\nfunction Pin({ annotation, index }: { annotation: StoredAnnotation; index: number }) {\n return (\n <div\n style={{ ...styles.pin, top: Math.max(8, annotation.rect.top - 10), left: Math.max(8, annotation.rect.left - 10) }}\n title={annotation.note}\n >\n {index + 1}\n </div>\n );\n}\n\nfunction getAnnotatableTarget(target: EventTarget | null): Element | null {\n if (!(target instanceof Element)) {\n return null;\n }\n\n if (target.closest(`[${ROOT_ATTR}]`)) {\n return null;\n }\n\n if (target === document.body || target === document.documentElement) {\n return null;\n }\n\n return target;\n}\n\nfunction getRect(element: Element): Rect {\n const rect = element.getBoundingClientRect();\n return {\n top: rect.top,\n left: rect.left,\n width: rect.width,\n height: rect.height,\n };\n}\n\nfunction getPopoverStyle(rect: Rect): CSSProperties {\n const top = Math.min(window.innerHeight - 260, rect.top + rect.height + 8);\n const left = Math.min(window.innerWidth - 340, Math.max(8, rect.left));\n\n return {\n ...styles.popover,\n top: Math.max(8, top),\n left,\n };\n}\n\nfunction formatSelectedSource(annotation: Annotation | null): string {\n const componentPath = annotation?.componentPath.length ? annotation.componentPath.join(\" › \") : null;\n\n if (!annotation) {\n return \"Source unavailable\";\n }\n\n if (!annotation.source?.filePath) {\n return `${annotation.element.selector} · source unavailable`;\n }\n\n const line = annotation.source.lineNumber ? `:${annotation.source.lineNumber}` : \"\";\n const component = componentPath ? ` · ${componentPath}` : \"\";\n return `${annotation.source.filePath}${line}${component}`;\n}\n\nfunction matchesHotkey(event: KeyboardEvent, hotkey: string): boolean {\n const parts = hotkey.toLowerCase().split(\"+\").map((part) => part.trim()).filter(Boolean);\n const key = parts.find((part) => ![\"ctrl\", \"control\", \"cmd\", \"meta\", \"mod\", \"shift\", \"alt\", \"option\"].includes(part));\n\n const wantsMeta = parts.includes(\"meta\") || parts.includes(\"cmd\") || (parts.includes(\"mod\") && isMac());\n const wantsCtrl = parts.includes(\"ctrl\") || parts.includes(\"control\") || (parts.includes(\"mod\") && !isMac());\n const wantsShift = parts.includes(\"shift\");\n const wantsAlt = parts.includes(\"alt\") || parts.includes(\"option\");\n\n return (\n event.key.toLowerCase() === key &&\n event.metaKey === wantsMeta &&\n event.ctrlKey === wantsCtrl &&\n event.shiftKey === wantsShift &&\n event.altKey === wantsAlt\n );\n}\n\nfunction isMac(): boolean {\n return typeof navigator !== \"undefined\" && /mac|iphone|ipad|ipod/i.test(navigator.platform);\n}\n\nconst baseFont = '13px/1.35 -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif';\n\nconst styles = {\n root: {\n position: \"fixed\",\n inset: 0,\n zIndex: 2147483647,\n pointerEvents: \"none\",\n font: baseFont,\n color: \"#0f172a\",\n } satisfies CSSProperties,\n floatingButton: {\n position: \"fixed\",\n right: 16,\n bottom: 16,\n pointerEvents: \"auto\",\n borderWidth: 1,\n borderStyle: \"solid\",\n borderColor: \"#cbd5e1\",\n background: \"#ffffff\",\n color: \"#0f172a\",\n borderRadius: 999,\n padding: \"10px 14px\",\n font: baseFont,\n fontWeight: 700,\n boxShadow: \"0 10px 25px rgba(15, 23, 42, 0.16)\",\n cursor: \"pointer\",\n } satisfies CSSProperties,\n floatingButtonActive: {\n background: \"#0f172a\",\n color: \"#ffffff\",\n borderColor: \"#0f172a\",\n } satisfies CSSProperties,\n box: {\n position: \"fixed\",\n borderRadius: 6,\n pointerEvents: \"none\",\n boxSizing: \"border-box\",\n } satisfies CSSProperties,\n hoverBox: {\n border: \"2px solid #38bdf8\",\n background: \"rgba(56, 189, 248, 0.08)\",\n } satisfies CSSProperties,\n selectedBox: {\n border: \"2px solid #f97316\",\n background: \"rgba(249, 115, 22, 0.1)\",\n } satisfies CSSProperties,\n popover: {\n position: \"fixed\",\n width: 320,\n pointerEvents: \"auto\",\n background: \"#ffffff\",\n border: \"1px solid #cbd5e1\",\n borderRadius: 12,\n padding: 12,\n boxShadow: \"0 18px 45px rgba(15, 23, 42, 0.22)\",\n } satisfies CSSProperties,\n popoverTitle: {\n fontWeight: 800,\n marginBottom: 4,\n } satisfies CSSProperties,\n metaText: {\n color: \"#64748b\",\n fontSize: 12,\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n whiteSpace: \"nowrap\",\n } satisfies CSSProperties,\n textarea: {\n width: \"100%\",\n boxSizing: \"border-box\",\n marginTop: 10,\n border: \"1px solid #cbd5e1\",\n borderRadius: 8,\n padding: 10,\n resize: \"vertical\",\n font: baseFont,\n } satisfies CSSProperties,\n popoverActions: {\n display: \"flex\",\n justifyContent: \"flex-end\",\n gap: 8,\n marginTop: 10,\n } satisfies CSSProperties,\n secondaryButton: {\n border: \"1px solid #cbd5e1\",\n borderRadius: 8,\n background: \"#ffffff\",\n padding: \"7px 10px\",\n cursor: \"pointer\",\n } satisfies CSSProperties,\n primaryButton: {\n border: \"1px solid #0f172a\",\n borderRadius: 8,\n background: \"#0f172a\",\n color: \"#ffffff\",\n padding: \"7px 10px\",\n cursor: \"pointer\",\n } satisfies CSSProperties,\n panel: {\n position: \"fixed\",\n right: 16,\n bottom: 68,\n width: 300,\n pointerEvents: \"auto\",\n background: \"#ffffff\",\n border: \"1px solid #cbd5e1\",\n borderRadius: 12,\n padding: 12,\n boxShadow: \"0 18px 45px rgba(15, 23, 42, 0.18)\",\n } satisfies CSSProperties,\n panelHeader: {\n display: \"flex\",\n justifyContent: \"space-between\",\n alignItems: \"center\",\n marginBottom: 8,\n } satisfies CSSProperties,\n badge: {\n minWidth: 20,\n height: 20,\n borderRadius: 999,\n display: \"inline-flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n background: \"#e2e8f0\",\n color: \"#334155\",\n fontWeight: 700,\n fontSize: 12,\n } satisfies CSSProperties,\n annotationList: {\n listStyle: \"decimal\",\n margin: \"0 0 10px 18px\",\n padding: 0,\n maxHeight: 180,\n overflow: \"auto\",\n } satisfies CSSProperties,\n annotationItem: {\n marginBottom: 8,\n } satisfies CSSProperties,\n noteText: {\n color: \"#0f172a\",\n fontWeight: 650,\n } satisfies CSSProperties,\n emptyText: {\n color: \"#64748b\",\n margin: \"6px 0 10px\",\n } satisfies CSSProperties,\n collectButton: {\n width: \"100%\",\n border: \"1px solid #0f172a\",\n borderRadius: 8,\n background: \"#0f172a\",\n color: \"#ffffff\",\n padding: \"9px 10px\",\n fontWeight: 800,\n cursor: \"pointer\",\n } satisfies CSSProperties,\n status: {\n marginTop: 8,\n color: \"#475569\",\n fontSize: 12,\n } satisfies CSSProperties,\n pin: {\n position: \"fixed\",\n width: 20,\n height: 20,\n borderRadius: 999,\n display: \"inline-flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n pointerEvents: \"none\",\n background: \"#f97316\",\n color: \"#ffffff\",\n fontSize: 12,\n fontWeight: 800,\n boxShadow: \"0 8px 18px rgba(15, 23, 42, 0.2)\",\n } satisfies CSSProperties,\n};\n","import { resolveElementInfo } from \"element-source\";\nimport type { Annotation, AnnotationSource } from \"./types\";\n\nconst MAX_TEXT_LENGTH = 240;\nconst MAX_HTML_LENGTH = 640;\nconst MAX_SELECTOR_DEPTH = 6;\n\nexport async function captureElementAnnotation(\n element: Element,\n note: string,\n id = createAnnotationId(),\n): Promise<Annotation> {\n const elementInfo = await safeResolveElementInfo(element);\n const source = normalizeSource(elementInfo?.source, elementInfo?.componentName);\n const sourceStack = normalizeSourceStack(elementInfo?.stack, source);\n const componentPath = getComponentPath(sourceStack, source);\n\n return {\n id,\n note,\n source,\n sourceStack,\n componentPath,\n element: {\n tagName: element.tagName.toLowerCase(),\n text: trimText(element.textContent ?? \"\", MAX_TEXT_LENGTH),\n html: trimText(getOuterHtml(element), MAX_HTML_LENGTH),\n selector: getElementSelector(element),\n },\n };\n}\n\nexport function createAnnotationId(): string {\n if (typeof crypto !== \"undefined\" && \"randomUUID\" in crypto) {\n return crypto.randomUUID();\n }\n\n return `annotation-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;\n}\n\nexport function getElementSelector(element: Element): string {\n const parts: string[] = [];\n let current: Element | null = element;\n\n while (current && current.nodeType === Node.ELEMENT_NODE && parts.length < MAX_SELECTOR_DEPTH) {\n parts.unshift(getSelectorPart(current));\n\n if (current.id) {\n break;\n }\n\n current = current.parentElement;\n if (current?.tagName.toLowerCase() === \"html\") {\n break;\n }\n }\n\n return parts.join(\" \");\n}\n\nexport function trimText(value: string, maxLength: number): string {\n const normalized = value.replace(/\\s+/g, \" \").trim();\n\n if (normalized.length <= maxLength) {\n return normalized;\n }\n\n return `${normalized.slice(0, Math.max(0, maxLength - 1)).trimEnd()}…`;\n}\n\nasync function safeResolveElementInfo(element: Element) {\n try {\n return await resolveElementInfo(element);\n } catch {\n return null;\n }\n}\n\nfunction normalizeSource(\n source: AnnotationSource | null | undefined,\n componentName: string | null | undefined,\n): AnnotationSource | null {\n if (!source) {\n return componentName\n ? { filePath: \"\", lineNumber: null, columnNumber: null, componentName }\n : null;\n }\n\n return {\n filePath: source.filePath,\n lineNumber: source.lineNumber ?? null,\n columnNumber: source.columnNumber ?? null,\n componentName: source.componentName ?? componentName ?? null,\n };\n}\n\nfunction normalizeSourceStack(\n stack: AnnotationSource[] | null | undefined,\n source: AnnotationSource | null,\n): AnnotationSource[] {\n const normalizedStack = (stack ?? []).map((frame) => ({\n filePath: frame.filePath,\n lineNumber: frame.lineNumber ?? null,\n columnNumber: frame.columnNumber ?? null,\n componentName: frame.componentName ?? null,\n }));\n\n if (source && !normalizedStack.some((frame) => isSameSourceFrame(frame, source))) {\n normalizedStack.unshift(source);\n }\n\n return normalizedStack;\n}\n\nfunction getComponentPath(stack: AnnotationSource[], source: AnnotationSource | null): string[] {\n const names = stack.map((frame) => frame.componentName).filter((name): name is string => Boolean(name));\n return Array.from(new Set(names.length ? names : source?.componentName ? [source.componentName] : []));\n}\n\nfunction isSameSourceFrame(a: AnnotationSource, b: AnnotationSource): boolean {\n return a.filePath === b.filePath && a.lineNumber === b.lineNumber && a.columnNumber === b.columnNumber;\n}\n\nfunction getOuterHtml(element: Element): string {\n if (\"outerHTML\" in element && typeof element.outerHTML === \"string\") {\n return element.outerHTML;\n }\n\n return `<${element.tagName.toLowerCase()}>`;\n}\n\nfunction getSelectorPart(element: Element): string {\n const tagName = element.tagName.toLowerCase();\n\n if (element.id) {\n return `#${escapeIdentifier(element.id)}`;\n }\n\n const testId = element.getAttribute(\"data-testid\");\n if (testId) {\n return `${tagName}[data-testid=\"${escapeAttribute(testId)}\"]`;\n }\n\n const classes = Array.from(element.classList)\n .filter(Boolean)\n .slice(0, 2)\n .map((className) => `.${escapeIdentifier(className)}`)\n .join(\"\");\n\n const siblingIndex = getNthOfType(element);\n const needsNth = siblingIndex > 1 || hasFollowingSiblingOfSameType(element);\n\n return `${tagName}${classes}${needsNth ? `:nth-of-type(${siblingIndex})` : \"\"}`;\n}\n\nfunction getNthOfType(element: Element): number {\n let index = 1;\n let sibling = element.previousElementSibling;\n\n while (sibling) {\n if (sibling.tagName === element.tagName) {\n index += 1;\n }\n sibling = sibling.previousElementSibling;\n }\n\n return index;\n}\n\nfunction hasFollowingSiblingOfSameType(element: Element): boolean {\n let sibling = element.nextElementSibling;\n\n while (sibling) {\n if (sibling.tagName === element.tagName) {\n return true;\n }\n sibling = sibling.nextElementSibling;\n }\n\n return false;\n}\n\nfunction escapeIdentifier(value: string): string {\n if (typeof CSS !== \"undefined\" && typeof CSS.escape === \"function\") {\n return CSS.escape(value);\n }\n\n return value.replace(/[^a-zA-Z0-9_-]/g, \"\\\\$&\");\n}\n\nfunction escapeAttribute(value: string): string {\n return value.replace(/\"/g, \"\\\\\\\"\");\n}\n","export async function copyTextToClipboard(text: string): Promise<void> {\n if (typeof navigator !== \"undefined\" && navigator.clipboard?.writeText) {\n await navigator.clipboard.writeText(text);\n return;\n }\n\n if (typeof document === \"undefined\") {\n throw new Error(\"Clipboard is not available outside a browser environment.\");\n }\n\n const textarea = document.createElement(\"textarea\");\n textarea.value = text;\n textarea.setAttribute(\"readonly\", \"\");\n textarea.style.position = \"fixed\";\n textarea.style.left = \"-9999px\";\n textarea.style.top = \"0\";\n document.body.appendChild(textarea);\n textarea.select();\n\n const copied = document.execCommand(\"copy\");\n textarea.remove();\n\n if (!copied) {\n throw new Error(\"Clipboard copy failed.\");\n }\n}\n","import type { Annotation, AnnotationCollection, SourceAnnotatorOutput } from \"./types\";\n\nconst TASK_FRAMING = \"Please update the UI based on these source-linked annotations.\";\n\nexport function createAnnotationCollection(annotations: Annotation[]): AnnotationCollection {\n return {\n annotations,\n createdAt: new Date().toISOString(),\n };\n}\n\nexport function formatAnnotationCollection(\n collection: AnnotationCollection,\n output: SourceAnnotatorOutput = \"markdown\",\n): string {\n if (output === \"json\") {\n return formatJson(collection);\n }\n\n const markdown = formatMarkdown(collection);\n\n if (output === \"markdown\") {\n return markdown;\n }\n\n return `${markdown}\\n\\n## JSON Payload\\n\\n\\`\\`\\`json\\n${formatJson(collection)}\\n\\`\\`\\``;\n}\n\nexport function formatMarkdown(collection: AnnotationCollection): string {\n const lines = [TASK_FRAMING, \"\", `Collected at: ${collection.createdAt}`, \"\"];\n\n if (collection.annotations.length === 0) {\n lines.push(\"No annotations were collected.\");\n return lines.join(\"\\n\");\n }\n\n collection.annotations.forEach((annotation, index) => {\n const source = formatSource(annotation);\n const sourceStack = formatSourceStack(annotation);\n const nearestComponent = annotation.source?.componentName;\n const ownerPath = annotation.componentPath.join(\" › \");\n\n lines.push(`## Annotation ${index + 1}`);\n lines.push(\"\");\n lines.push(`ID: ${annotation.id}`);\n lines.push(`Note: ${annotation.note || \"(no note provided)\"}`);\n\n if (source) {\n lines.push(`Source: ${source}`);\n }\n\n if (nearestComponent) {\n lines.push(`Nearest React component: ${nearestComponent}`);\n }\n\n if (ownerPath && ownerPath !== nearestComponent) {\n lines.push(`React owner path: ${ownerPath}`);\n }\n\n if (sourceStack.length) {\n lines.push(\"React source stack:\");\n sourceStack.forEach((frame) => lines.push(`- ${frame}`));\n }\n\n lines.push(`Element tag: ${annotation.element.tagName}`);\n\n if (annotation.element.html) {\n lines.push(`Element HTML: ${annotation.element.html}`);\n }\n\n if (annotation.element.text) {\n lines.push(`Element text: ${annotation.element.text}`);\n }\n\n if (annotation.element.selector) {\n lines.push(`Selector: ${annotation.element.selector}`);\n }\n\n lines.push(\"\");\n });\n\n return lines.join(\"\\n\").trimEnd();\n}\n\nfunction formatJson(collection: AnnotationCollection): string {\n return JSON.stringify(collection, null, 2);\n}\n\nfunction formatSource(annotation: Annotation): string {\n const source = annotation.source;\n\n if (!source?.filePath) {\n return \"\";\n }\n\n const line = source.lineNumber ? `:${source.lineNumber}` : \"\";\n const column = source.columnNumber ? `:${source.columnNumber}` : \"\";\n\n return `${source.filePath}${line}${column}`;\n}\n\nfunction formatSourceStack(annotation: Annotation): string[] {\n return annotation.sourceStack\n .map((frame) => {\n const location = formatSourceFrame(frame);\n const component = frame.componentName ? ` (${frame.componentName})` : \"\";\n\n return location ? `${location}${component}` : frame.componentName || \"\";\n })\n .filter(Boolean);\n}\n\nfunction formatSourceFrame(frame: Annotation[\"sourceStack\"][number]): string {\n if (!frame.filePath) {\n return \"\";\n }\n\n const line = frame.lineNumber ? `:${frame.lineNumber}` : \"\";\n const column = frame.columnNumber ? `:${frame.columnNumber}` : \"\";\n\n return `${frame.filePath}${line}${column}`;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
"use strict";
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __copyProps = (to, from, except, desc) => {
|
|
8
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
9
|
+
for (let key of __getOwnPropNames(from))
|
|
10
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
11
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
12
|
+
}
|
|
13
|
+
return to;
|
|
14
|
+
};
|
|
15
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
16
|
+
|
|
17
|
+
// src/register.ts
|
|
18
|
+
var register_exports = {};
|
|
19
|
+
module.exports = __toCommonJS(register_exports);
|
|
20
|
+
var import_install_hook_only = require("bippy/install-hook-only");
|
|
21
|
+
//# sourceMappingURL=register.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/register.ts"],"sourcesContent":["import \"bippy/install-hook-only\";\n\nexport {};\n"],"mappings":";;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA,+BAAO;","names":[]}
|
package/dist/register.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/register.ts"],"sourcesContent":["import \"bippy/install-hook-only\";\n\nexport {};\n"],"mappings":";;;AAAA,OAAO;","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@mikuexe/annotator-react",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "React overlay for source-aware UI annotations copied into coding agents.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": "Benjamin Modayil",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/mikuexe/annotator-react.git"
|
|
11
|
+
},
|
|
12
|
+
"homepage": "https://github.com/mikuexe/annotator-react#readme",
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/mikuexe/annotator-react/issues"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"react",
|
|
18
|
+
"vite",
|
|
19
|
+
"annotations",
|
|
20
|
+
"developer-tools",
|
|
21
|
+
"source-map",
|
|
22
|
+
"ai-agents"
|
|
23
|
+
],
|
|
24
|
+
"engines": {
|
|
25
|
+
"node": ">=18"
|
|
26
|
+
},
|
|
27
|
+
"main": "./dist/index.cjs",
|
|
28
|
+
"module": "./dist/index.js",
|
|
29
|
+
"types": "./dist/index.d.ts",
|
|
30
|
+
"files": [
|
|
31
|
+
"dist",
|
|
32
|
+
"README.md",
|
|
33
|
+
"CHANGELOG.md",
|
|
34
|
+
"LICENSE"
|
|
35
|
+
],
|
|
36
|
+
"sideEffects": [
|
|
37
|
+
"./dist/register.js",
|
|
38
|
+
"./dist/register.cjs"
|
|
39
|
+
],
|
|
40
|
+
"exports": {
|
|
41
|
+
".": {
|
|
42
|
+
"import": {
|
|
43
|
+
"types": "./dist/index.d.ts",
|
|
44
|
+
"default": "./dist/index.js"
|
|
45
|
+
},
|
|
46
|
+
"require": {
|
|
47
|
+
"types": "./dist/index.d.cts",
|
|
48
|
+
"default": "./dist/index.cjs"
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
"./register": {
|
|
52
|
+
"import": {
|
|
53
|
+
"types": "./dist/register.d.ts",
|
|
54
|
+
"default": "./dist/register.js"
|
|
55
|
+
},
|
|
56
|
+
"require": {
|
|
57
|
+
"types": "./dist/register.d.cts",
|
|
58
|
+
"default": "./dist/register.cjs"
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
"./package.json": "./package.json"
|
|
62
|
+
},
|
|
63
|
+
"scripts": {
|
|
64
|
+
"build": "tsup",
|
|
65
|
+
"typecheck": "tsc --noEmit",
|
|
66
|
+
"test": "vitest run",
|
|
67
|
+
"check": "npm run typecheck && npm run test && npm run build",
|
|
68
|
+
"example:install": "npm install --prefix examples/vite-react",
|
|
69
|
+
"example:build": "npm run build --prefix examples/vite-react",
|
|
70
|
+
"pack:dry-run": "npm pack --dry-run",
|
|
71
|
+
"check:all": "npm run check && npm run example:build && npm run pack:dry-run",
|
|
72
|
+
"changeset": "changeset",
|
|
73
|
+
"version-packages": "changeset version",
|
|
74
|
+
"release": "npm run check:all && changeset publish",
|
|
75
|
+
"prepack": "npm run build",
|
|
76
|
+
"prepublishOnly": "npm run check:all"
|
|
77
|
+
},
|
|
78
|
+
"dependencies": {
|
|
79
|
+
"bippy": "^0.5.39",
|
|
80
|
+
"element-source": "^0.0.5",
|
|
81
|
+
"sonner": "^2.0.7"
|
|
82
|
+
},
|
|
83
|
+
"peerDependencies": {
|
|
84
|
+
"react": ">=18.0.0",
|
|
85
|
+
"react-dom": ">=18.0.0"
|
|
86
|
+
},
|
|
87
|
+
"devDependencies": {
|
|
88
|
+
"@changesets/cli": "^2.31.0",
|
|
89
|
+
"@types/react": "^19.2.7",
|
|
90
|
+
"@types/react-dom": "^19.2.3",
|
|
91
|
+
"@vitejs/plugin-react": "^5.1.2",
|
|
92
|
+
"happy-dom": "^20.0.11",
|
|
93
|
+
"react": "^19.2.5",
|
|
94
|
+
"react-dom": "^19.2.5",
|
|
95
|
+
"tsup": "^8.5.1",
|
|
96
|
+
"typescript": "^6.0.3",
|
|
97
|
+
"vite": "^8.0.10",
|
|
98
|
+
"vitest": "^4.0.18"
|
|
99
|
+
},
|
|
100
|
+
"publishConfig": {
|
|
101
|
+
"access": "public"
|
|
102
|
+
}
|
|
103
|
+
}
|