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.js
CHANGED
|
@@ -3,7 +3,13 @@ function getClientScript(options) {
|
|
|
3
3
|
const activateShortcut = options.shortcuts?.activate ?? "ctrl+alt+z";
|
|
4
4
|
return `(function() {
|
|
5
5
|
'use strict';
|
|
6
|
-
|
|
6
|
+
// Guard against running twice on one page. Use a DOM marker (not just a
|
|
7
|
+
// window global) so the browser-extension build (isolated world) and the
|
|
8
|
+
// Vite-plugin build (page main world) can see each other and only one runs \u2014
|
|
9
|
+
// they share the DOM but not window globals, so a per-world flag misses.
|
|
10
|
+
if (window.__specter || document.documentElement.hasAttribute('data-specter')) return;
|
|
11
|
+
window.__specter = true;
|
|
12
|
+
document.documentElement.setAttribute('data-specter', '');
|
|
7
13
|
|
|
8
14
|
// \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
|
|
9
15
|
var PURPLE = '#AD24D3';
|
|
@@ -13,6 +19,7 @@ function getClientScript(options) {
|
|
|
13
19
|
var LABEL = '#8B8D94';
|
|
14
20
|
var MONO = "'JetBrains Mono', 'SF Mono', 'Fira Code', monospace";
|
|
15
21
|
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>';
|
|
22
|
+
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>';
|
|
16
23
|
var ACTIVATE = ${JSON.stringify(activateShortcut)};
|
|
17
24
|
|
|
18
25
|
// \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
|
|
@@ -32,6 +39,9 @@ function getClientScript(options) {
|
|
|
32
39
|
var lastMouse = { x: 0, y: 0 };
|
|
33
40
|
var selectedEls = [];
|
|
34
41
|
var selectedBadges = [];
|
|
42
|
+
var noteMap = new Map();
|
|
43
|
+
var noteInputEl = null;
|
|
44
|
+
var noteTargetEl = null;
|
|
35
45
|
|
|
36
46
|
// \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
|
|
37
47
|
var tooltip = document.createElement('div');
|
|
@@ -191,11 +201,11 @@ function getClientScript(options) {
|
|
|
191
201
|
pillExpanded = true;
|
|
192
202
|
if (text) { pillText.textContent = text; return; }
|
|
193
203
|
if (selectedEls.length > 0) {
|
|
194
|
-
pillText.textContent = selectedEls.length + ' selected \xB7 Cmd+C copy all \xB7 Esc clear';
|
|
204
|
+
pillText.textContent = selectedEls.length + ' selected \xB7 N annotate \xB7 Cmd+C copy all \xB7 Esc clear';
|
|
195
205
|
} else if (measureMode) {
|
|
196
206
|
pillText.textContent = 'Measure \xB7 hover distances \xB7 M pin \xB7 Cmd+C copy \xB7 Option toggle';
|
|
197
207
|
} else {
|
|
198
|
-
pillText.textContent = 'Properties \xB7 hover
|
|
208
|
+
pillText.textContent = 'Properties \xB7 hover \xB7 P pick \xB7 N annotate \xB7 Cmd+C copy \xB7 Option measure';
|
|
199
209
|
}
|
|
200
210
|
}
|
|
201
211
|
|
|
@@ -315,6 +325,55 @@ function getClientScript(options) {
|
|
|
315
325
|
return null;
|
|
316
326
|
}
|
|
317
327
|
|
|
328
|
+
// Collapse a rule's declarations to their shortest lossless form: drop
|
|
329
|
+
// 'initial' (= default, no signal) and always-'normal' noise, and fold
|
|
330
|
+
// side/corner longhands back into shorthands (margin/padding/radius/gap/transition).
|
|
331
|
+
function cleanDecls(style) {
|
|
332
|
+
var m = {}, order = [];
|
|
333
|
+
for (var i = 0; i < style.length; i++) {
|
|
334
|
+
var p = style[i];
|
|
335
|
+
var v = style.getPropertyValue(p);
|
|
336
|
+
if (!v) continue;
|
|
337
|
+
if (v === 'initial') continue;
|
|
338
|
+
if (p === 'transition-behavior') continue;
|
|
339
|
+
if (!(p in m)) order.push(p);
|
|
340
|
+
m[p] = v;
|
|
341
|
+
}
|
|
342
|
+
var used = {}, collapses = {};
|
|
343
|
+
function four(name, t, r, b, l) {
|
|
344
|
+
if (m[t] === undefined || m[r] === undefined || m[b] === undefined || m[l] === undefined) return;
|
|
345
|
+
used[t] = used[r] = used[b] = used[l] = 1;
|
|
346
|
+
var a = m[t], c = m[r], d = m[b], e = m[l], sh;
|
|
347
|
+
if (a === c && c === d && d === e) sh = a;
|
|
348
|
+
else if (a === d && c === e) sh = a + ' ' + c;
|
|
349
|
+
else if (c === e) sh = a + ' ' + c + ' ' + d;
|
|
350
|
+
else sh = a + ' ' + c + ' ' + d + ' ' + e;
|
|
351
|
+
collapses[t] = name + ': ' + sh;
|
|
352
|
+
}
|
|
353
|
+
four('margin', 'margin-top', 'margin-right', 'margin-bottom', 'margin-left');
|
|
354
|
+
four('padding', 'padding-top', 'padding-right', 'padding-bottom', 'padding-left');
|
|
355
|
+
four('border-radius', 'border-top-left-radius', 'border-top-right-radius', 'border-bottom-right-radius', 'border-bottom-left-radius');
|
|
356
|
+
if (m['row-gap'] !== undefined && m['column-gap'] !== undefined) {
|
|
357
|
+
var g = m['row-gap'] === m['column-gap'] ? m['row-gap'] : m['row-gap'] + ' ' + m['column-gap'];
|
|
358
|
+
collapses['row-gap'] = 'gap: ' + g; used['row-gap'] = used['column-gap'] = 1;
|
|
359
|
+
}
|
|
360
|
+
if (m['transition-property'] !== undefined) {
|
|
361
|
+
var tr = 'transition: ' + m['transition-property'];
|
|
362
|
+
if (m['transition-duration'] !== undefined) tr += ' ' + m['transition-duration'];
|
|
363
|
+
if (m['transition-timing-function'] !== undefined) tr += ' ' + m['transition-timing-function'];
|
|
364
|
+
if (m['transition-delay'] !== undefined && m['transition-delay'] !== '0s') tr += ' ' + m['transition-delay'];
|
|
365
|
+
collapses['transition-property'] = tr;
|
|
366
|
+
used['transition-property'] = used['transition-duration'] = used['transition-timing-function'] = used['transition-delay'] = 1;
|
|
367
|
+
}
|
|
368
|
+
var out = [];
|
|
369
|
+
order.forEach(function (p) {
|
|
370
|
+
if (collapses[p]) { out.push(' ' + collapses[p] + ';'); return; }
|
|
371
|
+
if (used[p]) return;
|
|
372
|
+
out.push(' ' + p + ': ' + m[p] + ';');
|
|
373
|
+
});
|
|
374
|
+
return out;
|
|
375
|
+
}
|
|
376
|
+
|
|
318
377
|
function getMatchingRules(el) {
|
|
319
378
|
var results = [];
|
|
320
379
|
try {
|
|
@@ -331,12 +390,7 @@ function getClientScript(options) {
|
|
|
331
390
|
if (/^[*,]|^:root|^html|^body$|^::before|^::after/.test(sel.trim())) continue;
|
|
332
391
|
try {
|
|
333
392
|
if (el.matches(sel)) {
|
|
334
|
-
var props =
|
|
335
|
-
for (var i = 0; i < rule.style.length; i++) {
|
|
336
|
-
var prop = rule.style[i];
|
|
337
|
-
var val = rule.style.getPropertyValue(prop);
|
|
338
|
-
if (val) props.push(' ' + prop + ': ' + val + ';');
|
|
339
|
-
}
|
|
393
|
+
var props = cleanDecls(rule.style);
|
|
340
394
|
if (props.length) results.push(sel + ' {\\n' + props.join('\\n') + '\\n}');
|
|
341
395
|
}
|
|
342
396
|
} catch (e) {}
|
|
@@ -456,9 +510,13 @@ function getClientScript(options) {
|
|
|
456
510
|
}
|
|
457
511
|
|
|
458
512
|
function buildMultiSelectCopyText() {
|
|
513
|
+
var n = selectedEls.length;
|
|
459
514
|
return selectedEls.map(function (el, i) {
|
|
460
515
|
var body = buildLLMClipboard(buildInfo(el));
|
|
461
|
-
|
|
516
|
+
var note = noteMap.get(el);
|
|
517
|
+
var header = n > 1 ? '[Specter ' + (i + 1) + '/' + n + ']' : '[Specter]';
|
|
518
|
+
if (note) header += '\\n\u270F\uFE0F CHANGE: ' + note;
|
|
519
|
+
return body.replace('[Specter]', header);
|
|
462
520
|
}).join('\\n\\n' + Array(41).join('\u2500') + '\\n\\n');
|
|
463
521
|
}
|
|
464
522
|
|
|
@@ -509,16 +567,25 @@ function getClientScript(options) {
|
|
|
509
567
|
}
|
|
510
568
|
|
|
511
569
|
// \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
|
|
512
|
-
function makeBadge(el, index) {
|
|
570
|
+
function makeBadge(el, index, note) {
|
|
513
571
|
var rect = el.getBoundingClientRect();
|
|
514
|
-
var
|
|
515
|
-
|
|
516
|
-
Object.assign(badge.style, {
|
|
572
|
+
var wrap = document.createElement('div');
|
|
573
|
+
Object.assign(wrap.style, {
|
|
517
574
|
position: 'fixed',
|
|
518
575
|
left: (rect.left - 4) + 'px',
|
|
519
576
|
top: (rect.top - 4) + 'px',
|
|
577
|
+
zIndex: '2147483644',
|
|
578
|
+
pointerEvents: 'none',
|
|
579
|
+
display: 'flex',
|
|
580
|
+
alignItems: 'flex-start',
|
|
581
|
+
gap: '4px',
|
|
582
|
+
});
|
|
583
|
+
var badge = document.createElement('div');
|
|
584
|
+
badge.textContent = String(index + 1);
|
|
585
|
+
Object.assign(badge.style, {
|
|
520
586
|
width: '20px',
|
|
521
587
|
height: '20px',
|
|
588
|
+
flexShrink: '0',
|
|
522
589
|
background: PURPLE,
|
|
523
590
|
color: '#fff',
|
|
524
591
|
fontSize: '11px',
|
|
@@ -528,19 +595,38 @@ function getClientScript(options) {
|
|
|
528
595
|
display: 'flex',
|
|
529
596
|
alignItems: 'center',
|
|
530
597
|
justifyContent: 'center',
|
|
531
|
-
zIndex: '2147483644',
|
|
532
|
-
pointerEvents: 'none',
|
|
533
598
|
boxShadow: '0 2px 6px rgba(0,0,0,0.3)',
|
|
534
599
|
});
|
|
535
|
-
|
|
536
|
-
|
|
600
|
+
wrap.appendChild(badge);
|
|
601
|
+
if (note) {
|
|
602
|
+
var chip = document.createElement('div');
|
|
603
|
+
chip.textContent = '\u270F\uFE0F ' + (note.length > 32 ? note.slice(0, 32) + '\u2026' : note);
|
|
604
|
+
Object.assign(chip.style, {
|
|
605
|
+
maxWidth: '240px',
|
|
606
|
+
background: TIP_BG,
|
|
607
|
+
color: '#fff',
|
|
608
|
+
fontSize: '10px',
|
|
609
|
+
lineHeight: '16px',
|
|
610
|
+
fontFamily: MONO,
|
|
611
|
+
padding: '2px 6px',
|
|
612
|
+
borderRadius: '4px',
|
|
613
|
+
whiteSpace: 'nowrap',
|
|
614
|
+
overflow: 'hidden',
|
|
615
|
+
textOverflow: 'ellipsis',
|
|
616
|
+
border: '1px solid ' + PURPLE,
|
|
617
|
+
boxShadow: '0 2px 6px rgba(0,0,0,0.3)',
|
|
618
|
+
});
|
|
619
|
+
wrap.appendChild(chip);
|
|
620
|
+
}
|
|
621
|
+
document.body.appendChild(wrap);
|
|
622
|
+
return wrap;
|
|
537
623
|
}
|
|
538
624
|
|
|
539
625
|
function rebuildBadges() {
|
|
540
626
|
selectedBadges.forEach(function (b) { b.remove(); });
|
|
541
627
|
selectedBadges.length = 0;
|
|
542
628
|
selectedEls.forEach(function (el, i) {
|
|
543
|
-
selectedBadges.push(makeBadge(el, i));
|
|
629
|
+
selectedBadges.push(makeBadge(el, i, noteMap.get(el)));
|
|
544
630
|
});
|
|
545
631
|
}
|
|
546
632
|
|
|
@@ -549,6 +635,7 @@ function getClientScript(options) {
|
|
|
549
635
|
if (idx >= 0) {
|
|
550
636
|
selectedEls.splice(idx, 1);
|
|
551
637
|
el.style.outline = '';
|
|
638
|
+
noteMap.delete(el);
|
|
552
639
|
} else {
|
|
553
640
|
selectedEls.push(el);
|
|
554
641
|
el.style.outline = '2px solid ' + PURPLE;
|
|
@@ -559,10 +646,12 @@ function getClientScript(options) {
|
|
|
559
646
|
}
|
|
560
647
|
|
|
561
648
|
function clearSelection() {
|
|
649
|
+
closeNoteInput();
|
|
562
650
|
selectedEls.forEach(function (el) { el.style.outline = ''; el.style.outlineOffset = ''; });
|
|
563
651
|
selectedEls.length = 0;
|
|
564
652
|
selectedBadges.forEach(function (b) { b.remove(); });
|
|
565
653
|
selectedBadges.length = 0;
|
|
654
|
+
noteMap.clear();
|
|
566
655
|
}
|
|
567
656
|
|
|
568
657
|
function updatePillForSelection() {
|
|
@@ -570,6 +659,142 @@ function getClientScript(options) {
|
|
|
570
659
|
else if (!pillWrap.matches(':hover')) collapsePill();
|
|
571
660
|
}
|
|
572
661
|
|
|
662
|
+
// \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
|
|
663
|
+
function openNoteInput(el) {
|
|
664
|
+
closeNoteInput();
|
|
665
|
+
if (selectedEls.indexOf(el) < 0) {
|
|
666
|
+
clearHoverOutline();
|
|
667
|
+
hideTooltip();
|
|
668
|
+
selectedEls.push(el);
|
|
669
|
+
el.style.outline = '2px solid ' + PURPLE;
|
|
670
|
+
el.style.outlineOffset = '-1px';
|
|
671
|
+
}
|
|
672
|
+
noteTargetEl = el;
|
|
673
|
+
var rect = el.getBoundingClientRect();
|
|
674
|
+
|
|
675
|
+
var box = document.createElement('div');
|
|
676
|
+
Object.assign(box.style, {
|
|
677
|
+
position: 'fixed',
|
|
678
|
+
zIndex: '2147483647',
|
|
679
|
+
display: 'inline-flex',
|
|
680
|
+
alignItems: 'flex-start',
|
|
681
|
+
gap: '8px',
|
|
682
|
+
boxSizing: 'border-box',
|
|
683
|
+
background: TIP_BG,
|
|
684
|
+
border: '1px solid ' + PURPLE,
|
|
685
|
+
borderRadius: '8px',
|
|
686
|
+
padding: '11px 12px',
|
|
687
|
+
boxShadow: '0 4px 16px rgba(0,0,0,0.35)',
|
|
688
|
+
fontFamily: MONO,
|
|
689
|
+
});
|
|
690
|
+
var pen = document.createElement('span');
|
|
691
|
+
pen.innerHTML = PENCIL;
|
|
692
|
+
Object.assign(pen.style, {
|
|
693
|
+
display: 'flex',
|
|
694
|
+
alignItems: 'center',
|
|
695
|
+
flexShrink: '0',
|
|
696
|
+
marginTop: '2px',
|
|
697
|
+
color: '#E0A3F5',
|
|
698
|
+
});
|
|
699
|
+
var input = document.createElement('textarea');
|
|
700
|
+
input.rows = 1;
|
|
701
|
+
input.placeholder = 'Describe the change\u2026 (Enter to save)';
|
|
702
|
+
input.value = noteMap.get(el) || '';
|
|
703
|
+
Object.assign(input.style, {
|
|
704
|
+
flexShrink: '0',
|
|
705
|
+
background: 'transparent',
|
|
706
|
+
border: 'none',
|
|
707
|
+
outline: 'none',
|
|
708
|
+
resize: 'none',
|
|
709
|
+
overflow: 'hidden',
|
|
710
|
+
color: '#fff',
|
|
711
|
+
fontFamily: MONO,
|
|
712
|
+
fontSize: '12px',
|
|
713
|
+
lineHeight: '18px',
|
|
714
|
+
padding: '0',
|
|
715
|
+
margin: '0',
|
|
716
|
+
whiteSpace: 'pre-wrap',
|
|
717
|
+
overflowWrap: 'anywhere',
|
|
718
|
+
});
|
|
719
|
+
// Hidden mirror to measure single-line text width so the field grows as you type.
|
|
720
|
+
var meas = document.createElement('span');
|
|
721
|
+
Object.assign(meas.style, {
|
|
722
|
+
position: 'absolute',
|
|
723
|
+
visibility: 'hidden',
|
|
724
|
+
whiteSpace: 'pre',
|
|
725
|
+
pointerEvents: 'none',
|
|
726
|
+
fontFamily: MONO,
|
|
727
|
+
fontSize: '12px',
|
|
728
|
+
left: '-9999px',
|
|
729
|
+
top: '0',
|
|
730
|
+
});
|
|
731
|
+
box.appendChild(pen);
|
|
732
|
+
box.appendChild(input);
|
|
733
|
+
box.appendChild(meas);
|
|
734
|
+
document.body.appendChild(box);
|
|
735
|
+
noteInputEl = box;
|
|
736
|
+
|
|
737
|
+
var margin = 8;
|
|
738
|
+
function textW(t) { meas.textContent = t; return meas.offsetWidth; }
|
|
739
|
+
|
|
740
|
+
// Anchor to the element; the box is repositioned around this as it grows.
|
|
741
|
+
var anchorL = rect.left, anchorT = rect.top, anchorB = rect.bottom;
|
|
742
|
+
|
|
743
|
+
function sizeAndPosition() {
|
|
744
|
+
var vw = window.innerWidth, vh = window.innerHeight;
|
|
745
|
+
var maxBoxW = Math.min(560, vw - margin * 2);
|
|
746
|
+
var maxTextW = Math.max(120, maxBoxW - 55); // room for icon + gaps + padding
|
|
747
|
+
var placeholderW = textW(input.placeholder);
|
|
748
|
+
var minTextW = Math.min(placeholderW, maxTextW);
|
|
749
|
+
var maxTaH = Math.min(14 * 18, Math.max(18, (vh - margin * 2) - 24)); // cap ~14 lines, never past viewport
|
|
750
|
+
|
|
751
|
+
// Width: grow with content up to the max, then wrapping takes over.
|
|
752
|
+
var single = textW(input.value || input.placeholder) + 3;
|
|
753
|
+
input.style.width = Math.min(Math.max(single, minTextW), maxTextW) + 'px';
|
|
754
|
+
// Height: fit wrapped content, scroll only in the extreme case.
|
|
755
|
+
input.style.height = 'auto';
|
|
756
|
+
var h = input.scrollHeight;
|
|
757
|
+
if (h > maxTaH) { input.style.height = maxTaH + 'px'; input.style.overflowY = 'auto'; }
|
|
758
|
+
else { input.style.height = h + 'px'; input.style.overflowY = 'hidden'; }
|
|
759
|
+
|
|
760
|
+
// Keep the whole box on screen.
|
|
761
|
+
var bw = box.offsetWidth, bh = box.offsetHeight;
|
|
762
|
+
var left = Math.min(Math.max(anchorL, margin), Math.max(margin, vw - bw - margin));
|
|
763
|
+
var top = anchorT - bh - 8; // prefer sitting above the element
|
|
764
|
+
if (top < margin) top = anchorB + 8; // otherwise below it
|
|
765
|
+
if (top + bh > vh - margin) top = Math.max(margin, vh - bh - margin);
|
|
766
|
+
box.style.left = left + 'px';
|
|
767
|
+
box.style.top = top + 'px';
|
|
768
|
+
}
|
|
769
|
+
sizeAndPosition();
|
|
770
|
+
|
|
771
|
+
input.addEventListener('input', sizeAndPosition);
|
|
772
|
+
input.addEventListener('keydown', function (ev) {
|
|
773
|
+
ev.stopPropagation();
|
|
774
|
+
if (ev.key === 'Enter' && !ev.shiftKey) { ev.preventDefault(); commitNote(input.value); }
|
|
775
|
+
else if (ev.key === 'Escape') { ev.preventDefault(); closeNoteInput(); }
|
|
776
|
+
});
|
|
777
|
+
|
|
778
|
+
rebuildBadges();
|
|
779
|
+
updatePillForSelection();
|
|
780
|
+
setTimeout(function () { input.focus(); }, 0);
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
function commitNote(val) {
|
|
784
|
+
val = (val || '').trim();
|
|
785
|
+
if (noteTargetEl) {
|
|
786
|
+
if (val) noteMap.set(noteTargetEl, val);
|
|
787
|
+
else noteMap.delete(noteTargetEl);
|
|
788
|
+
}
|
|
789
|
+
closeNoteInput();
|
|
790
|
+
rebuildBadges();
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
function closeNoteInput() {
|
|
794
|
+
if (noteInputEl) { noteInputEl.remove(); noteInputEl = null; }
|
|
795
|
+
noteTargetEl = null;
|
|
796
|
+
}
|
|
797
|
+
|
|
573
798
|
// \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
|
|
574
799
|
function setPin(el) {
|
|
575
800
|
pinEl = el;
|
|
@@ -866,6 +1091,7 @@ function getClientScript(options) {
|
|
|
866
1091
|
var target = e.target;
|
|
867
1092
|
if (!target || target === pillWrap || pillWrap.contains(target)) return;
|
|
868
1093
|
if (target === tooltip || tooltip.contains(target)) return;
|
|
1094
|
+
if (noteInputEl && (target === noteInputEl || noteInputEl.contains(target))) return;
|
|
869
1095
|
|
|
870
1096
|
lastHovered = target;
|
|
871
1097
|
|
|
@@ -900,6 +1126,9 @@ function getClientScript(options) {
|
|
|
900
1126
|
|
|
901
1127
|
if (!fiActive) return;
|
|
902
1128
|
|
|
1129
|
+
// Note editor open: let the field handle its own keys (Enter/Esc) and native copy/paste.
|
|
1130
|
+
if (noteInputEl) return;
|
|
1131
|
+
|
|
903
1132
|
if (e.key === 'Alt' && !e.ctrlKey && !e.metaKey && !e.shiftKey) {
|
|
904
1133
|
optionHeld = true;
|
|
905
1134
|
return;
|
|
@@ -930,6 +1159,12 @@ function getClientScript(options) {
|
|
|
930
1159
|
return;
|
|
931
1160
|
}
|
|
932
1161
|
|
|
1162
|
+
if (e.key === 'n' || e.key === 'N') {
|
|
1163
|
+
if (!lastHovered) return;
|
|
1164
|
+
openNoteInput(lastHovered);
|
|
1165
|
+
return;
|
|
1166
|
+
}
|
|
1167
|
+
|
|
933
1168
|
if (e.key === 'm' || e.key === 'M') {
|
|
934
1169
|
if (!measureMode || !lastHovered) return;
|
|
935
1170
|
if (pinEl) {
|