safari-mcp 2.10.3 → 2.10.5

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/safari.js +22 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "safari-mcp",
3
- "version": "2.10.3",
3
+ "version": "2.10.5",
4
4
  "mcpName": "io.github.achiya-automation/safari-mcp",
5
5
  "description": "Safari browser automation for AI agents — native macOS, zero Chrome overhead. 80 tools via AppleScript + JavaScript.",
6
6
  "type": "module",
package/safari.js CHANGED
@@ -1446,7 +1446,11 @@ export async function fill({ selector, value, ref }) {
1446
1446
  `if(isClosure){return '__CLOSURE_NATIVE_PASTE__';}` +
1447
1447
  // Synthetic ClipboardEvent paste — works on ProseMirror, TipTap, Slate, and most modern editors
1448
1448
  // that don't respond to execCommand but DO handle paste events
1449
- `try{el.focus();var sel2=window.getSelection();if(sel2.rangeCount){var rng=document.createRange();rng.selectNodeContents(el);sel2.removeAllRanges();sel2.addRange(rng);}var dt=new DataTransfer();dt.setData('text/plain','${val}');var pe=new ClipboardEvent('paste',{bubbles:true,cancelable:true,clipboardData:dt});var handled=!el.dispatchEvent(pe);if(handled||el.textContent.indexOf('${val.substring(0, 20)}')>=0){return 'Filled CE (synthetic paste)';}}catch(ep){}` +
1449
+ // Pre-clear: select all + delete BEFORE paste. Some editors (X's tweetTextarea_0
1450
+ // when it has URL-prefilled content from /intent/post?text=, Quill in some configs)
1451
+ // append paste content to selection instead of replacing — explicit delete avoids
1452
+ // the duplication seen when the textarea was pre-populated by URL parameters.
1453
+ `try{el.focus();var sel2=window.getSelection();if(sel2.rangeCount){var rng=document.createRange();rng.selectNodeContents(el);sel2.removeAllRanges();sel2.addRange(rng);document.execCommand('delete',false,null);}var dt=new DataTransfer();dt.setData('text/plain','${val}');var pe=new ClipboardEvent('paste',{bubbles:true,cancelable:true,clipboardData:dt});var handled=!el.dispatchEvent(pe);if(handled||el.textContent.indexOf('${val.substring(0, 20)}')>=0){return 'Filled CE (synthetic paste)';}}catch(ep){}` +
1450
1454
  // Synthetic paste did not verify — in a dialog, route to native paste (CGEvent Cmd+V)
1451
1455
  // to produce real isTrusted:true events. Avoids LinkedIn-style dialog dismissal too.
1452
1456
  `if(!!el.closest('[role="dialog"]')){return '__NATIVE_PASTE_DIALOG__';}` +
@@ -1505,6 +1509,23 @@ export async function fill({ selector, value, ref }) {
1505
1509
  `el.focus();el.click();` +
1506
1510
  `var t=document.activeElement||el;` +
1507
1511
  charInserts +
1512
+ // Verification: count actual text length vs expected. If char-by-char dropped
1513
+ // intermediate paragraphs (Hashnode-style Tiptap with markdown-like chars at
1514
+ // line starts: `>`, `**`, `[`), fall back to execCommand('insertHTML') with
1515
+ // paragraph-wrapped HTML.
1516
+ `var actualLen=(el.innerText||'').length;` +
1517
+ `var expectedLen=${value.length};` +
1518
+ `if(actualLen<expectedLen*0.6){` +
1519
+ // Clear and re-fill via insertHTML
1520
+ `var sel=window.getSelection();var r=document.createRange();r.selectNodeContents(el);sel.removeAllRanges();sel.addRange(r);` +
1521
+ `document.execCommand('delete',false,null);` +
1522
+ // Build paragraph HTML from the original value
1523
+ `var paras=${JSON.stringify(value)}.split(/\\n\\n+/).filter(function(p){return p.trim().length>0;});` +
1524
+ `var html=paras.map(function(p){var safe=p.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/\\n/g,'<br>');return '<p>'+safe+'</p>';}).join('');` +
1525
+ `el.focus();var sel2=window.getSelection();var r2=document.createRange();r2.selectNodeContents(el);r2.collapse(true);sel2.removeAllRanges();sel2.addRange(r2);` +
1526
+ `document.execCommand('insertHTML',false,html);` +
1527
+ `return 'Filled CE (ProseMirror insertHTML fallback, '+(el.innerText||'').length+'/'+expectedLen+')';` +
1528
+ `}` +
1508
1529
  `return 'Filled CE (ProseMirror char-by-char)';` +
1509
1530
  `})()`
1510
1531
  );