@emmaexcel/shakecursor 0.1.3 → 0.1.6

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.
@@ -1,12 +1,10 @@
1
1
  (function(e){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});function t(e,t,n){return Math.min(Math.max(e,t),n)}function n(e){return e.replace(/\s+/g,` `).trim()}function r(e,t=[]){return t.some(t=>{try{return e.matches(t)||!!e.closest(t)}catch{return!1}})}function i(){return/Mac|iPhone|iPad|iPod/i.test(window.navigator.platform)}function a(e){if(e.rangeCount===0)return null;let t=e.getRangeAt(0),n=t.getBoundingClientRect();return n.width>0||n.height>0?n:t.getClientRects()[0]??null}function o(e){return{rect:e,url:window.location.href,title:document.title}}function s(e){let t=n(e.innerText??``),r=e.tagName.toLowerCase(),i=e.getAttribute(`aria-label`),a=e.getAttribute(`title`),o=[i,a,t].find(Boolean);return{label:o?`${r}: ${o.slice(0,64)}`:r,data:void 0,mimeType:void 0,content:[`Selected element: <${r}>`,i?`ARIA label: ${i}`:``,a?`Title: ${a}`:``,t?`Visible text: ${t.slice(0,5e3)}`:``].filter(Boolean).join(`
2
2
  `)}}function c(e){let t=e.currentSrc||e.src,n=e.alt||`No alt text`,r,i;try{let t=document.createElement(`canvas`);t.width=e.naturalWidth,t.height=e.naturalHeight;let n=t.getContext(`2d`);if(n){n.drawImage(e,0,0);let[a,o]=t.toDataURL(`image/jpeg`,.8).split(`,`);r=o,i=a.split(`:`)[1].split(`;`)[0]}}catch(e){console.warn(`Failed to capture image data (likely CORS):`,e)}return{label:`image: ${n.slice(0,64)}`,data:r,mimeType:i,content:[`Selected image`,`Alt text: ${n}`,`Source: ${t}`,`Rendered size: ${Math.round(e.width)}x${Math.round(e.height)}`,`Natural size: ${e.naturalWidth}x${e.naturalHeight}`].join(`
3
- `)}}function l(e,t){return!(r(e,t.blockedSelectors)||t.allowedSelectors.length>0&&!r(e,t.allowedSelectors))}function u(e){let t=t=>{if(!e.isActive())return;let n=t.target;if(!(n instanceof HTMLElement)||e.isOverlayElement(n)){e.onHover(null);return}if(!l(n,e.config)){e.onHover(null);return}e.onHover(n.getBoundingClientRect())},r=()=>{!e.isActive()||!e.config.text||window.setTimeout(()=>{let t=window.getSelection(),r=n(t?.toString()??``);if(!t||!r)return;let i=a(t),s=t.anchorNode?.parentElement;!i||!s||!l(s,e.config)||e.onSelection({kind:`text`,label:`highlighted text`,content:r,...o(i)})},0)},i=t=>{if(!e.isActive())return;let r=t.target;if(!(r instanceof HTMLElement)||e.isOverlayElement(r)||!l(r,e.config)||n(window.getSelection()?.toString()??``))return;let i=r instanceof HTMLImageElement?r:r.querySelector(`img`),a=!!i;if(a&&!e.config.images||!a&&!e.config.elements)return;t.preventDefault(),t.stopPropagation();let u=i?c(i):s(r),d=i?i.getBoundingClientRect():r.getBoundingClientRect();e.onSelection({kind:i?`image`:`element`,label:u.label,content:u.content,data:u.data,mimeType:u.mimeType,...o(d)})};return document.addEventListener(`pointerover`,t),document.addEventListener(`mouseup`,r),document.addEventListener(`click`,i,!0),()=>{document.removeEventListener(`pointerover`,t),document.removeEventListener(`mouseup`,r),document.removeEventListener(`click`,i,!0)}}function d(e){let t=e.windowMs??650,n=e.cooldownMs??1e3,r=e.minSamples??7,i=e.minReversals??5,a=e.minDistance??340,o=e.minDeltaX??16,s=[],c=0,l=l=>{let u=performance.now();if(s.push({x:l.clientX,y:l.clientY,time:u}),s=s.filter(e=>u-e.time<t),s.length<r||u-c<n)return;let d=0,f=0,p=0;for(let e=1;e<s.length;e+=1){let t=s[e].x-s[e-1].x,n=s[e].y-s[e-1].y;if(f+=Math.hypot(t,n),Math.abs(t)<o)continue;let r=Math.sign(t);p!==0&&r!==p&&(d+=1),p=r}d>=i&&f>a&&(c=u,s=[],e.onShake())};return document.addEventListener(`pointermove`,l),()=>{document.removeEventListener(`pointermove`,l)}}var f=`http://localhost:11434/api/chat`,p=`qwen3-coder:480b-cloud`,m=`https://shakeai.onrender.com`;async function h(e,t){let n=e.provider??`ollama`,r=e.endpoint??f,i=e.model??p;if(n===`custom`){let n=await fetch(r,{method:`POST`,headers:{"Content-Type":`application/json`,...e.headers},body:JSON.stringify(t)});if(!n.ok)throw Error(`Custom endpoint returned ${n.status}`);let i=await n.json();return typeof i==`string`?i:i&&typeof i==`object`&&`answer`in i&&typeof i.answer==`string`?i.answer:i&&typeof i==`object`&&`content`in i&&typeof i.content==`string`?i.content:JSON.stringify(i,null,2)}let a=await fetch(r,{method:`POST`,headers:{"Content-Type":`application/json`,...e.headers},body:JSON.stringify({model:i,stream:!1,messages:[{role:`system`,content:`You are an in-page AI assistant. Use the selected website context to answer or transform content. Be direct and practical.`},{role:`user`,content:[`Page title: ${t.selection.title}`,`Page URL: ${t.selection.url}`,`Selection type: ${t.selection.kind}`,`Selection label: ${t.selection.label}`,`Selected context:`,t.selection.content,``,`User request: ${t.question}`].join(`
3
+ `)}}function l(e,t){if(r(e,t.blockedSelectors))return!1;let n=e.getBoundingClientRect(),i=window.innerWidth*window.innerHeight;return!(n.width*n.height>i*.5||t.allowedSelectors.length>0&&!r(e,t.allowedSelectors))}function u(e){let t=t=>{if(!e.isActive())return;let n=t.target;if(!(n instanceof HTMLElement)||e.isOverlayElement(n)){e.onHover(null);return}if(!l(n,e.config)){e.onHover(null);return}e.onHover(n.getBoundingClientRect())},r=()=>{!e.isActive()||!e.config.text||window.setTimeout(()=>{let t=window.getSelection(),r=n(t?.toString()??``);if(!t||!r)return;let i=a(t),s=t.anchorNode?.parentElement;!i||!s||!l(s,e.config)||e.onSelection({kind:`text`,label:`highlighted text`,content:r,...o(i)})},0)},i=t=>{if(!e.isActive())return;let r=t.target;if(!(r instanceof HTMLElement)||e.isOverlayElement(r)||!l(r,e.config)||n(window.getSelection()?.toString()??``))return;let i=r instanceof HTMLImageElement?r:r.querySelector(`img`),a=!!i;if(a&&!e.config.images||!a&&!e.config.elements)return;t.preventDefault(),t.stopPropagation();let u=i?c(i):s(r),d=i?i.getBoundingClientRect():r.getBoundingClientRect();e.onSelection({kind:i?`image`:`element`,label:u.label,content:u.content,data:u.data,mimeType:u.mimeType,...o(d)})};return document.addEventListener(`pointerover`,t),document.addEventListener(`mouseup`,r),document.addEventListener(`click`,i,!0),()=>{document.removeEventListener(`pointerover`,t),document.removeEventListener(`mouseup`,r),document.removeEventListener(`click`,i,!0)}}function d(e){let t=e.windowMs??650,n=e.cooldownMs??1e3,r=e.minSamples??7,i=e.minReversals??5,a=e.minDistance??340,o=e.minDeltaX??16,s=[],c=0,l=l=>{let u=performance.now();if(s.push({x:l.clientX,y:l.clientY,time:u}),s=s.filter(e=>u-e.time<t),s.length<r||u-c<n)return;let d=0,f=0,p=0;for(let e=1;e<s.length;e+=1){let t=s[e].x-s[e-1].x,n=s[e].y-s[e-1].y;if(f+=Math.hypot(t,n),Math.abs(t)<o)continue;let r=Math.sign(t);p!==0&&r!==p&&(d+=1),p=r}d>=i&&f>a&&(c=u,s=[],e.onShake())};return document.addEventListener(`pointermove`,l),()=>{document.removeEventListener(`pointermove`,l)}}var f=`http://localhost:11434/api/chat`,p=`qwen3-coder:480b-cloud`,m=`https://shakeai.onrender.com`;async function h(e,t){let n=e.provider??`ollama`,r=e.endpoint??f,i=e.model??p;if(n===`custom`){let n=await fetch(r,{method:`POST`,headers:{"Content-Type":`application/json`,...e.headers},body:JSON.stringify(t)});if(!n.ok)throw Error(`Custom endpoint returned ${n.status}`);let i=await n.json();return typeof i==`string`?i:i&&typeof i==`object`&&`answer`in i&&typeof i.answer==`string`?i.answer:i&&typeof i==`object`&&`content`in i&&typeof i.content==`string`?i.content:JSON.stringify(i,null,2)}let a=await fetch(r,{method:`POST`,headers:{"Content-Type":`application/json`,...e.headers},body:JSON.stringify({model:i,stream:!1,messages:[{role:`system`,content:`You are an in-page AI assistant. Use the selected website context to answer or transform content. Be direct and practical.`},{role:`user`,content:[`Page title: ${t.selection.title}`,`Page URL: ${t.selection.url}`,`Selection type: ${t.selection.kind}`,`Selection label: ${t.selection.label}`,`Selected context:`,t.selection.content,``,`User request: ${t.question}`].join(`
4
4
  `)}]})});if(!a.ok)throw Error(`Ollama returned ${a.status}`);let o=await a.json();if(o.error)throw Error(o.error);return o.message?.content?.trim()||`No response returned.`}async function g(e){let t=await fetch(`${e.apiBaseUrl??m}/v1/ask`,{method:`POST`,headers:{"Content-Type":`application/json`,"x-site-key":e.siteKey},body:JSON.stringify(e.payload)}),n=await t.json();if(!t.ok||n.error)throw Error(n.error??`Hosted API returned ${t.status}`);return n.answer??`No response returned.`}function _(e){let n=document.createElement(`div`);n.dataset.aiOverlayRoot=`true`;let r=document.createElement(`style`);r.dataset.aiOverlayStyle=`true`,r.textContent=`
5
- body.ai-overlay-active {
6
- cursor: none !important;
7
- }
8
- body.ai-overlay-active * {
9
- cursor: none !important;
5
+ body.ai-overlay-active,
6
+ body.ai-overlay-active * {
7
+ cursor: none !important;
10
8
  }
11
9
  `;let i=n.attachShadow({mode:`open`});document.head.append(r),document.body.append(n),i.innerHTML=`
12
10
  <style>
@@ -27,27 +25,29 @@
27
25
  position: fixed;
28
26
  left: 0;
29
27
  top: 0;
30
- width: 32px;
31
- height: 32px;
28
+ width: 24px;
29
+ height: 24px;
32
30
  pointer-events: none;
33
31
  z-index: 2147483647;
34
32
  display: none;
35
- /* Offset to center the "tip" of the pointer */
36
- margin-left: -4px;
37
- margin-top: -4px;
38
- filter: drop-shadow(0 0 8px rgba(66, 133, 244, 0.6));
33
+ margin-left: -2px;
34
+ margin-top: -2px;
35
+ filter: drop-shadow(0 4px 10px rgba(66, 133, 244, 0.3));
39
36
  }
40
37
 
41
38
  .custom-cursor.active {
42
39
  display: block;
43
40
  }
44
41
 
42
+ /* Rounded pointer shape */
45
43
  .cursor-shape {
46
44
  width: 100%;
47
45
  height: 100%;
48
46
  fill: white;
49
47
  stroke: var(--ai-blue);
50
- stroke-width: 2px;
48
+ stroke-width: 2.5px;
49
+ /* Using a circle with high radius as base for a rounded pointer */
50
+ stroke-linejoin: round;
51
51
  }
52
52
 
53
53
  .toast {
@@ -91,15 +91,15 @@
91
91
  z-index: 2147483644;
92
92
  pointer-events: none;
93
93
  display: none;
94
- /* Thicker, more professional border */
95
- border: 4px solid transparent;
94
+ /* Thinner, professional 2px border */
95
+ border: 2px solid transparent;
96
96
  border-radius: var(--ai-radius);
97
- /*Conic gradient on border only, no gray background fill */
98
- background: linear-gradient(transparent, transparent) padding-box,
99
- conic-gradient(var(--ai-blue), var(--ai-red), var(--ai-yellow), var(--ai-green), var(--ai-blue)) border-box;
100
- box-shadow:
101
- 0 0 30px color-mix(in srgb, var(--ai-blue), transparent 85%);
102
- transition: all 0.12s ease-out;
97
+ /* Pure transparent center, no fill at all */
98
+ background: none !important;
99
+ /* Conic gradient on border using border-image for absolute transparency in middle */
100
+ border-image: conic-gradient(var(--ai-blue), var(--ai-red), var(--ai-yellow), var(--ai-green), var(--ai-blue)) 1;
101
+ box-shadow: 0 0 15px rgba(66, 133, 244, 0.15);
102
+ transition: all 0.08s ease-out;
103
103
  }
104
104
 
105
105
  .panel {
@@ -235,7 +235,8 @@
235
235
  </style>
236
236
  <div class="custom-cursor">
237
237
  <svg viewBox="0 0 28 32" class="cursor-shape">
238
- <path d="M2,2 L2,28 L8,22 L14,32 L18,29 L12,19 L22,19 Z" stroke-linejoin="round" stroke-linecap="round" />
238
+ <!-- Rounded, professional pointer -->
239
+ <path d="M4,2 L4,26 L10,20 L15,30 L19,27 L14,18 L24,18 Z" stroke-linejoin="round" stroke-linecap="round" />
239
240
  </svg>
240
241
  </div>
241
242
  <div class="toast" role="status">
@@ -250,7 +251,7 @@
250
251
  <span class="kind"></span>
251
252
  <strong class="label"></strong>
252
253
  </div>
253
- <button class="close" type="button" aria-label="Close AI prompt">x</button>
254
+ <button class="close" type="button" aria-label="Close AI prompt">×</button>
254
255
  </div>
255
256
  <form>
256
257
  <textarea placeholder="Ask what to do with this selection..."></textarea>
package/dist/overlay.js CHANGED
@@ -77,7 +77,9 @@ function s(e) {
77
77
  };
78
78
  }
79
79
  function c(e, t) {
80
- return !(n(e, t.blockedSelectors) || t.allowedSelectors.length > 0 && !n(e, t.allowedSelectors));
80
+ if (n(e, t.blockedSelectors)) return !1;
81
+ let r = e.getBoundingClientRect(), i = window.innerWidth * window.innerHeight;
82
+ return !(r.width * r.height > i * .5 || t.allowedSelectors.length > 0 && !n(e, t.allowedSelectors));
81
83
  }
82
84
  function l(e) {
83
85
  let n = (t) => {
@@ -216,7 +218,7 @@ function g(t) {
216
218
  let n = document.createElement("div");
217
219
  n.dataset.aiOverlayRoot = "true";
218
220
  let r = document.createElement("style");
219
- r.dataset.aiOverlayStyle = "true", r.textContent = "\n body.ai-overlay-active { \n cursor: none !important; \n }\n body.ai-overlay-active * { \n cursor: none !important; \n }\n ";
221
+ r.dataset.aiOverlayStyle = "true", r.textContent = "\n body.ai-overlay-active,\n body.ai-overlay-active * {\n cursor: none !important;\n }\n ";
220
222
  let i = n.attachShadow({ mode: "open" });
221
223
  document.head.append(r), document.body.append(n), i.innerHTML = `
222
224
  <style>
@@ -237,27 +239,29 @@ function g(t) {
237
239
  position: fixed;
238
240
  left: 0;
239
241
  top: 0;
240
- width: 32px;
241
- height: 32px;
242
+ width: 24px;
243
+ height: 24px;
242
244
  pointer-events: none;
243
245
  z-index: 2147483647;
244
246
  display: none;
245
- /* Offset to center the "tip" of the pointer */
246
- margin-left: -4px;
247
- margin-top: -4px;
248
- filter: drop-shadow(0 0 8px rgba(66, 133, 244, 0.6));
247
+ margin-left: -2px;
248
+ margin-top: -2px;
249
+ filter: drop-shadow(0 4px 10px rgba(66, 133, 244, 0.3));
249
250
  }
250
251
 
251
252
  .custom-cursor.active {
252
253
  display: block;
253
254
  }
254
255
 
256
+ /* Rounded pointer shape */
255
257
  .cursor-shape {
256
258
  width: 100%;
257
259
  height: 100%;
258
260
  fill: white;
259
261
  stroke: var(--ai-blue);
260
- stroke-width: 2px;
262
+ stroke-width: 2.5px;
263
+ /* Using a circle with high radius as base for a rounded pointer */
264
+ stroke-linejoin: round;
261
265
  }
262
266
 
263
267
  .toast {
@@ -301,15 +305,15 @@ function g(t) {
301
305
  z-index: 2147483644;
302
306
  pointer-events: none;
303
307
  display: none;
304
- /* Thicker, more professional border */
305
- border: 4px solid transparent;
308
+ /* Thinner, professional 2px border */
309
+ border: 2px solid transparent;
306
310
  border-radius: var(--ai-radius);
307
- /*Conic gradient on border only, no gray background fill */
308
- background: linear-gradient(transparent, transparent) padding-box,
309
- conic-gradient(var(--ai-blue), var(--ai-red), var(--ai-yellow), var(--ai-green), var(--ai-blue)) border-box;
310
- box-shadow:
311
- 0 0 30px color-mix(in srgb, var(--ai-blue), transparent 85%);
312
- transition: all 0.12s ease-out;
311
+ /* Pure transparent center, no fill at all */
312
+ background: none !important;
313
+ /* Conic gradient on border using border-image for absolute transparency in middle */
314
+ border-image: conic-gradient(var(--ai-blue), var(--ai-red), var(--ai-yellow), var(--ai-green), var(--ai-blue)) 1;
315
+ box-shadow: 0 0 15px rgba(66, 133, 244, 0.15);
316
+ transition: all 0.08s ease-out;
313
317
  }
314
318
 
315
319
  .panel {
@@ -445,7 +449,8 @@ function g(t) {
445
449
  </style>
446
450
  <div class="custom-cursor">
447
451
  <svg viewBox="0 0 28 32" class="cursor-shape">
448
- <path d="M2,2 L2,28 L8,22 L14,32 L18,29 L12,19 L22,19 Z" stroke-linejoin="round" stroke-linecap="round" />
452
+ <!-- Rounded, professional pointer -->
453
+ <path d="M4,2 L4,26 L10,20 L15,30 L19,27 L14,18 L24,18 Z" stroke-linejoin="round" stroke-linecap="round" />
449
454
  </svg>
450
455
  </div>
451
456
  <div class="toast" role="status">
@@ -460,7 +465,7 @@ function g(t) {
460
465
  <span class="kind"></span>
461
466
  <strong class="label"></strong>
462
467
  </div>
463
- <button class="close" type="button" aria-label="Close AI prompt">x</button>
468
+ <button class="close" type="button" aria-label="Close AI prompt">×</button>
464
469
  </div>
465
470
  <form>
466
471
  <textarea placeholder="Ask what to do with this selection..."></textarea>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@emmaexcel/shakecursor",
3
- "version": "0.1.3",
3
+ "version": "0.1.6",
4
4
  "description": "Framework-agnostic browser SDK for contextual AI selection overlays.",
5
5
  "type": "module",
6
6
  "main": "./dist/overlay.global.js",