@agent-native/pinpoint 0.1.1 → 0.1.4

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/README.md CHANGED
@@ -234,6 +234,7 @@ All options can be passed as props to `<Pinpoint />` or as the config object to
234
234
  | `outputFormat` | `'compact' \| 'standard' \| 'detailed'` | `'standard'` | Detail level in agent output |
235
235
  | `autoSubmit` | `boolean` | `true` | Auto-submit annotations to agent chat |
236
236
  | `clearOnSend` | `boolean` | `false` | Clear pins after sending |
237
+ | `sendToAgent` | `(output) => void \| Promise<void>` | — | Custom bridge for annotation delivery |
237
238
  | `blockInteractions` | `boolean` | `false` | Block page clicks during selection |
238
239
  | `compactPopup` | `boolean` | `true` | Hide technical details behind toggle |
239
240
  | `freezeJSTimers` | `boolean` | `false` | Freeze JS timers during selection |
@@ -395,6 +396,9 @@ Inside [Builder.io's Fusion](https://builder.io), annotations are sent via `send
395
396
 
396
397
  No additional configuration needed when running inside a Builder.io project.
397
398
 
399
+ Hosts with their own chat implementation can pass `sendToAgent` to reuse Pinpoint's pin,
400
+ draw, queue, and prompt UI while delivering `{ message, context, submit }` themselves.
401
+
398
402
  ## Architecture
399
403
 
400
404
  - **SolidJS overlay** in **Shadow DOM** — zero interference with host app styles or React reconciliation
@@ -3,11 +3,11 @@ import {
3
3
  formatPinsForAgent,
4
4
  formatQueueForAgent,
5
5
  formatRichPinContext
6
- } from "./chunk-EPXBFDY6.js";
6
+ } from "./chunk-C5OZ7ZT5.js";
7
7
  import "./chunk-BB7X7W3H.js";
8
8
  export {
9
9
  formatPinsForAgent,
10
10
  formatQueueForAgent,
11
11
  formatRichPinContext
12
12
  };
13
- //# sourceMappingURL=agent-context-76ZW6ODH.js.map
13
+ //# sourceMappingURL=agent-context-OICFLRJ7.js.map
@@ -85,4 +85,4 @@ export {
85
85
  formatPinsForAgent,
86
86
  formatQueueForAgent
87
87
  };
88
- //# sourceMappingURL=chunk-EPXBFDY6.js.map
88
+ //# sourceMappingURL=chunk-C5OZ7ZT5.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/output/agent-context.ts"],"sourcesContent":["// @agent-native/pinpoint — Split output for sendToAgentChat()\n// MIT License\n//\n// Splits annotation output into { message, context } for the agent chat bridge.\n// The message is shown in chat UI. The context is hidden, appended for the agent.\n\nimport type {\n AgentOutput,\n Pin,\n OutputFormat,\n QueuedAnnotation,\n} from \"../types/index.js\";\nimport { formatPins } from \"./formatter.js\";\n\nexport type { AgentOutput } from \"../types/index.js\";\n\n/**\n * Format a single pin into rich context for the agent.\n *\n * ```\n * [Annotation on <button class=\"primary\"> in <Header> component]\n * Comment: \"This button should be blue instead of gray\"\n * Element: button.primary at (120, 45)\n * Source: src/components/Header.tsx:42\n * ```\n */\nexport function formatRichPinContext(pin: Pin): string {\n const lines: string[] = [];\n\n // Build element descriptor\n const tagName = pin.element.tagName.toLowerCase();\n const classes =\n pin.element.classNames.length > 0\n ? ` class=\"${pin.element.classNames.join(\" \")}\"`\n : \"\";\n const component = pin.framework?.componentPath\n ? ` in ${pin.framework.componentPath} component`\n : \"\";\n\n lines.push(`[Annotation on <${tagName}${classes}>${component}]`);\n lines.push(`Comment: \"${pin.comment}\"`);\n\n const rect = pin.element.boundingRect;\n const classStr =\n pin.element.classNames.length > 0\n ? `.${pin.element.classNames.join(\".\")}`\n : \"\";\n lines.push(\n `Element: ${tagName}${classStr} at (${Math.round(rect.x)}, ${Math.round(rect.y)})`,\n );\n\n if (pin.framework?.sourceFile) {\n lines.push(`Source: ${pin.framework.sourceFile}`);\n }\n\n if (pin.element.textContent) {\n const truncated = pin.element.textContent.slice(0, 80);\n lines.push(\n `Text: \"${truncated}${pin.element.textContent.length > 80 ? \"...\" : \"\"}\"`,\n );\n }\n\n return lines.join(\"\\n\");\n}\n\n/**\n * Format pins for agent chat.\n * The full formatted output goes into message (visible in chat UI) so the user\n * can see exactly what context the agent is working with.\n */\nexport function formatPinsForAgent(\n pins: Pin[],\n format: OutputFormat = \"standard\",\n): AgentOutput {\n if (pins.length === 0) {\n return { message: \"No annotations to send.\", context: \"\" };\n }\n\n // Use rich context format for each pin\n const richAnnotations = pins\n .map((pin) => formatRichPinContext(pin))\n .join(\"\\n\\n\");\n\n const instruction = `The user has annotated ${pins.length} element${pins.length === 1 ? \"\" : \"s\"} on the page with visual feedback. Review each annotation and make the requested changes.\\n\\n`;\n\n // Also include the structured format as context for the agent\n const details = formatPins(pins, format);\n const message = instruction + richAnnotations;\n\n return { message, context: details };\n}\n\n/**\n * Format queued annotations for batch sending.\n */\nexport function formatQueueForAgent(\n queue: QueuedAnnotation[],\n format: OutputFormat = \"standard\",\n): AgentOutput {\n if (queue.length === 0) {\n return { message: \"No queued annotations to send.\", context: \"\" };\n }\n\n const parts: string[] = [];\n const pins: Pin[] = [];\n\n parts.push(\n `The user has queued ${queue.length} annotation${queue.length === 1 ? \"\" : \"s\"} for batch review. Process each one:\\n`,\n );\n\n for (let i = 0; i < queue.length; i++) {\n const item = queue[i];\n parts.push(`--- Item ${i + 1} ---`);\n\n if (item.pin) {\n parts.push(formatRichPinContext(item.pin));\n pins.push(item.pin);\n }\n\n if (item.drawings && item.drawings.length > 0) {\n parts.push(`[Drawing: ${item.drawings.length} stroke(s) on the page]`);\n for (const stroke of item.drawings) {\n const startPt = stroke.points[0];\n const endPt = stroke.points[stroke.points.length - 1];\n parts.push(\n ` ${stroke.type} from (${Math.round(startPt.x)}, ${Math.round(startPt.y)}) to (${Math.round(endPt.x)}, ${Math.round(endPt.y)}) [${stroke.color}]`,\n );\n }\n }\n\n if (item.textNotes && item.textNotes.length > 0) {\n for (const note of item.textNotes) {\n parts.push(\n `[Text note at (${Math.round(note.x)}, ${Math.round(note.y)}): \"${note.text}\"]`,\n );\n }\n }\n\n parts.push(\"\");\n }\n\n const message = parts.join(\"\\n\");\n const context = pins.length > 0 ? formatPins(pins, format) : \"\";\n\n return { message, context };\n}\n"],"mappings":"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","names":[]}
@@ -4076,6 +4076,24 @@ var PinpointApp = (props) => {
4076
4076
  const [blockInteractions, setBlockInteractions] = createSignal(props.config.blockInteractions ?? false);
4077
4077
  const [autoSubmit, setAutoSubmit] = createSignal(props.config.autoSubmit ?? true);
4078
4078
  const [compactPopup, setCompactPopup] = createSignal(props.config.compactPopup ?? true);
4079
+ async function deliverToAgent(output) {
4080
+ const agentOutput = {
4081
+ ...output,
4082
+ submit: output.submit ?? autoSubmit()
4083
+ };
4084
+ if (props.config.sendToAgent) {
4085
+ await props.config.sendToAgent(agentOutput);
4086
+ return;
4087
+ }
4088
+ try {
4089
+ const {
4090
+ sendToAgentChat
4091
+ } = await import("@agent-native/core/client");
4092
+ sendToAgentChat(agentOutput);
4093
+ } catch {
4094
+ await navigator.clipboard.writeText([agentOutput.message, agentOutput.context].filter(Boolean).join("\n\n"));
4095
+ }
4096
+ }
4079
4097
  const storage = props.config.storage || (props.config.endpoint ? new RestClient(props.config.endpoint) : new MemoryStore());
4080
4098
  const picker = new ElementPicker({
4081
4099
  ignoreSelector: "#pinpoint-root, [data-pinpoint-marker]",
@@ -4353,25 +4371,15 @@ var PinpointApp = (props) => {
4353
4371
  if (items.length === 0) return;
4354
4372
  const {
4355
4373
  formatQueueForAgent
4356
- } = await import("./agent-context-76ZW6ODH.js");
4374
+ } = await import("./agent-context-OICFLRJ7.js");
4357
4375
  const {
4358
4376
  message,
4359
4377
  context
4360
4378
  } = formatQueueForAgent(items, outputFormat());
4361
- try {
4362
- const {
4363
- sendToAgentChat
4364
- } = await import("@agent-native/core/client");
4365
- sendToAgentChat({
4366
- message,
4367
- context,
4368
- submit: autoSubmit()
4369
- });
4370
- } catch {
4371
- await navigator.clipboard.writeText(`${message}
4372
-
4373
- ${context}`);
4374
- }
4379
+ await deliverToAgent({
4380
+ message,
4381
+ context
4382
+ });
4375
4383
  setQueue([]);
4376
4384
  if (clearOnSend()) {
4377
4385
  const pageUrl = window.location.pathname;
@@ -4399,25 +4407,15 @@ ${context}`);
4399
4407
  const selected = pins().filter((p) => ids.has(p.id));
4400
4408
  const {
4401
4409
  formatPinsForAgent
4402
- } = await import("./agent-context-76ZW6ODH.js");
4410
+ } = await import("./agent-context-OICFLRJ7.js");
4403
4411
  const {
4404
4412
  message,
4405
4413
  context
4406
4414
  } = formatPinsForAgent(selected, outputFormat());
4407
- try {
4408
- const {
4409
- sendToAgentChat
4410
- } = await import("@agent-native/core/client");
4411
- sendToAgentChat({
4412
- message,
4413
- context,
4414
- submit: autoSubmit()
4415
- });
4416
- } catch {
4417
- await navigator.clipboard.writeText(`${message}
4418
-
4419
- ${context}`);
4420
- }
4415
+ await deliverToAgent({
4416
+ message,
4417
+ context
4418
+ });
4421
4419
  setSelectedPinIds(/* @__PURE__ */ new Set());
4422
4420
  }
4423
4421
  function toggleActive() {
@@ -4501,20 +4499,12 @@ ${context}`);
4501
4499
  const pin = addPin(el, comment);
4502
4500
  const {
4503
4501
  formatRichPinContext
4504
- } = await import("./agent-context-76ZW6ODH.js");
4502
+ } = await import("./agent-context-OICFLRJ7.js");
4505
4503
  const richMessage = `Please fix: ${formatRichPinContext(pin)}`;
4506
- try {
4507
- const {
4508
- sendToAgentChat
4509
- } = await import("@agent-native/core/client");
4510
- sendToAgentChat({
4511
- message: richMessage,
4512
- context: "",
4513
- submit: autoSubmit()
4514
- });
4515
- } catch {
4516
- await navigator.clipboard.writeText(richMessage);
4517
- }
4504
+ await deliverToAgent({
4505
+ message: richMessage,
4506
+ context: ""
4507
+ });
4518
4508
  }
4519
4509
  function openEditPopup(pin) {
4520
4510
  setShowPopup(false);
@@ -4552,25 +4542,15 @@ ${context}`);
4552
4542
  async function sendPins() {
4553
4543
  const {
4554
4544
  formatPinsForAgent
4555
- } = await import("./agent-context-76ZW6ODH.js");
4545
+ } = await import("./agent-context-OICFLRJ7.js");
4556
4546
  const {
4557
4547
  message,
4558
4548
  context
4559
4549
  } = formatPinsForAgent(pins(), outputFormat());
4560
- try {
4561
- const {
4562
- sendToAgentChat
4563
- } = await import("@agent-native/core/client");
4564
- sendToAgentChat({
4565
- message,
4566
- context,
4567
- submit: autoSubmit()
4568
- });
4569
- } catch {
4570
- await navigator.clipboard.writeText(`${message}
4571
-
4572
- ${context}`);
4573
- }
4550
+ await deliverToAgent({
4551
+ message,
4552
+ context
4553
+ });
4574
4554
  if (clearOnSend()) {
4575
4555
  const pageUrl = window.location.pathname;
4576
4556
  await storage.clear(pageUrl);
@@ -4807,18 +4787,11 @@ ${context}`);
4807
4787
  return selectedElement();
4808
4788
  },
4809
4789
  onSend: async (instruction) => {
4810
- try {
4811
- const {
4812
- sendToAgentChat
4813
- } = await import("@agent-native/core/client");
4814
- const context = buildElementContext(selectedElement());
4815
- sendToAgentChat({
4816
- message: instruction,
4817
- context: JSON.stringify(context, null, 2),
4818
- submit: autoSubmit()
4819
- });
4820
- } catch {
4821
- }
4790
+ const context = buildElementContext(selectedElement());
4791
+ await deliverToAgent({
4792
+ message: instruction,
4793
+ context: JSON.stringify(context, null, 2)
4794
+ });
4822
4795
  setShowPrompt(false);
4823
4796
  },
4824
4797
  onCancel: () => setShowPrompt(false)
@@ -4894,4 +4867,4 @@ export {
4894
4867
  mountPinpoint,
4895
4868
  unmountPinpoint
4896
4869
  };
4897
- //# sourceMappingURL=chunk-5OW42OKO.js.map
4870
+ //# sourceMappingURL=chunk-PQIWC4FE.js.map