@pixerate/editor 0.227.1 → 0.228.0
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/dist/agent/index.cjs +3 -3
- package/dist/agent/index.js +2 -2
- package/dist/canvas/drawController.d.cts +6 -0
- package/dist/canvas/drawController.d.ts +6 -0
- package/dist/canvas/gestures.d.cts +87 -0
- package/dist/canvas/gestures.d.ts +87 -0
- package/dist/canvas/index.cjs +30 -2
- package/dist/canvas/index.d.cts +1 -0
- package/dist/canvas/index.d.ts +1 -0
- package/dist/canvas/index.js +30 -2
- package/dist/{chunk-53GOGBTM.cjs → chunk-CHAWTXBE.cjs} +243 -39
- package/dist/{chunk-7KZ2HKKC.js → chunk-LPFQHVCO.js} +244 -1
- package/dist/{chunk-4EFNS2HV.cjs → chunk-T7QSEKDY.cjs} +248 -5
- package/dist/{chunk-ZPJEH456.js → chunk-XVIK73LS.js} +206 -2
- package/dist/commands/agentTools.d.cts +14 -0
- package/dist/commands/agentTools.d.ts +14 -0
- package/dist/commands/builtins.d.cts +11 -1
- package/dist/commands/builtins.d.ts +11 -1
- package/dist/commands/index.cjs +23 -9
- package/dist/commands/index.js +17 -3
- package/package.json +1 -1
- package/skills/pixerate-agent-tools/SKILL.md +5 -0
- package/skills/pixerate-canvas/SKILL.md +41 -2
|
@@ -430,6 +430,238 @@ function matchArrangeShortcut(event, bindings = DEFAULT_ARRANGE_SHORTCUTS) {
|
|
|
430
430
|
return arrangeShortcutMap(bindings).resolve(event)?.command ?? null;
|
|
431
431
|
}
|
|
432
432
|
|
|
433
|
+
// src/canvas/gestures.ts
|
|
434
|
+
function gestureRandom(seed) {
|
|
435
|
+
let a = seed >>> 0;
|
|
436
|
+
return () => {
|
|
437
|
+
a = a + 1831565813 >>> 0;
|
|
438
|
+
let t = a;
|
|
439
|
+
t = Math.imul(t ^ t >>> 15, t | 1);
|
|
440
|
+
t ^= t + Math.imul(t ^ t >>> 7, t | 61);
|
|
441
|
+
return ((t ^ t >>> 14) >>> 0) / 4294967296;
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
function gestureSeed(value) {
|
|
445
|
+
const text = JSON.stringify(value) ?? "";
|
|
446
|
+
let h = 2166136261;
|
|
447
|
+
for (let i = 0; i < text.length; i++) {
|
|
448
|
+
h ^= text.charCodeAt(i);
|
|
449
|
+
h = Math.imul(h, 16777619);
|
|
450
|
+
}
|
|
451
|
+
return h >>> 0;
|
|
452
|
+
}
|
|
453
|
+
function gestureUnion(rects) {
|
|
454
|
+
if (!rects.length) throw new Error("Pass at least one rect.");
|
|
455
|
+
const x = Math.min(...rects.map((r) => r.x));
|
|
456
|
+
const y = Math.min(...rects.map((r) => r.y));
|
|
457
|
+
const right = Math.max(...rects.map((r) => r.x + r.width));
|
|
458
|
+
const bottom = Math.max(...rects.map((r) => r.y + r.height));
|
|
459
|
+
return { x, y, width: right - x, height: bottom - y };
|
|
460
|
+
}
|
|
461
|
+
var clamp = (v, lo, hi) => Math.min(hi, Math.max(lo, v));
|
|
462
|
+
var centre = (r) => [r.x + r.width / 2, r.y + r.height / 2];
|
|
463
|
+
function smoothNoise(rand, octaves = 3) {
|
|
464
|
+
const waves = Array.from({ length: octaves }, (_, i) => ({ f: (i + 1) * (0.6 + rand() * 0.8), phase: rand() * Math.PI * 2, amp: 1 / (i + 1) }));
|
|
465
|
+
const total = waves.reduce((sum, w) => sum + w.amp, 0);
|
|
466
|
+
return (t) => waves.reduce((sum, w) => sum + w.amp * Math.sin(2 * Math.PI * w.f * t + w.phase), 0) / total;
|
|
467
|
+
}
|
|
468
|
+
function handPressure(rand) {
|
|
469
|
+
const r = (lo, hi) => Math.round((lo + rand() * (hi - lo)) * 100) / 100;
|
|
470
|
+
return [r(0.35, 0.55), r(0.8, 0.95), r(0.9, 1), r(0.75, 0.95), r(0.4, 0.65)];
|
|
471
|
+
}
|
|
472
|
+
var round = (p) => [Math.round(p[0] * 100) / 100, Math.round(p[1] * 100) / 100];
|
|
473
|
+
function wobblyLine(a, b, rand, amp, bow = 0) {
|
|
474
|
+
const dx = b[0] - a[0];
|
|
475
|
+
const dy = b[1] - a[1];
|
|
476
|
+
const len = Math.hypot(dx, dy);
|
|
477
|
+
if (len === 0) return [a];
|
|
478
|
+
const nx = -dy / len;
|
|
479
|
+
const ny = dx / len;
|
|
480
|
+
const noise = smoothNoise(rand);
|
|
481
|
+
const n = clamp(Math.ceil(len / 4), 8, 200);
|
|
482
|
+
return Array.from({ length: n + 1 }, (_, i) => {
|
|
483
|
+
const t = i / n;
|
|
484
|
+
const off = bow * 4 * t * (1 - t) + amp * noise(t) * Math.sqrt(Math.sin(Math.PI * t));
|
|
485
|
+
return [a[0] + dx * t + nx * off, a[1] + dy * t + ny * off];
|
|
486
|
+
});
|
|
487
|
+
}
|
|
488
|
+
function circleGesture(rect, seed, options = {}) {
|
|
489
|
+
const rand = gestureRandom(seed);
|
|
490
|
+
const pad = Math.max(0, options.padding ?? 8);
|
|
491
|
+
const [cx, cy] = centre(rect);
|
|
492
|
+
const tilt = (rand() - 0.5) * 0.08;
|
|
493
|
+
const c = Math.abs(Math.cos(tilt));
|
|
494
|
+
const s = Math.abs(Math.sin(tilt));
|
|
495
|
+
const a = (rect.width / 2 * c + rect.height / 2 * s) * Math.SQRT2 + pad;
|
|
496
|
+
const b = (rect.width / 2 * s + rect.height / 2 * c) * Math.SQRT2 + pad;
|
|
497
|
+
const dir = rand() < 0.7 ? -1 : 1;
|
|
498
|
+
const start = -Math.PI * 0.75 + (rand() - 0.5) * 0.6;
|
|
499
|
+
const sweep = Math.PI * 2 + Math.PI * (0.1 + rand() * 0.12);
|
|
500
|
+
const jitter = 0.03 + rand() * 0.03;
|
|
501
|
+
const drift = 0.04 + rand() * 0.05;
|
|
502
|
+
const noise = smoothNoise(rand, 4);
|
|
503
|
+
const perimeter = Math.PI * (3 * (a + b) - Math.sqrt((3 * a + b) * (a + 3 * b)));
|
|
504
|
+
const n = clamp(Math.ceil(perimeter * (sweep / (Math.PI * 2)) / 5), 48, 360);
|
|
505
|
+
const points = Array.from({ length: n + 1 }, (_, i) => {
|
|
506
|
+
const t = i / n;
|
|
507
|
+
const angle = start + dir * sweep * t;
|
|
508
|
+
const grow = 1 + jitter * (0.5 + 0.5 * noise(t)) + drift * t * t;
|
|
509
|
+
const ex = a * grow * Math.cos(angle);
|
|
510
|
+
const ey = b * grow * Math.sin(angle);
|
|
511
|
+
return round([cx + ex * Math.cos(tilt) - ey * Math.sin(tilt), cy + ex * Math.sin(tilt) + ey * Math.cos(tilt)]);
|
|
512
|
+
});
|
|
513
|
+
return { points, pressure: handPressure(rand) };
|
|
514
|
+
}
|
|
515
|
+
function underlineGesture(rect, seed, options = {}) {
|
|
516
|
+
const rand = gestureRandom(seed);
|
|
517
|
+
const offset = Math.max(0, options.offset ?? 4);
|
|
518
|
+
const amp = options.wave ? clamp(rect.width * 0.02, 2, 4) : 0;
|
|
519
|
+
const shake = Math.min(1.5, offset * 0.4);
|
|
520
|
+
const base = rect.y + rect.height + offset + amp + shake;
|
|
521
|
+
const slope = Math.min(6, rect.width * 0.03);
|
|
522
|
+
const x0 = rect.x - rect.width * (0.01 + rand() * 0.04);
|
|
523
|
+
const x1 = rect.x + rect.width * (0.96 + rand() * 0.08);
|
|
524
|
+
const line = wobblyLine([x0, base + rand() * slope], [x1, base + rand() * slope], rand, shake, (rand() - 0.5) * 2 * shake);
|
|
525
|
+
const wavelength = clamp(rect.width / 8, 12, 28);
|
|
526
|
+
const phase = rand() * Math.PI * 2;
|
|
527
|
+
const points = amp ? line.map(([x, y]) => [x, y + amp * Math.sin(2 * Math.PI * (x - x0) / wavelength + phase)]) : line;
|
|
528
|
+
return { points: points.map(round), pressure: handPressure(rand) };
|
|
529
|
+
}
|
|
530
|
+
function gestureEdgePoint(rect, toward, gap = 0) {
|
|
531
|
+
const [cx, cy] = centre(rect);
|
|
532
|
+
const dx = toward[0] - cx;
|
|
533
|
+
const dy = toward[1] - cy;
|
|
534
|
+
const len = Math.hypot(dx, dy);
|
|
535
|
+
if (len === 0) return [cx, cy];
|
|
536
|
+
const t = Math.min(dx ? rect.width / 2 / Math.abs(dx) : Infinity, dy ? rect.height / 2 / Math.abs(dy) : Infinity);
|
|
537
|
+
return [cx + dx * t + dx / len * gap, cy + dy * t + dy / len * gap];
|
|
538
|
+
}
|
|
539
|
+
var isRect = (end) => !Array.isArray(end);
|
|
540
|
+
function arrowGesture(from, to, seed, options = {}) {
|
|
541
|
+
const rand = gestureRandom(seed);
|
|
542
|
+
const gap = Math.max(0, options.gap ?? 6);
|
|
543
|
+
const fromCentre = isRect(from) ? centre(from) : from;
|
|
544
|
+
const toCentre = isRect(to) ? centre(to) : to;
|
|
545
|
+
const start = isRect(from) ? gestureEdgePoint(from, toCentre, gap) : from;
|
|
546
|
+
const tip = isRect(to) ? gestureEdgePoint(to, fromCentre, gap) : to;
|
|
547
|
+
const dx = tip[0] - start[0];
|
|
548
|
+
const dy = tip[1] - start[1];
|
|
549
|
+
const len = Math.hypot(dx, dy);
|
|
550
|
+
if (len < 4) throw new Error("The arrow's ends are too close together (or overlap): pass ends at least a few canvas units apart.");
|
|
551
|
+
const curve = clamp(options.curve ?? (0.08 + rand() * 0.08) * (rand() < 0.5 ? -1 : 1), -1, 1);
|
|
552
|
+
const nx = -dy / len;
|
|
553
|
+
const ny = dx / len;
|
|
554
|
+
const control = [(start[0] + tip[0]) / 2 + nx * curve * len, (start[1] + tip[1]) / 2 + ny * curve * len];
|
|
555
|
+
const noise = smoothNoise(rand);
|
|
556
|
+
const amp = Math.min(2, len * 0.01);
|
|
557
|
+
const n = clamp(Math.ceil(len / 4), 12, 200);
|
|
558
|
+
const shaft = Array.from({ length: n + 1 }, (_, i) => {
|
|
559
|
+
const t = i / n;
|
|
560
|
+
const u = 1 - t;
|
|
561
|
+
const x = u * u * start[0] + 2 * u * t * control[0] + t * t * tip[0];
|
|
562
|
+
const y = u * u * start[1] + 2 * u * t * control[1] + t * t * tip[1];
|
|
563
|
+
const off = amp * noise(t) * Math.sqrt(Math.sin(Math.PI * t));
|
|
564
|
+
return [x + nx * off, y + ny * off];
|
|
565
|
+
});
|
|
566
|
+
const tx = tip[0] - control[0];
|
|
567
|
+
const ty = tip[1] - control[1];
|
|
568
|
+
const tl = Math.hypot(tx, ty) || 1;
|
|
569
|
+
const head = clamp(len * 0.15, 8, 22);
|
|
570
|
+
const barb = (angle2, length) => {
|
|
571
|
+
const c = Math.cos(angle2);
|
|
572
|
+
const s = Math.sin(angle2);
|
|
573
|
+
const bx = -(tx / tl) * c + ty / tl * s;
|
|
574
|
+
const by = -(ty / tl) * c - tx / tl * s;
|
|
575
|
+
return [1 / 3, 2 / 3, 1].map((f) => [tip[0] + bx * length * f, tip[1] + by * length * f]);
|
|
576
|
+
};
|
|
577
|
+
const clear = (pts) => !isRect(to) || pts.every(([x, y]) => x <= to.x || x >= to.x + to.width || y <= to.y || y >= to.y + to.height);
|
|
578
|
+
const fit = (angle2) => {
|
|
579
|
+
const length = head * (0.85 + rand() * 0.3);
|
|
580
|
+
for (let k = 1; k > 0.05; k -= 0.15) {
|
|
581
|
+
const pts = barb(angle2 * k, length);
|
|
582
|
+
if (clear(pts)) return pts;
|
|
583
|
+
}
|
|
584
|
+
return barb(0, length);
|
|
585
|
+
};
|
|
586
|
+
const angle = () => (25 + rand() * 8) * Math.PI / 180;
|
|
587
|
+
const first = fit(angle());
|
|
588
|
+
const second = fit(-angle());
|
|
589
|
+
const points = [...shaft, ...first, ...first.slice(0, -1).reverse(), tip, ...second];
|
|
590
|
+
return { points: points.map(round), pressure: handPressure(rand) };
|
|
591
|
+
}
|
|
592
|
+
function bracketGesture(rect, side, seed, options = {}) {
|
|
593
|
+
const rand = gestureRandom(seed);
|
|
594
|
+
const pad = Math.max(0, options.padding ?? 8);
|
|
595
|
+
const vertical = side === "left" || side === "right";
|
|
596
|
+
const span = vertical ? rect.height : rect.width;
|
|
597
|
+
const depth = Math.max(1, options.depth ?? clamp(span * 0.08, 6, 20));
|
|
598
|
+
const from = vertical ? rect.y : rect.x;
|
|
599
|
+
const edge = side === "left" ? rect.x - pad : side === "right" ? rect.x + rect.width + pad : side === "top" ? rect.y - pad : rect.y + rect.height + pad;
|
|
600
|
+
const out = side === "left" || side === "top" ? -1 : 1;
|
|
601
|
+
const at = (s, d) => vertical ? [edge + out * d, from + s * span] : [from + s * span, edge + out * d];
|
|
602
|
+
const noise = smoothNoise(rand);
|
|
603
|
+
const shake = Math.min(1, depth * 0.06);
|
|
604
|
+
const n = clamp(Math.ceil(span / 4), 24, 240);
|
|
605
|
+
let points;
|
|
606
|
+
if (options.shape === "square") {
|
|
607
|
+
const arm = depth * (0.8 + rand() * 0.2);
|
|
608
|
+
const spine = Array.from({ length: n + 1 }, (_, i) => at(i / n, depth + shake * (1 + noise(i / n))));
|
|
609
|
+
points = [at(0, depth - arm), at(0, depth * 0.5), ...spine, at(1, depth * 0.5), at(1, depth - arm * (0.85 + rand() * 0.15))];
|
|
610
|
+
} else {
|
|
611
|
+
const e = clamp(depth / Math.max(span, 1), 0.04, 0.2);
|
|
612
|
+
const brace = (s) => {
|
|
613
|
+
const u = s <= 0.5 ? s : 1 - s;
|
|
614
|
+
if (u < e) return depth / 2 * Math.sin(Math.PI / 2 * u / e);
|
|
615
|
+
if (u < 0.5 - e) return depth / 2;
|
|
616
|
+
return depth / 2 + depth / 2 * (1 - Math.cos(Math.PI / 2 * (u - (0.5 - e)) / e));
|
|
617
|
+
};
|
|
618
|
+
points = Array.from({ length: n + 1 }, (_, i) => {
|
|
619
|
+
const s = i / n;
|
|
620
|
+
return at(s, brace(s) + shake * (1 + noise(s)) * Math.sin(Math.PI * s));
|
|
621
|
+
});
|
|
622
|
+
}
|
|
623
|
+
return { points: points.map(round), pressure: handPressure(rand) };
|
|
624
|
+
}
|
|
625
|
+
function strikeGesture(rect, seed) {
|
|
626
|
+
const rand = gestureRandom(seed);
|
|
627
|
+
const [, cy] = centre(rect);
|
|
628
|
+
const slope = rect.height * 0.08;
|
|
629
|
+
const x0 = rect.x - rect.width * (0.01 + rand() * 0.03);
|
|
630
|
+
const x1 = rect.x + rect.width * (1.01 + rand() * 0.03);
|
|
631
|
+
const y0 = cy + (rand() - 0.5) * rect.height * 0.1 + slope * (rand() - 0.5);
|
|
632
|
+
const y1 = cy + (rand() - 0.5) * rect.height * 0.1 + slope * (rand() - 0.5);
|
|
633
|
+
const points = wobblyLine([x0, y0], [x1, y1], rand, Math.min(1.5, rect.height * 0.05));
|
|
634
|
+
return { points: points.map(round), pressure: handPressure(rand) };
|
|
635
|
+
}
|
|
636
|
+
function crossGesture(rect, seed) {
|
|
637
|
+
const rand = gestureRandom(seed);
|
|
638
|
+
const over = () => 0.02 + rand() * 0.04;
|
|
639
|
+
const left = () => rect.x - rect.width * over();
|
|
640
|
+
const right = () => rect.x + rect.width * (1 + over());
|
|
641
|
+
const top = () => rect.y - rect.height * over();
|
|
642
|
+
const bottom = () => rect.y + rect.height * (1 + over());
|
|
643
|
+
const amp = Math.min(1.5, Math.min(rect.width, rect.height) * 0.03);
|
|
644
|
+
const first = wobblyLine([left(), top()], [right(), bottom()], rand, amp);
|
|
645
|
+
const second = wobblyLine([right(), top()], [left(), bottom()], rand, amp);
|
|
646
|
+
const mid1 = Math.floor(first.length / 2);
|
|
647
|
+
const mid2 = Math.floor(second.length / 2);
|
|
648
|
+
const points = [...first, ...first.slice(mid1, -1).reverse(), ...second.slice(1, mid2 + 1).reverse(), ...second];
|
|
649
|
+
return { points: points.map(round), pressure: handPressure(rand) };
|
|
650
|
+
}
|
|
651
|
+
function checkGesture(at, seed, options = {}) {
|
|
652
|
+
const rand = gestureRandom(seed);
|
|
653
|
+
const [cx, cy] = isRect(at) ? centre(at) : at;
|
|
654
|
+
const s = Math.max(4, options.size ?? (isRect(at) ? clamp(Math.min(at.width, at.height) * 0.6, 12, 48) : 20));
|
|
655
|
+
const turn = (rand() - 0.5) * 12 * Math.PI / 180;
|
|
656
|
+
const place = ([x, y]) => [cx + s * (x * Math.cos(turn) - y * Math.sin(turn)), cy + s * (x * Math.sin(turn) + y * Math.cos(turn))];
|
|
657
|
+
const p0 = [-0.42 + rand() * 0.08, -0.05 + rand() * 0.08];
|
|
658
|
+
const p1 = [-0.12 + rand() * 0.06, 0.38 + rand() * 0.06];
|
|
659
|
+
const p2 = [0.48 + rand() * 0.06, -0.5 + rand() * 0.06];
|
|
660
|
+
const short = wobblyLine(place(p0), place(p1), rand, 0, -s * 0.03);
|
|
661
|
+
const long = wobblyLine(place(p1), place(p2), rand, Math.min(1, s * 0.02), s * 0.06);
|
|
662
|
+
return { points: [...short, ...long.slice(1)].map(round), pressure: [0.5, 0.95, 0.9, 0.7, 0.35] };
|
|
663
|
+
}
|
|
664
|
+
|
|
433
665
|
export {
|
|
434
666
|
PLATFORMS,
|
|
435
667
|
detectPlatform,
|
|
@@ -448,5 +680,16 @@ export {
|
|
|
448
680
|
toShortcutBindings,
|
|
449
681
|
ShortcutMap,
|
|
450
682
|
DEFAULT_ARRANGE_SHORTCUTS,
|
|
451
|
-
matchArrangeShortcut
|
|
683
|
+
matchArrangeShortcut,
|
|
684
|
+
gestureRandom,
|
|
685
|
+
gestureSeed,
|
|
686
|
+
gestureUnion,
|
|
687
|
+
circleGesture,
|
|
688
|
+
underlineGesture,
|
|
689
|
+
gestureEdgePoint,
|
|
690
|
+
arrowGesture,
|
|
691
|
+
bracketGesture,
|
|
692
|
+
strikeGesture,
|
|
693
|
+
crossGesture,
|
|
694
|
+
checkGesture
|
|
452
695
|
};
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
var PLATFORMS = ["mac", "windows", "linux"];
|
|
3
3
|
function detectPlatform() {
|
|
4
4
|
const nav = typeof navigator !== "undefined" ? navigator : void 0;
|
|
5
|
-
const text = `${_nullishCoalesce(_optionalChain([nav, 'optionalAccess',
|
|
6
|
-
if (/Mac|iPod|iPhone|iPad/.test(_optionalChain([nav, 'optionalAccess',
|
|
5
|
+
const text = `${_nullishCoalesce(_optionalChain([nav, 'optionalAccess', _2 => _2.platform]), () => ( ""))} ${_nullishCoalesce(_optionalChain([nav, 'optionalAccess', _3 => _3.userAgent]), () => ( ""))}`;
|
|
6
|
+
if (/Mac|iPod|iPhone|iPad/.test(_optionalChain([nav, 'optionalAccess', _4 => _4.platform]) || _optionalChain([nav, 'optionalAccess', _5 => _5.userAgent]) || "")) return "mac";
|
|
7
7
|
if (/Win/.test(text)) return "windows";
|
|
8
8
|
return "linux";
|
|
9
9
|
}
|
|
@@ -222,7 +222,7 @@ function isEditableEventTarget(event) {
|
|
|
222
222
|
if (!target) target = typeof document !== "undefined" ? document.activeElement : null;
|
|
223
223
|
if (!target || typeof target.tagName !== "string") return false;
|
|
224
224
|
const tag = target.tagName.toUpperCase();
|
|
225
|
-
return !!(tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT" || target.isContentEditable || _optionalChain([target, 'access',
|
|
225
|
+
return !!(tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT" || target.isContentEditable || _optionalChain([target, 'access', _6 => _6.getAttribute, 'optionalCall', _7 => _7("contenteditable")]) === "true" || _optionalChain([target, 'access', _8 => _8.closest, 'optionalCall', _9 => _9('[contenteditable="true"]')]) || _optionalChain([target, 'access', _10 => _10.closest, 'optionalCall', _11 => _11(".ProseMirror")]));
|
|
226
226
|
}
|
|
227
227
|
|
|
228
228
|
// src/commands/shortcuts.ts
|
|
@@ -427,9 +427,252 @@ function arrangeShortcutMap(bindings) {
|
|
|
427
427
|
return map;
|
|
428
428
|
}
|
|
429
429
|
function matchArrangeShortcut(event, bindings = DEFAULT_ARRANGE_SHORTCUTS) {
|
|
430
|
-
return _nullishCoalesce(_optionalChain([arrangeShortcutMap, 'call',
|
|
430
|
+
return _nullishCoalesce(_optionalChain([arrangeShortcutMap, 'call', _12 => _12(bindings), 'access', _13 => _13.resolve, 'call', _14 => _14(event), 'optionalAccess', _15 => _15.command]), () => ( null));
|
|
431
431
|
}
|
|
432
432
|
|
|
433
|
+
// src/canvas/gestures.ts
|
|
434
|
+
function gestureRandom(seed) {
|
|
435
|
+
let a = seed >>> 0;
|
|
436
|
+
return () => {
|
|
437
|
+
a = a + 1831565813 >>> 0;
|
|
438
|
+
let t = a;
|
|
439
|
+
t = Math.imul(t ^ t >>> 15, t | 1);
|
|
440
|
+
t ^= t + Math.imul(t ^ t >>> 7, t | 61);
|
|
441
|
+
return ((t ^ t >>> 14) >>> 0) / 4294967296;
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
function gestureSeed(value) {
|
|
445
|
+
const text = _nullishCoalesce(JSON.stringify(value), () => ( ""));
|
|
446
|
+
let h = 2166136261;
|
|
447
|
+
for (let i = 0; i < text.length; i++) {
|
|
448
|
+
h ^= text.charCodeAt(i);
|
|
449
|
+
h = Math.imul(h, 16777619);
|
|
450
|
+
}
|
|
451
|
+
return h >>> 0;
|
|
452
|
+
}
|
|
453
|
+
function gestureUnion(rects) {
|
|
454
|
+
if (!rects.length) throw new Error("Pass at least one rect.");
|
|
455
|
+
const x = Math.min(...rects.map((r) => r.x));
|
|
456
|
+
const y = Math.min(...rects.map((r) => r.y));
|
|
457
|
+
const right = Math.max(...rects.map((r) => r.x + r.width));
|
|
458
|
+
const bottom = Math.max(...rects.map((r) => r.y + r.height));
|
|
459
|
+
return { x, y, width: right - x, height: bottom - y };
|
|
460
|
+
}
|
|
461
|
+
var clamp = (v, lo, hi) => Math.min(hi, Math.max(lo, v));
|
|
462
|
+
var centre = (r) => [r.x + r.width / 2, r.y + r.height / 2];
|
|
463
|
+
function smoothNoise(rand, octaves = 3) {
|
|
464
|
+
const waves = Array.from({ length: octaves }, (_, i) => ({ f: (i + 1) * (0.6 + rand() * 0.8), phase: rand() * Math.PI * 2, amp: 1 / (i + 1) }));
|
|
465
|
+
const total = waves.reduce((sum, w) => sum + w.amp, 0);
|
|
466
|
+
return (t) => waves.reduce((sum, w) => sum + w.amp * Math.sin(2 * Math.PI * w.f * t + w.phase), 0) / total;
|
|
467
|
+
}
|
|
468
|
+
function handPressure(rand) {
|
|
469
|
+
const r = (lo, hi) => Math.round((lo + rand() * (hi - lo)) * 100) / 100;
|
|
470
|
+
return [r(0.35, 0.55), r(0.8, 0.95), r(0.9, 1), r(0.75, 0.95), r(0.4, 0.65)];
|
|
471
|
+
}
|
|
472
|
+
var round = (p) => [Math.round(p[0] * 100) / 100, Math.round(p[1] * 100) / 100];
|
|
473
|
+
function wobblyLine(a, b, rand, amp, bow = 0) {
|
|
474
|
+
const dx = b[0] - a[0];
|
|
475
|
+
const dy = b[1] - a[1];
|
|
476
|
+
const len = Math.hypot(dx, dy);
|
|
477
|
+
if (len === 0) return [a];
|
|
478
|
+
const nx = -dy / len;
|
|
479
|
+
const ny = dx / len;
|
|
480
|
+
const noise = smoothNoise(rand);
|
|
481
|
+
const n = clamp(Math.ceil(len / 4), 8, 200);
|
|
482
|
+
return Array.from({ length: n + 1 }, (_, i) => {
|
|
483
|
+
const t = i / n;
|
|
484
|
+
const off = bow * 4 * t * (1 - t) + amp * noise(t) * Math.sqrt(Math.sin(Math.PI * t));
|
|
485
|
+
return [a[0] + dx * t + nx * off, a[1] + dy * t + ny * off];
|
|
486
|
+
});
|
|
487
|
+
}
|
|
488
|
+
function circleGesture(rect, seed, options = {}) {
|
|
489
|
+
const rand = gestureRandom(seed);
|
|
490
|
+
const pad = Math.max(0, _nullishCoalesce(options.padding, () => ( 8)));
|
|
491
|
+
const [cx, cy] = centre(rect);
|
|
492
|
+
const tilt = (rand() - 0.5) * 0.08;
|
|
493
|
+
const c = Math.abs(Math.cos(tilt));
|
|
494
|
+
const s = Math.abs(Math.sin(tilt));
|
|
495
|
+
const a = (rect.width / 2 * c + rect.height / 2 * s) * Math.SQRT2 + pad;
|
|
496
|
+
const b = (rect.width / 2 * s + rect.height / 2 * c) * Math.SQRT2 + pad;
|
|
497
|
+
const dir = rand() < 0.7 ? -1 : 1;
|
|
498
|
+
const start = -Math.PI * 0.75 + (rand() - 0.5) * 0.6;
|
|
499
|
+
const sweep = Math.PI * 2 + Math.PI * (0.1 + rand() * 0.12);
|
|
500
|
+
const jitter = 0.03 + rand() * 0.03;
|
|
501
|
+
const drift = 0.04 + rand() * 0.05;
|
|
502
|
+
const noise = smoothNoise(rand, 4);
|
|
503
|
+
const perimeter = Math.PI * (3 * (a + b) - Math.sqrt((3 * a + b) * (a + 3 * b)));
|
|
504
|
+
const n = clamp(Math.ceil(perimeter * (sweep / (Math.PI * 2)) / 5), 48, 360);
|
|
505
|
+
const points = Array.from({ length: n + 1 }, (_, i) => {
|
|
506
|
+
const t = i / n;
|
|
507
|
+
const angle = start + dir * sweep * t;
|
|
508
|
+
const grow = 1 + jitter * (0.5 + 0.5 * noise(t)) + drift * t * t;
|
|
509
|
+
const ex = a * grow * Math.cos(angle);
|
|
510
|
+
const ey = b * grow * Math.sin(angle);
|
|
511
|
+
return round([cx + ex * Math.cos(tilt) - ey * Math.sin(tilt), cy + ex * Math.sin(tilt) + ey * Math.cos(tilt)]);
|
|
512
|
+
});
|
|
513
|
+
return { points, pressure: handPressure(rand) };
|
|
514
|
+
}
|
|
515
|
+
function underlineGesture(rect, seed, options = {}) {
|
|
516
|
+
const rand = gestureRandom(seed);
|
|
517
|
+
const offset = Math.max(0, _nullishCoalesce(options.offset, () => ( 4)));
|
|
518
|
+
const amp = options.wave ? clamp(rect.width * 0.02, 2, 4) : 0;
|
|
519
|
+
const shake = Math.min(1.5, offset * 0.4);
|
|
520
|
+
const base = rect.y + rect.height + offset + amp + shake;
|
|
521
|
+
const slope = Math.min(6, rect.width * 0.03);
|
|
522
|
+
const x0 = rect.x - rect.width * (0.01 + rand() * 0.04);
|
|
523
|
+
const x1 = rect.x + rect.width * (0.96 + rand() * 0.08);
|
|
524
|
+
const line = wobblyLine([x0, base + rand() * slope], [x1, base + rand() * slope], rand, shake, (rand() - 0.5) * 2 * shake);
|
|
525
|
+
const wavelength = clamp(rect.width / 8, 12, 28);
|
|
526
|
+
const phase = rand() * Math.PI * 2;
|
|
527
|
+
const points = amp ? line.map(([x, y]) => [x, y + amp * Math.sin(2 * Math.PI * (x - x0) / wavelength + phase)]) : line;
|
|
528
|
+
return { points: points.map(round), pressure: handPressure(rand) };
|
|
529
|
+
}
|
|
530
|
+
function gestureEdgePoint(rect, toward, gap = 0) {
|
|
531
|
+
const [cx, cy] = centre(rect);
|
|
532
|
+
const dx = toward[0] - cx;
|
|
533
|
+
const dy = toward[1] - cy;
|
|
534
|
+
const len = Math.hypot(dx, dy);
|
|
535
|
+
if (len === 0) return [cx, cy];
|
|
536
|
+
const t = Math.min(dx ? rect.width / 2 / Math.abs(dx) : Infinity, dy ? rect.height / 2 / Math.abs(dy) : Infinity);
|
|
537
|
+
return [cx + dx * t + dx / len * gap, cy + dy * t + dy / len * gap];
|
|
538
|
+
}
|
|
539
|
+
var isRect = (end) => !Array.isArray(end);
|
|
540
|
+
function arrowGesture(from, to, seed, options = {}) {
|
|
541
|
+
const rand = gestureRandom(seed);
|
|
542
|
+
const gap = Math.max(0, _nullishCoalesce(options.gap, () => ( 6)));
|
|
543
|
+
const fromCentre = isRect(from) ? centre(from) : from;
|
|
544
|
+
const toCentre = isRect(to) ? centre(to) : to;
|
|
545
|
+
const start = isRect(from) ? gestureEdgePoint(from, toCentre, gap) : from;
|
|
546
|
+
const tip = isRect(to) ? gestureEdgePoint(to, fromCentre, gap) : to;
|
|
547
|
+
const dx = tip[0] - start[0];
|
|
548
|
+
const dy = tip[1] - start[1];
|
|
549
|
+
const len = Math.hypot(dx, dy);
|
|
550
|
+
if (len < 4) throw new Error("The arrow's ends are too close together (or overlap): pass ends at least a few canvas units apart.");
|
|
551
|
+
const curve = clamp(_nullishCoalesce(options.curve, () => ( (0.08 + rand() * 0.08) * (rand() < 0.5 ? -1 : 1))), -1, 1);
|
|
552
|
+
const nx = -dy / len;
|
|
553
|
+
const ny = dx / len;
|
|
554
|
+
const control = [(start[0] + tip[0]) / 2 + nx * curve * len, (start[1] + tip[1]) / 2 + ny * curve * len];
|
|
555
|
+
const noise = smoothNoise(rand);
|
|
556
|
+
const amp = Math.min(2, len * 0.01);
|
|
557
|
+
const n = clamp(Math.ceil(len / 4), 12, 200);
|
|
558
|
+
const shaft = Array.from({ length: n + 1 }, (_, i) => {
|
|
559
|
+
const t = i / n;
|
|
560
|
+
const u = 1 - t;
|
|
561
|
+
const x = u * u * start[0] + 2 * u * t * control[0] + t * t * tip[0];
|
|
562
|
+
const y = u * u * start[1] + 2 * u * t * control[1] + t * t * tip[1];
|
|
563
|
+
const off = amp * noise(t) * Math.sqrt(Math.sin(Math.PI * t));
|
|
564
|
+
return [x + nx * off, y + ny * off];
|
|
565
|
+
});
|
|
566
|
+
const tx = tip[0] - control[0];
|
|
567
|
+
const ty = tip[1] - control[1];
|
|
568
|
+
const tl = Math.hypot(tx, ty) || 1;
|
|
569
|
+
const head = clamp(len * 0.15, 8, 22);
|
|
570
|
+
const barb = (angle2, length) => {
|
|
571
|
+
const c = Math.cos(angle2);
|
|
572
|
+
const s = Math.sin(angle2);
|
|
573
|
+
const bx = -(tx / tl) * c + ty / tl * s;
|
|
574
|
+
const by = -(ty / tl) * c - tx / tl * s;
|
|
575
|
+
return [1 / 3, 2 / 3, 1].map((f) => [tip[0] + bx * length * f, tip[1] + by * length * f]);
|
|
576
|
+
};
|
|
577
|
+
const clear = (pts) => !isRect(to) || pts.every(([x, y]) => x <= to.x || x >= to.x + to.width || y <= to.y || y >= to.y + to.height);
|
|
578
|
+
const fit = (angle2) => {
|
|
579
|
+
const length = head * (0.85 + rand() * 0.3);
|
|
580
|
+
for (let k = 1; k > 0.05; k -= 0.15) {
|
|
581
|
+
const pts = barb(angle2 * k, length);
|
|
582
|
+
if (clear(pts)) return pts;
|
|
583
|
+
}
|
|
584
|
+
return barb(0, length);
|
|
585
|
+
};
|
|
586
|
+
const angle = () => (25 + rand() * 8) * Math.PI / 180;
|
|
587
|
+
const first = fit(angle());
|
|
588
|
+
const second = fit(-angle());
|
|
589
|
+
const points = [...shaft, ...first, ...first.slice(0, -1).reverse(), tip, ...second];
|
|
590
|
+
return { points: points.map(round), pressure: handPressure(rand) };
|
|
591
|
+
}
|
|
592
|
+
function bracketGesture(rect, side, seed, options = {}) {
|
|
593
|
+
const rand = gestureRandom(seed);
|
|
594
|
+
const pad = Math.max(0, _nullishCoalesce(options.padding, () => ( 8)));
|
|
595
|
+
const vertical = side === "left" || side === "right";
|
|
596
|
+
const span = vertical ? rect.height : rect.width;
|
|
597
|
+
const depth = Math.max(1, _nullishCoalesce(options.depth, () => ( clamp(span * 0.08, 6, 20))));
|
|
598
|
+
const from = vertical ? rect.y : rect.x;
|
|
599
|
+
const edge = side === "left" ? rect.x - pad : side === "right" ? rect.x + rect.width + pad : side === "top" ? rect.y - pad : rect.y + rect.height + pad;
|
|
600
|
+
const out = side === "left" || side === "top" ? -1 : 1;
|
|
601
|
+
const at = (s, d) => vertical ? [edge + out * d, from + s * span] : [from + s * span, edge + out * d];
|
|
602
|
+
const noise = smoothNoise(rand);
|
|
603
|
+
const shake = Math.min(1, depth * 0.06);
|
|
604
|
+
const n = clamp(Math.ceil(span / 4), 24, 240);
|
|
605
|
+
let points;
|
|
606
|
+
if (options.shape === "square") {
|
|
607
|
+
const arm = depth * (0.8 + rand() * 0.2);
|
|
608
|
+
const spine = Array.from({ length: n + 1 }, (_, i) => at(i / n, depth + shake * (1 + noise(i / n))));
|
|
609
|
+
points = [at(0, depth - arm), at(0, depth * 0.5), ...spine, at(1, depth * 0.5), at(1, depth - arm * (0.85 + rand() * 0.15))];
|
|
610
|
+
} else {
|
|
611
|
+
const e = clamp(depth / Math.max(span, 1), 0.04, 0.2);
|
|
612
|
+
const brace = (s) => {
|
|
613
|
+
const u = s <= 0.5 ? s : 1 - s;
|
|
614
|
+
if (u < e) return depth / 2 * Math.sin(Math.PI / 2 * u / e);
|
|
615
|
+
if (u < 0.5 - e) return depth / 2;
|
|
616
|
+
return depth / 2 + depth / 2 * (1 - Math.cos(Math.PI / 2 * (u - (0.5 - e)) / e));
|
|
617
|
+
};
|
|
618
|
+
points = Array.from({ length: n + 1 }, (_, i) => {
|
|
619
|
+
const s = i / n;
|
|
620
|
+
return at(s, brace(s) + shake * (1 + noise(s)) * Math.sin(Math.PI * s));
|
|
621
|
+
});
|
|
622
|
+
}
|
|
623
|
+
return { points: points.map(round), pressure: handPressure(rand) };
|
|
624
|
+
}
|
|
625
|
+
function strikeGesture(rect, seed) {
|
|
626
|
+
const rand = gestureRandom(seed);
|
|
627
|
+
const [, cy] = centre(rect);
|
|
628
|
+
const slope = rect.height * 0.08;
|
|
629
|
+
const x0 = rect.x - rect.width * (0.01 + rand() * 0.03);
|
|
630
|
+
const x1 = rect.x + rect.width * (1.01 + rand() * 0.03);
|
|
631
|
+
const y0 = cy + (rand() - 0.5) * rect.height * 0.1 + slope * (rand() - 0.5);
|
|
632
|
+
const y1 = cy + (rand() - 0.5) * rect.height * 0.1 + slope * (rand() - 0.5);
|
|
633
|
+
const points = wobblyLine([x0, y0], [x1, y1], rand, Math.min(1.5, rect.height * 0.05));
|
|
634
|
+
return { points: points.map(round), pressure: handPressure(rand) };
|
|
635
|
+
}
|
|
636
|
+
function crossGesture(rect, seed) {
|
|
637
|
+
const rand = gestureRandom(seed);
|
|
638
|
+
const over = () => 0.02 + rand() * 0.04;
|
|
639
|
+
const left = () => rect.x - rect.width * over();
|
|
640
|
+
const right = () => rect.x + rect.width * (1 + over());
|
|
641
|
+
const top = () => rect.y - rect.height * over();
|
|
642
|
+
const bottom = () => rect.y + rect.height * (1 + over());
|
|
643
|
+
const amp = Math.min(1.5, Math.min(rect.width, rect.height) * 0.03);
|
|
644
|
+
const first = wobblyLine([left(), top()], [right(), bottom()], rand, amp);
|
|
645
|
+
const second = wobblyLine([right(), top()], [left(), bottom()], rand, amp);
|
|
646
|
+
const mid1 = Math.floor(first.length / 2);
|
|
647
|
+
const mid2 = Math.floor(second.length / 2);
|
|
648
|
+
const points = [...first, ...first.slice(mid1, -1).reverse(), ...second.slice(1, mid2 + 1).reverse(), ...second];
|
|
649
|
+
return { points: points.map(round), pressure: handPressure(rand) };
|
|
650
|
+
}
|
|
651
|
+
function checkGesture(at, seed, options = {}) {
|
|
652
|
+
const rand = gestureRandom(seed);
|
|
653
|
+
const [cx, cy] = isRect(at) ? centre(at) : at;
|
|
654
|
+
const s = Math.max(4, _nullishCoalesce(options.size, () => ( (isRect(at) ? clamp(Math.min(at.width, at.height) * 0.6, 12, 48) : 20))));
|
|
655
|
+
const turn = (rand() - 0.5) * 12 * Math.PI / 180;
|
|
656
|
+
const place = ([x, y]) => [cx + s * (x * Math.cos(turn) - y * Math.sin(turn)), cy + s * (x * Math.sin(turn) + y * Math.cos(turn))];
|
|
657
|
+
const p0 = [-0.42 + rand() * 0.08, -0.05 + rand() * 0.08];
|
|
658
|
+
const p1 = [-0.12 + rand() * 0.06, 0.38 + rand() * 0.06];
|
|
659
|
+
const p2 = [0.48 + rand() * 0.06, -0.5 + rand() * 0.06];
|
|
660
|
+
const short = wobblyLine(place(p0), place(p1), rand, 0, -s * 0.03);
|
|
661
|
+
const long = wobblyLine(place(p1), place(p2), rand, Math.min(1, s * 0.02), s * 0.06);
|
|
662
|
+
return { points: [...short, ...long.slice(1)].map(round), pressure: [0.5, 0.95, 0.9, 0.7, 0.35] };
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
|
|
666
|
+
|
|
667
|
+
|
|
668
|
+
|
|
669
|
+
|
|
670
|
+
|
|
671
|
+
|
|
672
|
+
|
|
673
|
+
|
|
674
|
+
|
|
675
|
+
|
|
433
676
|
|
|
434
677
|
|
|
435
678
|
|
|
@@ -449,4 +692,4 @@ function matchArrangeShortcut(event, bindings = DEFAULT_ARRANGE_SHORTCUTS) {
|
|
|
449
692
|
|
|
450
693
|
|
|
451
694
|
|
|
452
|
-
exports.PLATFORMS = PLATFORMS; exports.detectPlatform = detectPlatform; exports.keyToCode = keyToCode; exports.codeToKeyText = codeToKeyText; exports.parseChord = parseChord; exports.chordToString = chordToString; exports.normalizeChord = normalizeChord; exports.effectiveChord = effectiveChord; exports.effectiveChordKey = effectiveChordKey; exports.formatShortcut = formatShortcut; exports.eventCode = eventCode; exports.shortcutCode = shortcutCode; exports.codeFromKey = codeFromKey; exports.isEditableEventTarget = isEditableEventTarget; exports.toShortcutBindings = toShortcutBindings; exports.ShortcutMap = ShortcutMap; exports.DEFAULT_ARRANGE_SHORTCUTS = DEFAULT_ARRANGE_SHORTCUTS; exports.matchArrangeShortcut = matchArrangeShortcut;
|
|
695
|
+
exports.PLATFORMS = PLATFORMS; exports.detectPlatform = detectPlatform; exports.keyToCode = keyToCode; exports.codeToKeyText = codeToKeyText; exports.parseChord = parseChord; exports.chordToString = chordToString; exports.normalizeChord = normalizeChord; exports.effectiveChord = effectiveChord; exports.effectiveChordKey = effectiveChordKey; exports.formatShortcut = formatShortcut; exports.eventCode = eventCode; exports.shortcutCode = shortcutCode; exports.codeFromKey = codeFromKey; exports.isEditableEventTarget = isEditableEventTarget; exports.toShortcutBindings = toShortcutBindings; exports.ShortcutMap = ShortcutMap; exports.DEFAULT_ARRANGE_SHORTCUTS = DEFAULT_ARRANGE_SHORTCUTS; exports.matchArrangeShortcut = matchArrangeShortcut; exports.gestureRandom = gestureRandom; exports.gestureSeed = gestureSeed; exports.gestureUnion = gestureUnion; exports.circleGesture = circleGesture; exports.underlineGesture = underlineGesture; exports.gestureEdgePoint = gestureEdgePoint; exports.arrowGesture = arrowGesture; exports.bracketGesture = bracketGesture; exports.strikeGesture = strikeGesture; exports.crossGesture = crossGesture; exports.checkGesture = checkGesture;
|