vite-plugin-specter 0.3.4 → 0.4.1
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 -19
- package/dist/extension-chrome/content.js +254 -19
- package/dist/extension-chrome/manifest.json +2 -2
- package/dist/extension-firefox/content.js +254 -19
- package/dist/extension-firefox/manifest.json +2 -8
- package/dist/index.cjs +254 -19
- package/dist/index.js +254 -19
- package/extension/content.js +254 -19
- package/extension/icons/specter.png +0 -0
- package/extension/manifest.firefox.json +3 -9
- package/extension/manifest.json +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -30,7 +30,13 @@ function getClientScript(options) {
|
|
|
30
30
|
const activateShortcut = options.shortcuts?.activate ?? "ctrl+alt+z";
|
|
31
31
|
return `(function() {
|
|
32
32
|
'use strict';
|
|
33
|
-
|
|
33
|
+
// Guard against running twice on one page. Use a DOM marker (not just a
|
|
34
|
+
// window global) so the browser-extension build (isolated world) and the
|
|
35
|
+
// Vite-plugin build (page main world) can see each other and only one runs \u2014
|
|
36
|
+
// they share the DOM but not window globals, so a per-world flag misses.
|
|
37
|
+
if (window.__specter || document.documentElement.hasAttribute('data-specter')) return;
|
|
38
|
+
window.__specter = true;
|
|
39
|
+
document.documentElement.setAttribute('data-specter', '');
|
|
34
40
|
|
|
35
41
|
// \u2500\u2500\u2500 Constants \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
|
|
36
42
|
var PURPLE = '#AD24D3';
|
|
@@ -40,6 +46,7 @@ function getClientScript(options) {
|
|
|
40
46
|
var LABEL = '#8B8D94';
|
|
41
47
|
var MONO = "'JetBrains Mono', 'SF Mono', 'Fira Code', monospace";
|
|
42
48
|
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>';
|
|
49
|
+
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
50
|
var ACTIVATE = ${JSON.stringify(activateShortcut)};
|
|
44
51
|
|
|
45
52
|
// \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 +66,9 @@ function getClientScript(options) {
|
|
|
59
66
|
var lastMouse = { x: 0, y: 0 };
|
|
60
67
|
var selectedEls = [];
|
|
61
68
|
var selectedBadges = [];
|
|
69
|
+
var noteMap = new Map();
|
|
70
|
+
var noteInputEl = null;
|
|
71
|
+
var noteTargetEl = null;
|
|
62
72
|
|
|
63
73
|
// \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
74
|
var tooltip = document.createElement('div');
|
|
@@ -218,11 +228,11 @@ function getClientScript(options) {
|
|
|
218
228
|
pillExpanded = true;
|
|
219
229
|
if (text) { pillText.textContent = text; return; }
|
|
220
230
|
if (selectedEls.length > 0) {
|
|
221
|
-
pillText.textContent = selectedEls.length + ' selected \xB7 Cmd+C copy all \xB7 Esc clear';
|
|
231
|
+
pillText.textContent = selectedEls.length + ' selected \xB7 N annotate \xB7 Cmd+C copy all \xB7 Esc clear';
|
|
222
232
|
} else if (measureMode) {
|
|
223
233
|
pillText.textContent = 'Measure \xB7 hover distances \xB7 M pin \xB7 Cmd+C copy \xB7 Option toggle';
|
|
224
234
|
} else {
|
|
225
|
-
pillText.textContent = 'Properties \xB7 hover
|
|
235
|
+
pillText.textContent = 'Properties \xB7 hover \xB7 P pick \xB7 N annotate \xB7 Cmd+C copy \xB7 Option measure';
|
|
226
236
|
}
|
|
227
237
|
}
|
|
228
238
|
|
|
@@ -342,6 +352,55 @@ function getClientScript(options) {
|
|
|
342
352
|
return null;
|
|
343
353
|
}
|
|
344
354
|
|
|
355
|
+
// Collapse a rule's declarations to their shortest lossless form: drop
|
|
356
|
+
// 'initial' (= default, no signal) and always-'normal' noise, and fold
|
|
357
|
+
// side/corner longhands back into shorthands (margin/padding/radius/gap/transition).
|
|
358
|
+
function cleanDecls(style) {
|
|
359
|
+
var m = {}, order = [];
|
|
360
|
+
for (var i = 0; i < style.length; i++) {
|
|
361
|
+
var p = style[i];
|
|
362
|
+
var v = style.getPropertyValue(p);
|
|
363
|
+
if (!v) continue;
|
|
364
|
+
if (v === 'initial') continue;
|
|
365
|
+
if (p === 'transition-behavior') continue;
|
|
366
|
+
if (!(p in m)) order.push(p);
|
|
367
|
+
m[p] = v;
|
|
368
|
+
}
|
|
369
|
+
var used = {}, collapses = {};
|
|
370
|
+
function four(name, t, r, b, l) {
|
|
371
|
+
if (m[t] === undefined || m[r] === undefined || m[b] === undefined || m[l] === undefined) return;
|
|
372
|
+
used[t] = used[r] = used[b] = used[l] = 1;
|
|
373
|
+
var a = m[t], c = m[r], d = m[b], e = m[l], sh;
|
|
374
|
+
if (a === c && c === d && d === e) sh = a;
|
|
375
|
+
else if (a === d && c === e) sh = a + ' ' + c;
|
|
376
|
+
else if (c === e) sh = a + ' ' + c + ' ' + d;
|
|
377
|
+
else sh = a + ' ' + c + ' ' + d + ' ' + e;
|
|
378
|
+
collapses[t] = name + ': ' + sh;
|
|
379
|
+
}
|
|
380
|
+
four('margin', 'margin-top', 'margin-right', 'margin-bottom', 'margin-left');
|
|
381
|
+
four('padding', 'padding-top', 'padding-right', 'padding-bottom', 'padding-left');
|
|
382
|
+
four('border-radius', 'border-top-left-radius', 'border-top-right-radius', 'border-bottom-right-radius', 'border-bottom-left-radius');
|
|
383
|
+
if (m['row-gap'] !== undefined && m['column-gap'] !== undefined) {
|
|
384
|
+
var g = m['row-gap'] === m['column-gap'] ? m['row-gap'] : m['row-gap'] + ' ' + m['column-gap'];
|
|
385
|
+
collapses['row-gap'] = 'gap: ' + g; used['row-gap'] = used['column-gap'] = 1;
|
|
386
|
+
}
|
|
387
|
+
if (m['transition-property'] !== undefined) {
|
|
388
|
+
var tr = 'transition: ' + m['transition-property'];
|
|
389
|
+
if (m['transition-duration'] !== undefined) tr += ' ' + m['transition-duration'];
|
|
390
|
+
if (m['transition-timing-function'] !== undefined) tr += ' ' + m['transition-timing-function'];
|
|
391
|
+
if (m['transition-delay'] !== undefined && m['transition-delay'] !== '0s') tr += ' ' + m['transition-delay'];
|
|
392
|
+
collapses['transition-property'] = tr;
|
|
393
|
+
used['transition-property'] = used['transition-duration'] = used['transition-timing-function'] = used['transition-delay'] = 1;
|
|
394
|
+
}
|
|
395
|
+
var out = [];
|
|
396
|
+
order.forEach(function (p) {
|
|
397
|
+
if (collapses[p]) { out.push(' ' + collapses[p] + ';'); return; }
|
|
398
|
+
if (used[p]) return;
|
|
399
|
+
out.push(' ' + p + ': ' + m[p] + ';');
|
|
400
|
+
});
|
|
401
|
+
return out;
|
|
402
|
+
}
|
|
403
|
+
|
|
345
404
|
function getMatchingRules(el) {
|
|
346
405
|
var results = [];
|
|
347
406
|
try {
|
|
@@ -358,12 +417,7 @@ function getClientScript(options) {
|
|
|
358
417
|
if (/^[*,]|^:root|^html|^body$|^::before|^::after/.test(sel.trim())) continue;
|
|
359
418
|
try {
|
|
360
419
|
if (el.matches(sel)) {
|
|
361
|
-
var props =
|
|
362
|
-
for (var i = 0; i < rule.style.length; i++) {
|
|
363
|
-
var prop = rule.style[i];
|
|
364
|
-
var val = rule.style.getPropertyValue(prop);
|
|
365
|
-
if (val) props.push(' ' + prop + ': ' + val + ';');
|
|
366
|
-
}
|
|
420
|
+
var props = cleanDecls(rule.style);
|
|
367
421
|
if (props.length) results.push(sel + ' {\\n' + props.join('\\n') + '\\n}');
|
|
368
422
|
}
|
|
369
423
|
} catch (e) {}
|
|
@@ -483,9 +537,13 @@ function getClientScript(options) {
|
|
|
483
537
|
}
|
|
484
538
|
|
|
485
539
|
function buildMultiSelectCopyText() {
|
|
540
|
+
var n = selectedEls.length;
|
|
486
541
|
return selectedEls.map(function (el, i) {
|
|
487
542
|
var body = buildLLMClipboard(buildInfo(el));
|
|
488
|
-
|
|
543
|
+
var note = noteMap.get(el);
|
|
544
|
+
var header = n > 1 ? '[Specter ' + (i + 1) + '/' + n + ']' : '[Specter]';
|
|
545
|
+
if (note) header += '\\n\u270F\uFE0F CHANGE: ' + note;
|
|
546
|
+
return body.replace('[Specter]', header);
|
|
489
547
|
}).join('\\n\\n' + Array(41).join('\u2500') + '\\n\\n');
|
|
490
548
|
}
|
|
491
549
|
|
|
@@ -536,16 +594,25 @@ function getClientScript(options) {
|
|
|
536
594
|
}
|
|
537
595
|
|
|
538
596
|
// \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
|
|
539
|
-
function makeBadge(el, index) {
|
|
597
|
+
function makeBadge(el, index, note) {
|
|
540
598
|
var rect = el.getBoundingClientRect();
|
|
541
|
-
var
|
|
542
|
-
|
|
543
|
-
Object.assign(badge.style, {
|
|
599
|
+
var wrap = document.createElement('div');
|
|
600
|
+
Object.assign(wrap.style, {
|
|
544
601
|
position: 'fixed',
|
|
545
602
|
left: (rect.left - 4) + 'px',
|
|
546
603
|
top: (rect.top - 4) + 'px',
|
|
604
|
+
zIndex: '2147483644',
|
|
605
|
+
pointerEvents: 'none',
|
|
606
|
+
display: 'flex',
|
|
607
|
+
alignItems: 'flex-start',
|
|
608
|
+
gap: '4px',
|
|
609
|
+
});
|
|
610
|
+
var badge = document.createElement('div');
|
|
611
|
+
badge.textContent = String(index + 1);
|
|
612
|
+
Object.assign(badge.style, {
|
|
547
613
|
width: '20px',
|
|
548
614
|
height: '20px',
|
|
615
|
+
flexShrink: '0',
|
|
549
616
|
background: PURPLE,
|
|
550
617
|
color: '#fff',
|
|
551
618
|
fontSize: '11px',
|
|
@@ -555,19 +622,38 @@ function getClientScript(options) {
|
|
|
555
622
|
display: 'flex',
|
|
556
623
|
alignItems: 'center',
|
|
557
624
|
justifyContent: 'center',
|
|
558
|
-
zIndex: '2147483644',
|
|
559
|
-
pointerEvents: 'none',
|
|
560
625
|
boxShadow: '0 2px 6px rgba(0,0,0,0.3)',
|
|
561
626
|
});
|
|
562
|
-
|
|
563
|
-
|
|
627
|
+
wrap.appendChild(badge);
|
|
628
|
+
if (note) {
|
|
629
|
+
var chip = document.createElement('div');
|
|
630
|
+
chip.textContent = '\u270F\uFE0F ' + (note.length > 32 ? note.slice(0, 32) + '\u2026' : note);
|
|
631
|
+
Object.assign(chip.style, {
|
|
632
|
+
maxWidth: '240px',
|
|
633
|
+
background: TIP_BG,
|
|
634
|
+
color: '#fff',
|
|
635
|
+
fontSize: '10px',
|
|
636
|
+
lineHeight: '16px',
|
|
637
|
+
fontFamily: MONO,
|
|
638
|
+
padding: '2px 6px',
|
|
639
|
+
borderRadius: '4px',
|
|
640
|
+
whiteSpace: 'nowrap',
|
|
641
|
+
overflow: 'hidden',
|
|
642
|
+
textOverflow: 'ellipsis',
|
|
643
|
+
border: '1px solid ' + PURPLE,
|
|
644
|
+
boxShadow: '0 2px 6px rgba(0,0,0,0.3)',
|
|
645
|
+
});
|
|
646
|
+
wrap.appendChild(chip);
|
|
647
|
+
}
|
|
648
|
+
document.body.appendChild(wrap);
|
|
649
|
+
return wrap;
|
|
564
650
|
}
|
|
565
651
|
|
|
566
652
|
function rebuildBadges() {
|
|
567
653
|
selectedBadges.forEach(function (b) { b.remove(); });
|
|
568
654
|
selectedBadges.length = 0;
|
|
569
655
|
selectedEls.forEach(function (el, i) {
|
|
570
|
-
selectedBadges.push(makeBadge(el, i));
|
|
656
|
+
selectedBadges.push(makeBadge(el, i, noteMap.get(el)));
|
|
571
657
|
});
|
|
572
658
|
}
|
|
573
659
|
|
|
@@ -576,6 +662,7 @@ function getClientScript(options) {
|
|
|
576
662
|
if (idx >= 0) {
|
|
577
663
|
selectedEls.splice(idx, 1);
|
|
578
664
|
el.style.outline = '';
|
|
665
|
+
noteMap.delete(el);
|
|
579
666
|
} else {
|
|
580
667
|
selectedEls.push(el);
|
|
581
668
|
el.style.outline = '2px solid ' + PURPLE;
|
|
@@ -586,10 +673,12 @@ function getClientScript(options) {
|
|
|
586
673
|
}
|
|
587
674
|
|
|
588
675
|
function clearSelection() {
|
|
676
|
+
closeNoteInput();
|
|
589
677
|
selectedEls.forEach(function (el) { el.style.outline = ''; el.style.outlineOffset = ''; });
|
|
590
678
|
selectedEls.length = 0;
|
|
591
679
|
selectedBadges.forEach(function (b) { b.remove(); });
|
|
592
680
|
selectedBadges.length = 0;
|
|
681
|
+
noteMap.clear();
|
|
593
682
|
}
|
|
594
683
|
|
|
595
684
|
function updatePillForSelection() {
|
|
@@ -597,6 +686,142 @@ function getClientScript(options) {
|
|
|
597
686
|
else if (!pillWrap.matches(':hover')) collapsePill();
|
|
598
687
|
}
|
|
599
688
|
|
|
689
|
+
// \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
|
|
690
|
+
function openNoteInput(el) {
|
|
691
|
+
closeNoteInput();
|
|
692
|
+
if (selectedEls.indexOf(el) < 0) {
|
|
693
|
+
clearHoverOutline();
|
|
694
|
+
hideTooltip();
|
|
695
|
+
selectedEls.push(el);
|
|
696
|
+
el.style.outline = '2px solid ' + PURPLE;
|
|
697
|
+
el.style.outlineOffset = '-1px';
|
|
698
|
+
}
|
|
699
|
+
noteTargetEl = el;
|
|
700
|
+
var rect = el.getBoundingClientRect();
|
|
701
|
+
|
|
702
|
+
var box = document.createElement('div');
|
|
703
|
+
Object.assign(box.style, {
|
|
704
|
+
position: 'fixed',
|
|
705
|
+
zIndex: '2147483647',
|
|
706
|
+
display: 'inline-flex',
|
|
707
|
+
alignItems: 'flex-start',
|
|
708
|
+
gap: '8px',
|
|
709
|
+
boxSizing: 'border-box',
|
|
710
|
+
background: TIP_BG,
|
|
711
|
+
border: '1px solid ' + PURPLE,
|
|
712
|
+
borderRadius: '8px',
|
|
713
|
+
padding: '11px 12px',
|
|
714
|
+
boxShadow: '0 4px 16px rgba(0,0,0,0.35)',
|
|
715
|
+
fontFamily: MONO,
|
|
716
|
+
});
|
|
717
|
+
var pen = document.createElement('span');
|
|
718
|
+
pen.innerHTML = PENCIL;
|
|
719
|
+
Object.assign(pen.style, {
|
|
720
|
+
display: 'flex',
|
|
721
|
+
alignItems: 'center',
|
|
722
|
+
flexShrink: '0',
|
|
723
|
+
marginTop: '2px',
|
|
724
|
+
color: '#E0A3F5',
|
|
725
|
+
});
|
|
726
|
+
var input = document.createElement('textarea');
|
|
727
|
+
input.rows = 1;
|
|
728
|
+
input.placeholder = 'Describe the change\u2026 (Enter to save)';
|
|
729
|
+
input.value = noteMap.get(el) || '';
|
|
730
|
+
Object.assign(input.style, {
|
|
731
|
+
flexShrink: '0',
|
|
732
|
+
background: 'transparent',
|
|
733
|
+
border: 'none',
|
|
734
|
+
outline: 'none',
|
|
735
|
+
resize: 'none',
|
|
736
|
+
overflow: 'hidden',
|
|
737
|
+
color: '#fff',
|
|
738
|
+
fontFamily: MONO,
|
|
739
|
+
fontSize: '12px',
|
|
740
|
+
lineHeight: '18px',
|
|
741
|
+
padding: '0',
|
|
742
|
+
margin: '0',
|
|
743
|
+
whiteSpace: 'pre-wrap',
|
|
744
|
+
overflowWrap: 'anywhere',
|
|
745
|
+
});
|
|
746
|
+
// Hidden mirror to measure single-line text width so the field grows as you type.
|
|
747
|
+
var meas = document.createElement('span');
|
|
748
|
+
Object.assign(meas.style, {
|
|
749
|
+
position: 'absolute',
|
|
750
|
+
visibility: 'hidden',
|
|
751
|
+
whiteSpace: 'pre',
|
|
752
|
+
pointerEvents: 'none',
|
|
753
|
+
fontFamily: MONO,
|
|
754
|
+
fontSize: '12px',
|
|
755
|
+
left: '-9999px',
|
|
756
|
+
top: '0',
|
|
757
|
+
});
|
|
758
|
+
box.appendChild(pen);
|
|
759
|
+
box.appendChild(input);
|
|
760
|
+
box.appendChild(meas);
|
|
761
|
+
document.body.appendChild(box);
|
|
762
|
+
noteInputEl = box;
|
|
763
|
+
|
|
764
|
+
var margin = 8;
|
|
765
|
+
function textW(t) { meas.textContent = t; return meas.offsetWidth; }
|
|
766
|
+
|
|
767
|
+
// Anchor to the element; the box is repositioned around this as it grows.
|
|
768
|
+
var anchorL = rect.left, anchorT = rect.top, anchorB = rect.bottom;
|
|
769
|
+
|
|
770
|
+
function sizeAndPosition() {
|
|
771
|
+
var vw = window.innerWidth, vh = window.innerHeight;
|
|
772
|
+
var maxBoxW = Math.min(560, vw - margin * 2);
|
|
773
|
+
var maxTextW = Math.max(120, maxBoxW - 55); // room for icon + gaps + padding
|
|
774
|
+
var placeholderW = textW(input.placeholder);
|
|
775
|
+
var minTextW = Math.min(placeholderW, maxTextW);
|
|
776
|
+
var maxTaH = Math.min(14 * 18, Math.max(18, (vh - margin * 2) - 24)); // cap ~14 lines, never past viewport
|
|
777
|
+
|
|
778
|
+
// Width: grow with content up to the max, then wrapping takes over.
|
|
779
|
+
var single = textW(input.value || input.placeholder) + 3;
|
|
780
|
+
input.style.width = Math.min(Math.max(single, minTextW), maxTextW) + 'px';
|
|
781
|
+
// Height: fit wrapped content, scroll only in the extreme case.
|
|
782
|
+
input.style.height = 'auto';
|
|
783
|
+
var h = input.scrollHeight;
|
|
784
|
+
if (h > maxTaH) { input.style.height = maxTaH + 'px'; input.style.overflowY = 'auto'; }
|
|
785
|
+
else { input.style.height = h + 'px'; input.style.overflowY = 'hidden'; }
|
|
786
|
+
|
|
787
|
+
// Keep the whole box on screen.
|
|
788
|
+
var bw = box.offsetWidth, bh = box.offsetHeight;
|
|
789
|
+
var left = Math.min(Math.max(anchorL, margin), Math.max(margin, vw - bw - margin));
|
|
790
|
+
var top = anchorT - bh - 8; // prefer sitting above the element
|
|
791
|
+
if (top < margin) top = anchorB + 8; // otherwise below it
|
|
792
|
+
if (top + bh > vh - margin) top = Math.max(margin, vh - bh - margin);
|
|
793
|
+
box.style.left = left + 'px';
|
|
794
|
+
box.style.top = top + 'px';
|
|
795
|
+
}
|
|
796
|
+
sizeAndPosition();
|
|
797
|
+
|
|
798
|
+
input.addEventListener('input', sizeAndPosition);
|
|
799
|
+
input.addEventListener('keydown', function (ev) {
|
|
800
|
+
ev.stopPropagation();
|
|
801
|
+
if (ev.key === 'Enter' && !ev.shiftKey) { ev.preventDefault(); commitNote(input.value); }
|
|
802
|
+
else if (ev.key === 'Escape') { ev.preventDefault(); closeNoteInput(); }
|
|
803
|
+
});
|
|
804
|
+
|
|
805
|
+
rebuildBadges();
|
|
806
|
+
updatePillForSelection();
|
|
807
|
+
setTimeout(function () { input.focus(); }, 0);
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
function commitNote(val) {
|
|
811
|
+
val = (val || '').trim();
|
|
812
|
+
if (noteTargetEl) {
|
|
813
|
+
if (val) noteMap.set(noteTargetEl, val);
|
|
814
|
+
else noteMap.delete(noteTargetEl);
|
|
815
|
+
}
|
|
816
|
+
closeNoteInput();
|
|
817
|
+
rebuildBadges();
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
function closeNoteInput() {
|
|
821
|
+
if (noteInputEl) { noteInputEl.remove(); noteInputEl = null; }
|
|
822
|
+
noteTargetEl = null;
|
|
823
|
+
}
|
|
824
|
+
|
|
600
825
|
// \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
|
|
601
826
|
function setPin(el) {
|
|
602
827
|
pinEl = el;
|
|
@@ -893,6 +1118,7 @@ function getClientScript(options) {
|
|
|
893
1118
|
var target = e.target;
|
|
894
1119
|
if (!target || target === pillWrap || pillWrap.contains(target)) return;
|
|
895
1120
|
if (target === tooltip || tooltip.contains(target)) return;
|
|
1121
|
+
if (noteInputEl && (target === noteInputEl || noteInputEl.contains(target))) return;
|
|
896
1122
|
|
|
897
1123
|
lastHovered = target;
|
|
898
1124
|
|
|
@@ -927,6 +1153,9 @@ function getClientScript(options) {
|
|
|
927
1153
|
|
|
928
1154
|
if (!fiActive) return;
|
|
929
1155
|
|
|
1156
|
+
// Note editor open: let the field handle its own keys (Enter/Esc) and native copy/paste.
|
|
1157
|
+
if (noteInputEl) return;
|
|
1158
|
+
|
|
930
1159
|
if (e.key === 'Alt' && !e.ctrlKey && !e.metaKey && !e.shiftKey) {
|
|
931
1160
|
optionHeld = true;
|
|
932
1161
|
return;
|
|
@@ -957,6 +1186,12 @@ function getClientScript(options) {
|
|
|
957
1186
|
return;
|
|
958
1187
|
}
|
|
959
1188
|
|
|
1189
|
+
if (e.key === 'n' || e.key === 'N') {
|
|
1190
|
+
if (!lastHovered) return;
|
|
1191
|
+
openNoteInput(lastHovered);
|
|
1192
|
+
return;
|
|
1193
|
+
}
|
|
1194
|
+
|
|
960
1195
|
if (e.key === 'm' || e.key === 'M') {
|
|
961
1196
|
if (!measureMode || !lastHovered) return;
|
|
962
1197
|
if (pinEl) {
|