a.id === hoveredMarkerId,
);
if (!hoveredAnnotation?.boundingBox) return null;
+ // Drawing-linked annotations highlight the stroke via canvas, not an element box
+ if (hoveredAnnotation.drawingIndex != null) return null;
// Render individual element boxes if available (cmd+shift+click multi-select)
if (hoveredAnnotation.elementBoundingBoxes?.length) {
@@ -3984,7 +4920,7 @@ export function PageFeedbackToolbarCSS({
})()}
{/* Hover tooltip */}
- {hoverInfo && !pendingAnnotation && !isScrolling && !isDragging && (
+ {hoverInfo && !pendingAnnotation && !isScrolling && !isDragging && !isDrawMode && (
- {/* Show element/area outline while adding annotation */}
- {pendingAnnotation.multiSelectElements?.length
+ {/* Show element/area outline while adding annotation (skip for drawing-linked annotations — drawing is the highlight) */}
+ {pendingAnnotation.drawingIndex != null
+ ? null
+ : pendingAnnotation.multiSelectElements?.length
? // Cmd+shift+click multi-select: show individual boxes with live positions
pendingAnnotation.multiSelectElements
.filter((el) => document.contains(el))
@@ -4140,8 +5078,10 @@ export function PageFeedbackToolbarCSS({
{/* Edit annotation popup */}
{editingAnnotation && (
<>
- {/* Show element/area outline while editing */}
- {editingAnnotation.elementBoundingBoxes?.length
+ {/* Show element/area outline while editing (skip for drawing-linked — drawing is the highlight) */}
+ {editingAnnotation.drawingIndex != null
+ ? null
+ : editingAnnotation.elementBoundingBoxes?.length
? // Cmd+shift+click: show individual element boxes (use live rects when available)
(() => {
// Use live positions from editingTargetElements when available
diff --git a/package/src/components/page-toolbar-css/styles.module.scss b/package/src/components/page-toolbar-css/styles.module.scss
index ea5102ee..56ab62b8 100644
--- a/package/src/components/page-toolbar-css/styles.module.scss
+++ b/package/src/components/page-toolbar-css/styles.module.scss
@@ -134,6 +134,17 @@ $green: #34c759;
}
}
+@keyframes tooltipOut {
+ from {
+ opacity: 1;
+ transform: translateX(-50%) translateY(0) scale(0.909);
+ }
+ to {
+ opacity: 0;
+ transform: translateX(-50%) translateY(2px) scale(0.891);
+ }
+}
+
@keyframes hoverHighlightIn {
from {
opacity: 0;
@@ -190,7 +201,7 @@ $green: #34c759;
position: fixed;
bottom: 1.25rem;
right: 1.25rem;
- width: 297px;
+ width: 337px;
z-index: 100000;
font-family:
system-ui,
@@ -262,10 +273,10 @@ $green: #34c759;
height: 44px;
border-radius: 1.5rem;
padding: 0.375rem;
- width: 257px;
+ width: 297px;
&.serverConnected {
- width: 297px;
+ width: 337px;
}
}
}
@@ -1007,6 +1018,10 @@ $green: #34c759;
&.enter {
animation: tooltipIn 0.1s ease-out forwards;
}
+
+ &.exit {
+ animation: tooltipOut 0.1s ease-out forwards;
+ }
}
.markerQuote {
@@ -2045,6 +2060,27 @@ $green: #34c759;
}
}
+// =============================================================================
+// Draw Canvas
+// =============================================================================
+
+.drawCanvas {
+ position: fixed;
+ inset: 0;
+ z-index: 99996; // Below markers (99998) and overlay (99997)
+ // !important needed to override .overlay > * { pointer-events: auto }
+ pointer-events: none !important;
+
+ &.active {
+ pointer-events: auto !important;
+ cursor: crosshair !important;
+
+ &[data-stroke-hover] {
+ cursor: pointer !important;
+ }
+ }
+}
+
// =============================================================================
// Drag Selection
// =============================================================================
diff --git a/package/src/process-shim.ts b/package/src/process-shim.ts
new file mode 100644
index 00000000..1d85fb26
--- /dev/null
+++ b/package/src/process-shim.ts
@@ -0,0 +1,11 @@
+const browserGlobal = globalThis as Record;
+
+if (typeof browserGlobal.process === "undefined") {
+ browserGlobal.process = {
+ env: {
+ NODE_ENV: "production",
+ },
+ };
+}
+
+export {};
diff --git a/package/src/types.ts b/package/src/types.ts
index 08b6ec6c..265d2088 100644
--- a/package/src/types.ts
+++ b/package/src/types.ts
@@ -27,6 +27,8 @@ export type Annotation = {
width: number;
height: number;
}>; // Individual bounding boxes for multi-select hover highlighting
+ drawingIndex?: number; // Index of linked drawing stroke (click-to-annotate)
+ strokeId?: string; // Unique ID of linked drawing stroke
// Protocol fields (added when syncing to server)
sessionId?: string;
diff --git a/package/tsup.config.ts b/package/tsup.config.ts
index af6833c8..a7546c0e 100644
--- a/package/tsup.config.ts
+++ b/package/tsup.config.ts
@@ -82,22 +82,79 @@ export default {};
};
}
-export default defineConfig((options) => [
- // React component
- {
- entry: ["src/index.ts"],
- format: ["cjs", "esm"],
- dts: true,
- splitting: false,
- sourcemap: true,
- clean: !options.watch,
- external: ["react", "react-dom"],
- esbuildPlugins: [scssModulesPlugin()],
- define: {
- __VERSION__: JSON.stringify(VERSION),
+export default defineConfig((options) => {
+ if (!options.watch) {
+ fs.rmSync("dist", { recursive: true, force: true });
+ }
+
+ return [
+ // React component
+ {
+ entry: ["src/index.ts"],
+ format: ["cjs", "esm"],
+ dts: true,
+ splitting: false,
+ sourcemap: true,
+ clean: false,
+ external: ["react", "react-dom"],
+ esbuildPlugins: [scssModulesPlugin()],
+ define: {
+ __VERSION__: JSON.stringify(VERSION),
+ "process.env.NODE_ENV": JSON.stringify("production"),
+ },
+ banner: {
+ js: '"use client";',
+ },
},
- banner: {
- js: '"use client";',
+ // Browser module API
+ {
+ entry: {
+ browser: "src/browser.tsx",
+ },
+ format: ["cjs", "esm"],
+ dts: true,
+ splitting: false,
+ sourcemap: true,
+ clean: false,
+ esbuildPlugins: [scssModulesPlugin()],
+ define: {
+ __VERSION__: JSON.stringify(VERSION),
+ "process.env.NODE_ENV": JSON.stringify("production"),
+ },
+ banner: {
+ js: '"use client";',
+ },
+ },
+ // Script-tag bundle
+ {
+ entry: {
+ agentation: "src/browser.tsx",
+ },
+ format: ["iife"],
+ globalName: "Agentation",
+ dts: false,
+ splitting: false,
+ sourcemap: false,
+ minify: true,
+ clean: false,
+ esbuildPlugins: [scssModulesPlugin()],
+ define: {
+ __VERSION__: JSON.stringify(VERSION),
+ "process.env.NODE_ENV": JSON.stringify("production"),
+ },
+ outExtension({ format }) {
+ if (format === "iife") {
+ return {
+ js: ".browser.min.js",
+ };
+ }
+ return {
+ js: ".js",
+ };
+ },
+ banner: {
+ js: '"use client";',
+ },
},
- },
-]);
+ ];
+});