@qwertybit/pr-preview 0.1.3 → 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.
@@ -257,27 +257,40 @@
257
257
  }
258
258
 
259
259
  // src/recorder/inpage/cursor.ts
260
- var CURSOR_SVG = `<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M5.5 3.21V20.8c0 .45.54.67.85.35l4.86-4.86a.5.5 0 0 1 .35-.15h6.87a.5.5 0 0 0 .35-.85L6.35 2.85a.5.5 0 0 0-.85.36Z" fill="#1a1a2e" stroke="#fff" stroke-width="1.5"/></svg>`;
260
+ var CURSOR_SVG = `<svg width="100%" height="100%" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M5.5 3.21V20.8c0 .45.54.67.85.35l4.86-4.86a.5.5 0 0 1 .35-.15h6.87a.5.5 0 0 0 .35-.85L6.35 2.85a.5.5 0 0 0-.85.36Z" fill="#1a1a2e" stroke="#fff" stroke-width="1.5"/></svg>`;
261
261
  function ease(t) {
262
262
  return t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2;
263
263
  }
264
+ function easeOut(t) {
265
+ return 1 - Math.pow(1 - t, 3);
266
+ }
264
267
  function installCursor() {
265
268
  if (window.__prPreviewCursor) return;
266
269
  let el = null;
267
270
  let pos = { x: (window.innerWidth || 1280) / 2, y: (window.innerHeight || 800) / 2 };
271
+ let scale = 1;
268
272
  function ensure() {
269
273
  if (el && document.body.contains(el)) return el;
270
274
  el = document.createElement("div");
271
275
  el.id = "__pr-preview-cursor";
272
- el.style.cssText = "position:fixed;z-index:2147483647;pointer-events:none;width:24px;height:24px;transition:none;will-change:transform;display:none;filter:drop-shadow(0 1px 2px rgba(0,0,0,.4));";
273
- el.innerHTML = CURSOR_SVG;
276
+ el.style.cssText = "position:fixed;left:0;top:0;width:0;height:0;z-index:2147483647;pointer-events:none;transform-origin:0 0;transition:none;will-change:transform;display:none;";
277
+ const halo = document.createElement("div");
278
+ halo.style.cssText = "position:absolute;left:-26px;top:-26px;width:52px;height:52px;border-radius:50%;background:radial-gradient(circle,rgba(79,142,247,.45) 0%,rgba(79,142,247,.18) 42%,rgba(79,142,247,0) 70%);";
279
+ const arrow = document.createElement("div");
280
+ arrow.style.cssText = "position:absolute;left:-6px;top:-5px;width:40px;height:40px;filter:drop-shadow(0 2px 3px rgba(0,0,0,.5));";
281
+ arrow.innerHTML = CURSOR_SVG;
282
+ el.appendChild(halo);
283
+ el.appendChild(arrow);
274
284
  document.body.appendChild(el);
275
285
  return el;
276
286
  }
287
+ function apply() {
288
+ const node = ensure();
289
+ node.style.transform = `translate(${pos.x}px, ${pos.y}px) scale(${scale})`;
290
+ }
277
291
  function place(x, y) {
278
292
  pos = { x, y };
279
- const node = ensure();
280
- node.style.transform = `translate(${x}px, ${y}px)`;
293
+ apply();
281
294
  }
282
295
  window.__prPreviewCursor = {
283
296
  show(x, y) {
@@ -290,28 +303,80 @@
290
303
  const node = ensure();
291
304
  node.style.display = "block";
292
305
  const from = { ...pos };
293
- const dist = Math.hypot(x - from.x, y - from.y);
294
- const dur = Math.max(140, Math.min(durationMs, dist * 0.7));
306
+ const dx = x - from.x;
307
+ const dy = y - from.y;
308
+ const dist = Math.hypot(dx, dy);
295
309
  if (dist < 1) {
310
+ place(x, y);
296
311
  resolve();
297
312
  return;
298
313
  }
314
+ const dur = Math.max(280, Math.min(durationMs, dist * 0.9));
315
+ const ux = dx / dist;
316
+ const uy = dy / dist;
317
+ const nx = -uy;
318
+ const ny = ux;
319
+ const overshoot = dist > 70 ? Math.min(dist * 0.08, 14) : 0;
320
+ const aim = { x: x + ux * overshoot, y: y + uy * overshoot };
321
+ const adx = aim.x - from.x;
322
+ const ady = aim.y - from.y;
323
+ const arc = Math.min(dist * 0.16, 55) * (Math.random() < 0.5 ? -1 : 1);
324
+ const cx = from.x + adx * 0.5 + nx * arc;
325
+ const cy = from.y + ady * 0.5 + ny * arc;
326
+ const jitter = Math.min(dist * 0.02, 3.5);
327
+ const ballisticDur = overshoot ? dur * 0.82 : dur;
328
+ const correctDur = 150;
329
+ const correct = (from2, c0) => {
330
+ const t = Math.min(1, (performance.now() - c0) / correctDur);
331
+ const k = easeOut(t);
332
+ place(from2.x + (x - from2.x) * k, from2.y + (y - from2.y) * k);
333
+ if (t < 1) requestAnimationFrame(() => correct(from2, c0));
334
+ else {
335
+ place(x, y);
336
+ resolve();
337
+ }
338
+ };
299
339
  const t0 = performance.now();
300
- const tick = (now) => {
301
- const t = Math.min(1, (now - t0) / dur);
340
+ const ballistic = (now) => {
341
+ const t = Math.min(1, (now - t0) / ballisticDur);
302
342
  const k = ease(t);
303
- place(from.x + (x - from.x) * k, from.y + (y - from.y) * k);
304
- if (t < 1) requestAnimationFrame(tick);
305
- else resolve();
343
+ const mt = 1 - k;
344
+ let px = mt * mt * from.x + 2 * mt * k * cx + k * k * aim.x;
345
+ let py = mt * mt * from.y + 2 * mt * k * cy + k * k * aim.y;
346
+ if (t < 0.9) {
347
+ const fade = 1 - t / 0.9;
348
+ px += (Math.random() * 2 - 1) * jitter * fade;
349
+ py += (Math.random() * 2 - 1) * jitter * fade;
350
+ }
351
+ place(px, py);
352
+ if (t < 1) requestAnimationFrame(ballistic);
353
+ else if (overshoot) correct({ x: aim.x, y: aim.y }, performance.now());
354
+ else {
355
+ place(x, y);
356
+ resolve();
357
+ }
306
358
  };
307
- requestAnimationFrame(tick);
359
+ requestAnimationFrame(ballistic);
308
360
  });
309
361
  },
310
362
  clickPulse() {
311
363
  return new Promise((resolve) => {
312
364
  const ring = document.createElement("div");
313
- ring.style.cssText = `position:fixed;z-index:2147483646;pointer-events:none;left:${pos.x - 14}px;top:${pos.y - 14}px;width:28px;height:28px;border-radius:50%;border:3px solid #4f8ef7;opacity:.9;transform:scale(.4);transition:transform .3s ease-out,opacity .3s ease-out;`;
365
+ ring.style.cssText = `position:fixed;z-index:2147483646;pointer-events:none;left:${pos.x - 22}px;top:${pos.y - 22}px;width:44px;height:44px;border-radius:50%;border:3px solid #4f8ef7;opacity:.9;transform:scale(.4);transition:transform .3s ease-out,opacity .3s ease-out;`;
314
366
  document.body.appendChild(ring);
367
+ const pressT0 = performance.now();
368
+ const PRESS_MS = 200;
369
+ const press = (now) => {
370
+ const p = Math.min(1, (now - pressT0) / PRESS_MS);
371
+ scale = 1 - 0.18 * Math.sin(p * Math.PI);
372
+ apply();
373
+ if (p < 1) requestAnimationFrame(press);
374
+ else {
375
+ scale = 1;
376
+ apply();
377
+ }
378
+ };
379
+ requestAnimationFrame(press);
315
380
  requestAnimationFrame(() => {
316
381
  ring.style.transform = "scale(1.4)";
317
382
  ring.style.opacity = "0";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qwertybit/pr-preview",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "Record a UI journey once, get polished before/after MP4 clips of every pull request — automatically.",
5
5
  "license": "MIT",
6
6
  "author": "QwertyBit <vlad@flowpoint.ai>",
@@ -57,6 +57,7 @@
57
57
  "frontend"
58
58
  ],
59
59
  "dependencies": {
60
+ "@modelcontextprotocol/sdk": "^1.29.0",
60
61
  "commander": "^12.1.0",
61
62
  "get-port": "^7.1.0",
62
63
  "gifenc": "^1.0.3",