instruckt 0.4.8 → 0.4.13

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
@@ -41,10 +41,12 @@ Or with the IIFE build:
41
41
  2. Press **A** or click the annotate button to enter annotation mode
42
42
  3. Hover over any element — instruckt highlights it and detects its framework component
43
43
  4. Click to annotate — type your feedback and save
44
- 5. Annotations auto-copy as structured markdown to your clipboard
44
+ 5. Annotations auto-copy as structured markdown to your clipboard (requires secure context — `https://` or `localhost`)
45
45
  6. Paste into any AI coding agent (Claude Code, Cursor, Codex, Copilot, OpenCode, etc.)
46
46
  7. The agent reads the markdown and makes the requested code changes
47
47
 
48
+ > **Note:** Auto-copy requires a secure context (`https://` or `localhost`). On `http://` domains (e.g. `.test`), use the copy button in the toolbar instead.
49
+
48
50
  ### Example Output
49
51
 
50
52
  ```markdown
@@ -50,7 +50,8 @@ function getCsrfToken() {
50
50
  function headers() {
51
51
  const h = {
52
52
  "Content-Type": "application/json",
53
- Accept: "application/json"
53
+ Accept: "application/json",
54
+ "X-Requested-With": "XMLHttpRequest"
54
55
  };
55
56
  const csrf = getCsrfToken();
56
57
  if (csrf) h["X-XSRF-TOKEN"] = csrf;
@@ -225,6 +226,24 @@ var TOOLBAR_CSS = (
225
226
  box-shadow: var(--ik-shadow);
226
227
  border-radius: 8px;
227
228
  }
229
+ /* Instant tooltip */
230
+ .clear-all-btn::before {
231
+ content: attr(data-tooltip);
232
+ position: absolute;
233
+ right: calc(100% + 6px);
234
+ top: 50%;
235
+ transform: translateY(-50%);
236
+ white-space: nowrap;
237
+ font-size: 11px;
238
+ padding: 4px 8px;
239
+ border-radius: 6px;
240
+ background: var(--ik-text);
241
+ color: var(--ik-bg);
242
+ pointer-events: none;
243
+ opacity: 0;
244
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
245
+ }
246
+ .clear-all-btn:hover::before { opacity: 1; }
228
247
  /* Invisible bridge so hover doesn't break crossing the gap */
229
248
  .clear-all-btn::after {
230
249
  content: '';
@@ -511,13 +530,15 @@ var Toolbar = class {
511
530
  clearBtn.classList.add("danger-btn");
512
531
  const clearAllBtn = this.makeBtn(
513
532
  `<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10 10-4.5 10-10S17.5 2 12 2"/><line x1="4.93" y1="4.93" x2="19.07" y2="19.07"/></svg>`,
514
- "Clear ALL annotations across every page",
533
+ "Delete all instructions.",
515
534
  () => {
516
535
  var _a2, _b;
517
536
  return (_b = (_a2 = this.callbacks).onClearAll) == null ? void 0 : _b.call(_a2);
518
537
  }
519
538
  );
520
539
  clearAllBtn.classList.add("danger-btn", "clear-all-btn");
540
+ clearAllBtn.removeAttribute("title");
541
+ clearAllBtn.setAttribute("data-tooltip", "Delete all instructions.");
521
542
  clearWrap.appendChild(clearBtn);
522
543
  clearWrap.appendChild(clearAllBtn);
523
544
  const minimizeBtn = this.makeBtn(ICONS.minimize, "Minimize toolbar", () => {
@@ -760,6 +781,7 @@ var AnnotationPopup = class {
760
781
  submitBtn.disabled = textarea.value.trim().length === 0;
761
782
  });
762
783
  textarea.addEventListener("keydown", (e) => {
784
+ e.stopPropagation();
763
785
  if (e.key === "Enter" && !e.shiftKey) {
764
786
  e.preventDefault();
765
787
  if (!submitBtn.disabled) submitBtn.click();
@@ -820,6 +842,7 @@ var AnnotationPopup = class {
820
842
  const saveBtn = popup.querySelector('[data-action="save"]');
821
843
  const deleteBtn = popup.querySelector('[data-action="delete"]');
822
844
  textarea.addEventListener("keydown", (e) => {
845
+ e.stopPropagation();
823
846
  if (e.key === "Enter" && !e.shiftKey) {
824
847
  e.preventDefault();
825
848
  saveBtn.click();
@@ -846,9 +869,9 @@ var AnnotationPopup = class {
846
869
  textarea.setSelectionRange(textarea.value.length, textarea.value.length);
847
870
  }
848
871
  // ── Helpers ───────────────────────────────────────────────────
849
- /** Prevent popup interactions from reaching page handlers (e.g. @click.outside) */
872
+ /** Prevent popup interactions from reaching page handlers (e.g. @click.outside, form submit) */
850
873
  stopHostPropagation(host) {
851
- for (const evt of ["click", "mousedown", "pointerdown"]) {
874
+ for (const evt of ["click", "mousedown", "pointerdown", "keydown", "keyup", "keypress", "submit"]) {
852
875
  host.addEventListener(evt, (e) => e.stopPropagation());
853
876
  }
854
877
  }
@@ -1262,6 +1285,13 @@ var _Instruckt = class _Instruckt {
1262
1285
  if (this.highlightLocked) return;
1263
1286
  (_a = this.highlight) == null ? void 0 : _a.hide();
1264
1287
  };
1288
+ /** Block mousedown/pointerdown in annotation mode so SPA frameworks can't navigate */
1289
+ this.boundAnnotateBlock = (e) => {
1290
+ if (this.isInstruckt(e.target)) return;
1291
+ e.preventDefault();
1292
+ e.stopPropagation();
1293
+ e.stopImmediatePropagation();
1294
+ };
1265
1295
  this.boundClick = (e) => {
1266
1296
  var _a, _b, _c, _d;
1267
1297
  const target = e.target;
@@ -1327,7 +1357,7 @@ var _Instruckt = class _Instruckt {
1327
1357
  onFreezeAnimations: (frozen) => {
1328
1358
  this.setFrozen(frozen);
1329
1359
  },
1330
- onCopy: () => this.copyAnnotations(),
1360
+ onCopy: () => this.copyToClipboard(true),
1331
1361
  onClearPage: () => this.clearPage(),
1332
1362
  onClearAll: () => this.clearEverything(),
1333
1363
  onMinimize: (min) => this.onMinimize(min)
@@ -1339,6 +1369,7 @@ var _Instruckt = class _Instruckt {
1339
1369
  window.addEventListener("scroll", this.boundReposition, { passive: true });
1340
1370
  window.addEventListener("resize", this.boundReposition, { passive: true });
1341
1371
  document.addEventListener("livewire:navigated", () => this.reattach());
1372
+ document.addEventListener("inertia:navigate", () => this.syncMarkers());
1342
1373
  window.addEventListener("popstate", () => {
1343
1374
  setTimeout(() => this.reattach(), 0);
1344
1375
  });
@@ -1354,7 +1385,7 @@ var _Instruckt = class _Instruckt {
1354
1385
  onFreezeAnimations: (frozen) => {
1355
1386
  this.setFrozen(frozen);
1356
1387
  },
1357
- onCopy: () => this.copyAnnotations(),
1388
+ onCopy: () => this.copyToClipboard(true),
1358
1389
  onClearPage: () => this.clearPage(),
1359
1390
  onClearAll: () => this.clearEverything(),
1360
1391
  onMinimize: (min) => this.onMinimize(min)
@@ -1590,11 +1621,17 @@ var _Instruckt = class _Instruckt {
1590
1621
  attachAnnotateListeners() {
1591
1622
  document.addEventListener("mousemove", this.boundMouseMove);
1592
1623
  document.addEventListener("mouseleave", this.boundMouseLeave);
1624
+ for (const evt of ["mousedown", "pointerdown"]) {
1625
+ window.addEventListener(evt, this.boundAnnotateBlock, true);
1626
+ }
1593
1627
  window.addEventListener("click", this.boundClick, true);
1594
1628
  }
1595
1629
  detachAnnotateListeners() {
1596
1630
  document.removeEventListener("mousemove", this.boundMouseMove);
1597
1631
  document.removeEventListener("mouseleave", this.boundMouseLeave);
1632
+ for (const evt of ["mousedown", "pointerdown"]) {
1633
+ window.removeEventListener(evt, this.boundAnnotateBlock, true);
1634
+ }
1598
1635
  window.removeEventListener("click", this.boundClick, true);
1599
1636
  }
1600
1637
  isInstruckt(el) {
@@ -1740,17 +1777,28 @@ var _Instruckt = class _Instruckt {
1740
1777
  }
1741
1778
  }
1742
1779
  // ── Copy / export ─────────────────────────────────────────────
1780
+ /** Auto-copy on annotation submit — only in secure contexts to avoid focus side-effects */
1743
1781
  copyAnnotations() {
1782
+ this.copyToClipboard(false);
1783
+ }
1784
+ /** Copy to clipboard. With fallback=true, uses execCommand for non-secure contexts (user-initiated only). */
1785
+ copyToClipboard(fallback) {
1744
1786
  const md = this.exportMarkdown();
1745
- navigator.clipboard.writeText(md).catch(() => {
1746
- const el = document.createElement("textarea");
1747
- el.value = md;
1748
- el.style.cssText = "position:fixed;left:-9999px";
1749
- document.body.appendChild(el);
1750
- el.select();
1751
- document.execCommand("copy");
1752
- el.remove();
1753
- });
1787
+ if (window.isSecureContext) {
1788
+ navigator.clipboard.writeText(md).catch(() => {
1789
+ });
1790
+ } else if (fallback) {
1791
+ try {
1792
+ const el = document.createElement("textarea");
1793
+ el.value = md;
1794
+ el.style.cssText = "position:fixed;left:-9999px";
1795
+ document.body.appendChild(el);
1796
+ el.select();
1797
+ document.execCommand("copy");
1798
+ el.remove();
1799
+ } catch (e) {
1800
+ }
1801
+ }
1754
1802
  }
1755
1803
  exportMarkdown() {
1756
1804
  const pending = this.annotations.filter((a) => a.status !== "resolved" && a.status !== "dismissed");