vite-plugin-specter 0.3.3 → 0.4.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/README.md +22 -4
- package/dist/client.js +254 -29
- package/dist/extension-chrome/background.js +4 -0
- package/dist/extension-chrome/content.js +1211 -0
- package/dist/extension-chrome/icons/icon128.png +0 -0
- package/dist/extension-chrome/icons/icon16.png +0 -0
- package/dist/extension-chrome/icons/icon32.png +0 -0
- package/dist/extension-chrome/icons/icon48.png +0 -0
- package/dist/extension-chrome/manifest.json +38 -0
- package/dist/extension-firefox/content.js +254 -29
- package/dist/extension-firefox/manifest.json +1 -1
- package/dist/index.cjs +254 -29
- package/dist/index.js +254 -29
- package/extension/background.js +1 -5
- package/extension/content.js +254 -29
- package/extension/icons/specter.png +0 -0
- package/extension/manifest.firefox.json +13 -5
- package/extension/manifest.json +8 -6
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -40,6 +40,7 @@ function getClientScript(options) {
|
|
|
40
40
|
var LABEL = '#8B8D94';
|
|
41
41
|
var MONO = "'JetBrains Mono', 'SF Mono', 'Fira Code', monospace";
|
|
42
42
|
var ZAP = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"></polygon></svg>';
|
|
43
|
+
var PENCIL = '<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z"></path><path d="m15 5 4 4"></path></svg>';
|
|
43
44
|
var ACTIVATE = ${JSON.stringify(activateShortcut)};
|
|
44
45
|
|
|
45
46
|
// \u2500\u2500\u2500 State \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
@@ -59,6 +60,9 @@ function getClientScript(options) {
|
|
|
59
60
|
var lastMouse = { x: 0, y: 0 };
|
|
60
61
|
var selectedEls = [];
|
|
61
62
|
var selectedBadges = [];
|
|
63
|
+
var noteMap = new Map();
|
|
64
|
+
var noteInputEl = null;
|
|
65
|
+
var noteTargetEl = null;
|
|
62
66
|
|
|
63
67
|
// \u2500\u2500\u2500 Tooltip \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
64
68
|
var tooltip = document.createElement('div');
|
|
@@ -218,11 +222,11 @@ function getClientScript(options) {
|
|
|
218
222
|
pillExpanded = true;
|
|
219
223
|
if (text) { pillText.textContent = text; return; }
|
|
220
224
|
if (selectedEls.length > 0) {
|
|
221
|
-
pillText.textContent = selectedEls.length + ' selected \xB7 Cmd+C copy all \xB7 Esc clear';
|
|
225
|
+
pillText.textContent = selectedEls.length + ' selected \xB7 N annotate \xB7 Cmd+C copy all \xB7 Esc clear';
|
|
222
226
|
} else if (measureMode) {
|
|
223
227
|
pillText.textContent = 'Measure \xB7 hover distances \xB7 M pin \xB7 Cmd+C copy \xB7 Option toggle';
|
|
224
228
|
} else {
|
|
225
|
-
pillText.textContent = 'Properties \xB7 hover
|
|
229
|
+
pillText.textContent = 'Properties \xB7 hover \xB7 P pick \xB7 N annotate \xB7 Cmd+C copy \xB7 Option measure';
|
|
226
230
|
}
|
|
227
231
|
}
|
|
228
232
|
|
|
@@ -303,17 +307,11 @@ function getClientScript(options) {
|
|
|
303
307
|
return { hex: toHex(m[1], m[2], m[3]), alpha: m[4] !== undefined ? parseFloat(m[4]) : 1 };
|
|
304
308
|
}
|
|
305
309
|
|
|
306
|
-
function colorLabel(str) {
|
|
307
|
-
var c = parseColor(str);
|
|
308
|
-
if (!c) return null;
|
|
309
|
-
if (c.alpha < 1) return c.hex + ' (' + Math.round(c.alpha * 100) + '% opacity)';
|
|
310
|
-
return c.hex;
|
|
311
|
-
}
|
|
312
|
-
|
|
313
310
|
function colorObj(str) {
|
|
314
311
|
var c = parseColor(str);
|
|
315
312
|
if (!c) return null;
|
|
316
|
-
|
|
313
|
+
var label = c.alpha < 1 ? c.hex + ' (' + Math.round(c.alpha * 100) + '% opacity)' : c.hex;
|
|
314
|
+
return { raw: str, hex: c.hex, alpha: c.alpha, label: label };
|
|
317
315
|
}
|
|
318
316
|
|
|
319
317
|
function edge(t, r, b, l) {
|
|
@@ -348,6 +346,55 @@ function getClientScript(options) {
|
|
|
348
346
|
return null;
|
|
349
347
|
}
|
|
350
348
|
|
|
349
|
+
// Collapse a rule's declarations to their shortest lossless form: drop
|
|
350
|
+
// 'initial' (= default, no signal) and always-'normal' noise, and fold
|
|
351
|
+
// side/corner longhands back into shorthands (margin/padding/radius/gap/transition).
|
|
352
|
+
function cleanDecls(style) {
|
|
353
|
+
var m = {}, order = [];
|
|
354
|
+
for (var i = 0; i < style.length; i++) {
|
|
355
|
+
var p = style[i];
|
|
356
|
+
var v = style.getPropertyValue(p);
|
|
357
|
+
if (!v) continue;
|
|
358
|
+
if (v === 'initial') continue;
|
|
359
|
+
if (p === 'transition-behavior') continue;
|
|
360
|
+
if (!(p in m)) order.push(p);
|
|
361
|
+
m[p] = v;
|
|
362
|
+
}
|
|
363
|
+
var used = {}, collapses = {};
|
|
364
|
+
function four(name, t, r, b, l) {
|
|
365
|
+
if (m[t] === undefined || m[r] === undefined || m[b] === undefined || m[l] === undefined) return;
|
|
366
|
+
used[t] = used[r] = used[b] = used[l] = 1;
|
|
367
|
+
var a = m[t], c = m[r], d = m[b], e = m[l], sh;
|
|
368
|
+
if (a === c && c === d && d === e) sh = a;
|
|
369
|
+
else if (a === d && c === e) sh = a + ' ' + c;
|
|
370
|
+
else if (c === e) sh = a + ' ' + c + ' ' + d;
|
|
371
|
+
else sh = a + ' ' + c + ' ' + d + ' ' + e;
|
|
372
|
+
collapses[t] = name + ': ' + sh;
|
|
373
|
+
}
|
|
374
|
+
four('margin', 'margin-top', 'margin-right', 'margin-bottom', 'margin-left');
|
|
375
|
+
four('padding', 'padding-top', 'padding-right', 'padding-bottom', 'padding-left');
|
|
376
|
+
four('border-radius', 'border-top-left-radius', 'border-top-right-radius', 'border-bottom-right-radius', 'border-bottom-left-radius');
|
|
377
|
+
if (m['row-gap'] !== undefined && m['column-gap'] !== undefined) {
|
|
378
|
+
var g = m['row-gap'] === m['column-gap'] ? m['row-gap'] : m['row-gap'] + ' ' + m['column-gap'];
|
|
379
|
+
collapses['row-gap'] = 'gap: ' + g; used['row-gap'] = used['column-gap'] = 1;
|
|
380
|
+
}
|
|
381
|
+
if (m['transition-property'] !== undefined) {
|
|
382
|
+
var tr = 'transition: ' + m['transition-property'];
|
|
383
|
+
if (m['transition-duration'] !== undefined) tr += ' ' + m['transition-duration'];
|
|
384
|
+
if (m['transition-timing-function'] !== undefined) tr += ' ' + m['transition-timing-function'];
|
|
385
|
+
if (m['transition-delay'] !== undefined && m['transition-delay'] !== '0s') tr += ' ' + m['transition-delay'];
|
|
386
|
+
collapses['transition-property'] = tr;
|
|
387
|
+
used['transition-property'] = used['transition-duration'] = used['transition-timing-function'] = used['transition-delay'] = 1;
|
|
388
|
+
}
|
|
389
|
+
var out = [];
|
|
390
|
+
order.forEach(function (p) {
|
|
391
|
+
if (collapses[p]) { out.push(' ' + collapses[p] + ';'); return; }
|
|
392
|
+
if (used[p]) return;
|
|
393
|
+
out.push(' ' + p + ': ' + m[p] + ';');
|
|
394
|
+
});
|
|
395
|
+
return out;
|
|
396
|
+
}
|
|
397
|
+
|
|
351
398
|
function getMatchingRules(el) {
|
|
352
399
|
var results = [];
|
|
353
400
|
try {
|
|
@@ -364,12 +411,7 @@ function getClientScript(options) {
|
|
|
364
411
|
if (/^[*,]|^:root|^html|^body$|^::before|^::after/.test(sel.trim())) continue;
|
|
365
412
|
try {
|
|
366
413
|
if (el.matches(sel)) {
|
|
367
|
-
var props =
|
|
368
|
-
for (var i = 0; i < rule.style.length; i++) {
|
|
369
|
-
var prop = rule.style[i];
|
|
370
|
-
var val = rule.style.getPropertyValue(prop);
|
|
371
|
-
if (val) props.push(' ' + prop + ': ' + val + ';');
|
|
372
|
-
}
|
|
414
|
+
var props = cleanDecls(rule.style);
|
|
373
415
|
if (props.length) results.push(sel + ' {\\n' + props.join('\\n') + '\\n}');
|
|
374
416
|
}
|
|
375
417
|
} catch (e) {}
|
|
@@ -489,9 +531,13 @@ function getClientScript(options) {
|
|
|
489
531
|
}
|
|
490
532
|
|
|
491
533
|
function buildMultiSelectCopyText() {
|
|
534
|
+
var n = selectedEls.length;
|
|
492
535
|
return selectedEls.map(function (el, i) {
|
|
493
536
|
var body = buildLLMClipboard(buildInfo(el));
|
|
494
|
-
|
|
537
|
+
var note = noteMap.get(el);
|
|
538
|
+
var header = n > 1 ? '[Specter ' + (i + 1) + '/' + n + ']' : '[Specter]';
|
|
539
|
+
if (note) header += '\\n\u270F\uFE0F CHANGE: ' + note;
|
|
540
|
+
return body.replace('[Specter]', header);
|
|
495
541
|
}).join('\\n\\n' + Array(41).join('\u2500') + '\\n\\n');
|
|
496
542
|
}
|
|
497
543
|
|
|
@@ -515,6 +561,7 @@ function getClientScript(options) {
|
|
|
515
561
|
function clearHoverOutline() {
|
|
516
562
|
if (outlinedEl) {
|
|
517
563
|
outlinedEl.style.outline = prevOutline;
|
|
564
|
+
outlinedEl.style.outlineOffset = '';
|
|
518
565
|
outlinedEl = null;
|
|
519
566
|
}
|
|
520
567
|
}
|
|
@@ -541,16 +588,25 @@ function getClientScript(options) {
|
|
|
541
588
|
}
|
|
542
589
|
|
|
543
590
|
// \u2500\u2500\u2500 Multi-select \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
544
|
-
function makeBadge(el, index) {
|
|
591
|
+
function makeBadge(el, index, note) {
|
|
545
592
|
var rect = el.getBoundingClientRect();
|
|
546
|
-
var
|
|
547
|
-
|
|
548
|
-
Object.assign(badge.style, {
|
|
593
|
+
var wrap = document.createElement('div');
|
|
594
|
+
Object.assign(wrap.style, {
|
|
549
595
|
position: 'fixed',
|
|
550
596
|
left: (rect.left - 4) + 'px',
|
|
551
597
|
top: (rect.top - 4) + 'px',
|
|
598
|
+
zIndex: '2147483644',
|
|
599
|
+
pointerEvents: 'none',
|
|
600
|
+
display: 'flex',
|
|
601
|
+
alignItems: 'flex-start',
|
|
602
|
+
gap: '4px',
|
|
603
|
+
});
|
|
604
|
+
var badge = document.createElement('div');
|
|
605
|
+
badge.textContent = String(index + 1);
|
|
606
|
+
Object.assign(badge.style, {
|
|
552
607
|
width: '20px',
|
|
553
608
|
height: '20px',
|
|
609
|
+
flexShrink: '0',
|
|
554
610
|
background: PURPLE,
|
|
555
611
|
color: '#fff',
|
|
556
612
|
fontSize: '11px',
|
|
@@ -560,19 +616,38 @@ function getClientScript(options) {
|
|
|
560
616
|
display: 'flex',
|
|
561
617
|
alignItems: 'center',
|
|
562
618
|
justifyContent: 'center',
|
|
563
|
-
zIndex: '2147483644',
|
|
564
|
-
pointerEvents: 'none',
|
|
565
619
|
boxShadow: '0 2px 6px rgba(0,0,0,0.3)',
|
|
566
620
|
});
|
|
567
|
-
|
|
568
|
-
|
|
621
|
+
wrap.appendChild(badge);
|
|
622
|
+
if (note) {
|
|
623
|
+
var chip = document.createElement('div');
|
|
624
|
+
chip.textContent = '\u270F\uFE0F ' + (note.length > 32 ? note.slice(0, 32) + '\u2026' : note);
|
|
625
|
+
Object.assign(chip.style, {
|
|
626
|
+
maxWidth: '240px',
|
|
627
|
+
background: TIP_BG,
|
|
628
|
+
color: '#fff',
|
|
629
|
+
fontSize: '10px',
|
|
630
|
+
lineHeight: '16px',
|
|
631
|
+
fontFamily: MONO,
|
|
632
|
+
padding: '2px 6px',
|
|
633
|
+
borderRadius: '4px',
|
|
634
|
+
whiteSpace: 'nowrap',
|
|
635
|
+
overflow: 'hidden',
|
|
636
|
+
textOverflow: 'ellipsis',
|
|
637
|
+
border: '1px solid ' + PURPLE,
|
|
638
|
+
boxShadow: '0 2px 6px rgba(0,0,0,0.3)',
|
|
639
|
+
});
|
|
640
|
+
wrap.appendChild(chip);
|
|
641
|
+
}
|
|
642
|
+
document.body.appendChild(wrap);
|
|
643
|
+
return wrap;
|
|
569
644
|
}
|
|
570
645
|
|
|
571
646
|
function rebuildBadges() {
|
|
572
647
|
selectedBadges.forEach(function (b) { b.remove(); });
|
|
573
648
|
selectedBadges.length = 0;
|
|
574
649
|
selectedEls.forEach(function (el, i) {
|
|
575
|
-
selectedBadges.push(makeBadge(el, i));
|
|
650
|
+
selectedBadges.push(makeBadge(el, i, noteMap.get(el)));
|
|
576
651
|
});
|
|
577
652
|
}
|
|
578
653
|
|
|
@@ -581,6 +656,7 @@ function getClientScript(options) {
|
|
|
581
656
|
if (idx >= 0) {
|
|
582
657
|
selectedEls.splice(idx, 1);
|
|
583
658
|
el.style.outline = '';
|
|
659
|
+
noteMap.delete(el);
|
|
584
660
|
} else {
|
|
585
661
|
selectedEls.push(el);
|
|
586
662
|
el.style.outline = '2px solid ' + PURPLE;
|
|
@@ -591,10 +667,12 @@ function getClientScript(options) {
|
|
|
591
667
|
}
|
|
592
668
|
|
|
593
669
|
function clearSelection() {
|
|
594
|
-
|
|
670
|
+
closeNoteInput();
|
|
671
|
+
selectedEls.forEach(function (el) { el.style.outline = ''; el.style.outlineOffset = ''; });
|
|
595
672
|
selectedEls.length = 0;
|
|
596
673
|
selectedBadges.forEach(function (b) { b.remove(); });
|
|
597
674
|
selectedBadges.length = 0;
|
|
675
|
+
noteMap.clear();
|
|
598
676
|
}
|
|
599
677
|
|
|
600
678
|
function updatePillForSelection() {
|
|
@@ -602,6 +680,142 @@ function getClientScript(options) {
|
|
|
602
680
|
else if (!pillWrap.matches(':hover')) collapsePill();
|
|
603
681
|
}
|
|
604
682
|
|
|
683
|
+
// \u2500\u2500\u2500 Annotations (inline change notes) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
684
|
+
function openNoteInput(el) {
|
|
685
|
+
closeNoteInput();
|
|
686
|
+
if (selectedEls.indexOf(el) < 0) {
|
|
687
|
+
clearHoverOutline();
|
|
688
|
+
hideTooltip();
|
|
689
|
+
selectedEls.push(el);
|
|
690
|
+
el.style.outline = '2px solid ' + PURPLE;
|
|
691
|
+
el.style.outlineOffset = '-1px';
|
|
692
|
+
}
|
|
693
|
+
noteTargetEl = el;
|
|
694
|
+
var rect = el.getBoundingClientRect();
|
|
695
|
+
|
|
696
|
+
var box = document.createElement('div');
|
|
697
|
+
Object.assign(box.style, {
|
|
698
|
+
position: 'fixed',
|
|
699
|
+
zIndex: '2147483647',
|
|
700
|
+
display: 'inline-flex',
|
|
701
|
+
alignItems: 'flex-start',
|
|
702
|
+
gap: '8px',
|
|
703
|
+
boxSizing: 'border-box',
|
|
704
|
+
background: TIP_BG,
|
|
705
|
+
border: '1px solid ' + PURPLE,
|
|
706
|
+
borderRadius: '8px',
|
|
707
|
+
padding: '11px 12px',
|
|
708
|
+
boxShadow: '0 4px 16px rgba(0,0,0,0.35)',
|
|
709
|
+
fontFamily: MONO,
|
|
710
|
+
});
|
|
711
|
+
var pen = document.createElement('span');
|
|
712
|
+
pen.innerHTML = PENCIL;
|
|
713
|
+
Object.assign(pen.style, {
|
|
714
|
+
display: 'flex',
|
|
715
|
+
alignItems: 'center',
|
|
716
|
+
flexShrink: '0',
|
|
717
|
+
marginTop: '2px',
|
|
718
|
+
color: '#E0A3F5',
|
|
719
|
+
});
|
|
720
|
+
var input = document.createElement('textarea');
|
|
721
|
+
input.rows = 1;
|
|
722
|
+
input.placeholder = 'Describe the change\u2026 (Enter to save)';
|
|
723
|
+
input.value = noteMap.get(el) || '';
|
|
724
|
+
Object.assign(input.style, {
|
|
725
|
+
flexShrink: '0',
|
|
726
|
+
background: 'transparent',
|
|
727
|
+
border: 'none',
|
|
728
|
+
outline: 'none',
|
|
729
|
+
resize: 'none',
|
|
730
|
+
overflow: 'hidden',
|
|
731
|
+
color: '#fff',
|
|
732
|
+
fontFamily: MONO,
|
|
733
|
+
fontSize: '12px',
|
|
734
|
+
lineHeight: '18px',
|
|
735
|
+
padding: '0',
|
|
736
|
+
margin: '0',
|
|
737
|
+
whiteSpace: 'pre-wrap',
|
|
738
|
+
overflowWrap: 'anywhere',
|
|
739
|
+
});
|
|
740
|
+
// Hidden mirror to measure single-line text width so the field grows as you type.
|
|
741
|
+
var meas = document.createElement('span');
|
|
742
|
+
Object.assign(meas.style, {
|
|
743
|
+
position: 'absolute',
|
|
744
|
+
visibility: 'hidden',
|
|
745
|
+
whiteSpace: 'pre',
|
|
746
|
+
pointerEvents: 'none',
|
|
747
|
+
fontFamily: MONO,
|
|
748
|
+
fontSize: '12px',
|
|
749
|
+
left: '-9999px',
|
|
750
|
+
top: '0',
|
|
751
|
+
});
|
|
752
|
+
box.appendChild(pen);
|
|
753
|
+
box.appendChild(input);
|
|
754
|
+
box.appendChild(meas);
|
|
755
|
+
document.body.appendChild(box);
|
|
756
|
+
noteInputEl = box;
|
|
757
|
+
|
|
758
|
+
var margin = 8;
|
|
759
|
+
function textW(t) { meas.textContent = t; return meas.offsetWidth; }
|
|
760
|
+
|
|
761
|
+
// Anchor to the element; the box is repositioned around this as it grows.
|
|
762
|
+
var anchorL = rect.left, anchorT = rect.top, anchorB = rect.bottom;
|
|
763
|
+
|
|
764
|
+
function sizeAndPosition() {
|
|
765
|
+
var vw = window.innerWidth, vh = window.innerHeight;
|
|
766
|
+
var maxBoxW = Math.min(560, vw - margin * 2);
|
|
767
|
+
var maxTextW = Math.max(120, maxBoxW - 55); // room for icon + gaps + padding
|
|
768
|
+
var placeholderW = textW(input.placeholder);
|
|
769
|
+
var minTextW = Math.min(placeholderW, maxTextW);
|
|
770
|
+
var maxTaH = Math.min(14 * 18, Math.max(18, (vh - margin * 2) - 24)); // cap ~14 lines, never past viewport
|
|
771
|
+
|
|
772
|
+
// Width: grow with content up to the max, then wrapping takes over.
|
|
773
|
+
var single = textW(input.value || input.placeholder) + 3;
|
|
774
|
+
input.style.width = Math.min(Math.max(single, minTextW), maxTextW) + 'px';
|
|
775
|
+
// Height: fit wrapped content, scroll only in the extreme case.
|
|
776
|
+
input.style.height = 'auto';
|
|
777
|
+
var h = input.scrollHeight;
|
|
778
|
+
if (h > maxTaH) { input.style.height = maxTaH + 'px'; input.style.overflowY = 'auto'; }
|
|
779
|
+
else { input.style.height = h + 'px'; input.style.overflowY = 'hidden'; }
|
|
780
|
+
|
|
781
|
+
// Keep the whole box on screen.
|
|
782
|
+
var bw = box.offsetWidth, bh = box.offsetHeight;
|
|
783
|
+
var left = Math.min(Math.max(anchorL, margin), Math.max(margin, vw - bw - margin));
|
|
784
|
+
var top = anchorT - bh - 8; // prefer sitting above the element
|
|
785
|
+
if (top < margin) top = anchorB + 8; // otherwise below it
|
|
786
|
+
if (top + bh > vh - margin) top = Math.max(margin, vh - bh - margin);
|
|
787
|
+
box.style.left = left + 'px';
|
|
788
|
+
box.style.top = top + 'px';
|
|
789
|
+
}
|
|
790
|
+
sizeAndPosition();
|
|
791
|
+
|
|
792
|
+
input.addEventListener('input', sizeAndPosition);
|
|
793
|
+
input.addEventListener('keydown', function (ev) {
|
|
794
|
+
ev.stopPropagation();
|
|
795
|
+
if (ev.key === 'Enter' && !ev.shiftKey) { ev.preventDefault(); commitNote(input.value); }
|
|
796
|
+
else if (ev.key === 'Escape') { ev.preventDefault(); closeNoteInput(); }
|
|
797
|
+
});
|
|
798
|
+
|
|
799
|
+
rebuildBadges();
|
|
800
|
+
updatePillForSelection();
|
|
801
|
+
setTimeout(function () { input.focus(); }, 0);
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
function commitNote(val) {
|
|
805
|
+
val = (val || '').trim();
|
|
806
|
+
if (noteTargetEl) {
|
|
807
|
+
if (val) noteMap.set(noteTargetEl, val);
|
|
808
|
+
else noteMap.delete(noteTargetEl);
|
|
809
|
+
}
|
|
810
|
+
closeNoteInput();
|
|
811
|
+
rebuildBadges();
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
function closeNoteInput() {
|
|
815
|
+
if (noteInputEl) { noteInputEl.remove(); noteInputEl = null; }
|
|
816
|
+
noteTargetEl = null;
|
|
817
|
+
}
|
|
818
|
+
|
|
605
819
|
// \u2500\u2500\u2500 Pin (measure) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
606
820
|
function setPin(el) {
|
|
607
821
|
pinEl = el;
|
|
@@ -898,6 +1112,7 @@ function getClientScript(options) {
|
|
|
898
1112
|
var target = e.target;
|
|
899
1113
|
if (!target || target === pillWrap || pillWrap.contains(target)) return;
|
|
900
1114
|
if (target === tooltip || tooltip.contains(target)) return;
|
|
1115
|
+
if (noteInputEl && (target === noteInputEl || noteInputEl.contains(target))) return;
|
|
901
1116
|
|
|
902
1117
|
lastHovered = target;
|
|
903
1118
|
|
|
@@ -932,6 +1147,9 @@ function getClientScript(options) {
|
|
|
932
1147
|
|
|
933
1148
|
if (!fiActive) return;
|
|
934
1149
|
|
|
1150
|
+
// Note editor open: let the field handle its own keys (Enter/Esc) and native copy/paste.
|
|
1151
|
+
if (noteInputEl) return;
|
|
1152
|
+
|
|
935
1153
|
if (e.key === 'Alt' && !e.ctrlKey && !e.metaKey && !e.shiftKey) {
|
|
936
1154
|
optionHeld = true;
|
|
937
1155
|
return;
|
|
@@ -962,6 +1180,12 @@ function getClientScript(options) {
|
|
|
962
1180
|
return;
|
|
963
1181
|
}
|
|
964
1182
|
|
|
1183
|
+
if (e.key === 'n' || e.key === 'N') {
|
|
1184
|
+
if (!lastHovered) return;
|
|
1185
|
+
openNoteInput(lastHovered);
|
|
1186
|
+
return;
|
|
1187
|
+
}
|
|
1188
|
+
|
|
965
1189
|
if (e.key === 'm' || e.key === 'M') {
|
|
966
1190
|
if (!measureMode || !lastHovered) return;
|
|
967
1191
|
if (pinEl) {
|
|
@@ -1006,8 +1230,9 @@ function getClientScript(options) {
|
|
|
1006
1230
|
|
|
1007
1231
|
window.__specterToggle = function() { if (fiActive) deactivate(); else activate(); };
|
|
1008
1232
|
|
|
1009
|
-
|
|
1010
|
-
|
|
1233
|
+
var _rt = (typeof browser !== 'undefined' && browser.runtime) || (typeof chrome !== 'undefined' && chrome.runtime);
|
|
1234
|
+
if (_rt && _rt.onMessage) {
|
|
1235
|
+
_rt.onMessage.addListener(function(msg) {
|
|
1011
1236
|
if (msg && msg.type === 'specter-toggle') window.__specterToggle();
|
|
1012
1237
|
});
|
|
1013
1238
|
}
|