checkpoint-cli 0.1.9 → 0.2.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.
Files changed (2) hide show
  1. package/dist/index.js +415 -249
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -81,12 +81,15 @@ function requireAuth() {
81
81
  /* ── Checkpoint Tracking Script (injected into HTML responses) ── */
82
82
  function trackingScript() {
83
83
  return `
84
- <script data-checkpoint>
84
+ <script data-checkpoint-script="1">
85
85
  (function(){
86
86
  var lastPath='';
87
87
  var pickMode=false;
88
88
  var pickClickHandler=null;
89
89
  var pickEscHandler=null;
90
+ var pickMoveHandler=null;
91
+ var pickInspectBox=null;
92
+ var pickInspectLabel=null;
90
93
 
91
94
  function getMetrics(){
92
95
  var de=document.documentElement;
@@ -120,6 +123,133 @@ function trackingScript() {
120
123
  return String(value).replace(/\\s+/g,' ').trim().slice(0,120);
121
124
  }
122
125
 
126
+ function getFileLeaf(path){
127
+ if(!path) return '';
128
+ try{
129
+ var raw=String(path);
130
+ var parts=raw.split(/[\\\\/]/);
131
+ return parts[parts.length-1]||raw;
132
+ }catch(e){}
133
+ return '';
134
+ }
135
+
136
+ function getInspectElementLabel(el){
137
+ if(!el||!el.tagName) return '(unknown)';
138
+ var tag=String(el.tagName).toLowerCase();
139
+ var id=el.id?('#'+el.id):'';
140
+ var classes='';
141
+ try{
142
+ var cls=(el.className&&typeof el.className==='string')
143
+ ? el.className.trim().split(/\\s+/).filter(Boolean).slice(0,3)
144
+ : [];
145
+ if(cls.length>0){
146
+ classes='.'+cls.join('.');
147
+ }
148
+ }catch(e){}
149
+ return shortText(tag+id+classes);
150
+ }
151
+
152
+ function ensurePickInspectUi(){
153
+ if(!document.body) return;
154
+ if(!pickInspectBox){
155
+ pickInspectBox=document.createElement('div');
156
+ pickInspectBox.setAttribute('data-checkpoint-inspect','box');
157
+ pickInspectBox.style.position='fixed';
158
+ pickInspectBox.style.left='0';
159
+ pickInspectBox.style.top='0';
160
+ pickInspectBox.style.width='0';
161
+ pickInspectBox.style.height='0';
162
+ pickInspectBox.style.border='2px solid #6366f1';
163
+ pickInspectBox.style.background='rgba(99,102,241,0.12)';
164
+ pickInspectBox.style.boxSizing='border-box';
165
+ pickInspectBox.style.borderRadius='4px';
166
+ pickInspectBox.style.pointerEvents='none';
167
+ pickInspectBox.style.zIndex='2147483646';
168
+ pickInspectBox.style.display='none';
169
+ document.body.appendChild(pickInspectBox);
170
+ }
171
+ if(!pickInspectLabel){
172
+ pickInspectLabel=document.createElement('div');
173
+ pickInspectLabel.setAttribute('data-checkpoint-inspect','label');
174
+ pickInspectLabel.style.position='fixed';
175
+ pickInspectLabel.style.left='0';
176
+ pickInspectLabel.style.top='0';
177
+ pickInspectLabel.style.maxWidth='380px';
178
+ pickInspectLabel.style.padding='6px 8px';
179
+ pickInspectLabel.style.borderRadius='8px';
180
+ pickInspectLabel.style.border='1px solid rgba(148,163,184,0.45)';
181
+ pickInspectLabel.style.background='rgba(15,23,42,0.96)';
182
+ pickInspectLabel.style.color='#e2e8f0';
183
+ pickInspectLabel.style.font='12px/1.35 ui-monospace, SFMono-Regular, Menlo, monospace';
184
+ pickInspectLabel.style.whiteSpace='pre-line';
185
+ pickInspectLabel.style.wordBreak='break-word';
186
+ pickInspectLabel.style.pointerEvents='none';
187
+ pickInspectLabel.style.zIndex='2147483647';
188
+ pickInspectLabel.style.display='none';
189
+ pickInspectLabel.style.boxShadow='0 6px 20px rgba(0,0,0,0.35)';
190
+ document.body.appendChild(pickInspectLabel);
191
+ }
192
+ }
193
+
194
+ function hidePickInspectUi(){
195
+ if(pickInspectBox) pickInspectBox.style.display='none';
196
+ if(pickInspectLabel) pickInspectLabel.style.display='none';
197
+ }
198
+
199
+ function renderPickInspect(target,mouseX,mouseY){
200
+ ensurePickInspectUi();
201
+ if(!pickInspectBox||!pickInspectLabel||!target||target.nodeType!==1){
202
+ hidePickInspectUi();
203
+ return;
204
+ }
205
+ var sourceTarget=findBestSourceAnchorTarget(target);
206
+ var anchorEl=sourceTarget.element||target;
207
+ var sourceAnchor=sourceTarget.sourceAnchor||getSourceAnchor(anchorEl);
208
+ var rect=anchorEl.getBoundingClientRect?anchorEl.getBoundingClientRect():null;
209
+ if(!rect||rect.width<1||rect.height<1){
210
+ hidePickInspectUi();
211
+ return;
212
+ }
213
+
214
+ pickInspectBox.style.display='block';
215
+ pickInspectBox.style.left=Math.max(0,rect.left)+'px';
216
+ pickInspectBox.style.top=Math.max(0,rect.top)+'px';
217
+ pickInspectBox.style.width=Math.max(1,rect.width)+'px';
218
+ pickInspectBox.style.height=Math.max(1,rect.height)+'px';
219
+
220
+ var primary=getInspectElementLabel(anchorEl);
221
+ var sourceLabel='(no source metadata)';
222
+ if(sourceAnchor){
223
+ var leaf=getFileLeaf(sourceAnchor.source_file);
224
+ if(leaf&&typeof sourceAnchor.source_line==='number'){
225
+ sourceLabel=leaf+':'+sourceAnchor.source_line;
226
+ }else if(leaf){
227
+ sourceLabel=leaf;
228
+ }else if(sourceAnchor.component_name){
229
+ sourceLabel=sourceAnchor.component_name;
230
+ }
231
+ if(sourceAnchor.explicit_id){
232
+ sourceLabel=sourceLabel+' · #'+sourceAnchor.explicit_id;
233
+ }
234
+ }
235
+ pickInspectLabel.textContent=primary+'\\n'+sourceLabel;
236
+ pickInspectLabel.style.display='block';
237
+
238
+ var viewW=window.innerWidth||document.documentElement.clientWidth||0;
239
+ var viewH=window.innerHeight||document.documentElement.clientHeight||0;
240
+ var labelRect=pickInspectLabel.getBoundingClientRect();
241
+ var left=mouseX+14;
242
+ var top=mouseY+14;
243
+ if(left+labelRect.width>viewW-8){
244
+ left=Math.max(8,mouseX-labelRect.width-14);
245
+ }
246
+ if(top+labelRect.height>viewH-8){
247
+ top=Math.max(8,mouseY-labelRect.height-14);
248
+ }
249
+ pickInspectLabel.style.left=left+'px';
250
+ pickInspectLabel.style.top=top+'px';
251
+ }
252
+
123
253
  function elementSelector(el){
124
254
  if(!el||!el.tagName) return '';
125
255
  var tag=el.tagName.toLowerCase();
@@ -339,132 +469,19 @@ function trackingScript() {
339
469
  return '';
340
470
  }
341
471
 
342
- function getNavSignature(){
343
- try{
344
- var candidates=[];
345
- var activeSelectors=[
346
- '[aria-current=\"page\"]',
347
- '[aria-selected=\"true\"]',
348
- '[data-state=\"active\"]',
349
- '.active',
350
- '.is-active',
351
- '.selected'
352
- ];
353
- for(var i=0;i<activeSelectors.length;i++){
354
- var nodes=document.querySelectorAll(activeSelectors[i]);
355
- for(var j=0;j<nodes.length&&candidates.length<8;j++){
356
- var text=getVisibleText(nodes[j]);
357
- if(text) candidates.push(text);
358
- }
359
- }
360
- if(candidates.length===0){
361
- var heading=document.querySelector('main h1, [role=\"main\"] h1, h1, h2');
362
- var fallbackText=getVisibleText(heading);
363
- if(fallbackText) candidates.push(fallbackText);
472
+ function getSourceDebugInfo(fiber){
473
+ var current=fiber;
474
+ var steps=0;
475
+ while(current&&steps<24){
476
+ if(current._debugSource){
477
+ return current._debugSource;
364
478
  }
365
- if(candidates.length===0) return '';
366
- return candidates.slice(0,4).join('|');
367
- }catch(e){}
368
- return '';
369
- }
370
-
371
- function getHeadingSignature(){
372
- try{
373
- var heading=document.querySelector('main h1, [role=\"main\"] h1, h1, h2');
374
- return getVisibleText(heading);
375
- }catch(e){}
376
- return '';
377
- }
378
-
379
- function getViewContext(){
380
- return {
381
- nav_signature:getNavSignature()||undefined,
382
- heading_signature:getHeadingSignature()||undefined
383
- };
384
- }
385
-
386
- function viewContextMatches(expected){
387
- if(!expected||typeof expected!=='object') return true;
388
- var current=getViewContext();
389
- if(expected.nav_signature&&current.nav_signature&&expected.nav_signature!==current.nav_signature){
390
- return false;
391
- }
392
- if(expected.heading_signature&&current.heading_signature&&expected.heading_signature!==current.heading_signature){
393
- return false;
394
- }
395
- return true;
396
- }
397
-
398
- function isScrollable(el){
399
- if(!el||el.nodeType!==1||!window.getComputedStyle) return false;
400
- try{
401
- var style=window.getComputedStyle(el);
402
- var overflowY=String(style.overflowY||'').toLowerCase();
403
- var overflowX=String(style.overflowX||'').toLowerCase();
404
- var canScrollY=(overflowY==='auto'||overflowY==='scroll'||overflowY==='overlay')&&(el.scrollHeight-el.clientHeight>1);
405
- var canScrollX=(overflowX==='auto'||overflowX==='scroll'||overflowX==='overlay')&&(el.scrollWidth-el.clientWidth>1);
406
- return !!(canScrollX||canScrollY);
407
- }catch(e){}
408
- return false;
409
- }
410
-
411
- function getNearestScrollContainer(el){
412
- if(!el) return null;
413
- var current=el.parentElement;
414
- var depth=0;
415
- while(current&&depth<10){
416
- if(current===document.body||current===document.documentElement) return null;
417
- if(isScrollable(current)) return current;
418
- current=current.parentElement;
419
- depth++;
479
+ current=current.return||null;
480
+ steps++;
420
481
  }
421
482
  return null;
422
483
  }
423
484
 
424
- function getScrollContext(el,clientX,clientY){
425
- var container=getNearestScrollContainer(el);
426
- if(!container) return null;
427
- var rect=container.getBoundingClientRect?container.getBoundingClientRect():null;
428
- if(!rect) return null;
429
- var safeW=Math.max(1,rect.width||1);
430
- var safeH=Math.max(1,rect.height||1);
431
- return {
432
- container_selector_chain:getSelectorChain(container),
433
- container_fingerprint:getDomFingerprint(container),
434
- container_x_percent:clampPercent(((clientX-rect.left)/safeW)*100),
435
- container_y_percent:clampPercent(((clientY-rect.top)/safeH)*100)
436
- };
437
- }
438
-
439
- function getPointFromScrollContext(scrollContext){
440
- if(!scrollContext||typeof scrollContext!=='object') return null;
441
- var container=null;
442
- if(Array.isArray(scrollContext.container_selector_chain)){
443
- for(var i=0;i<scrollContext.container_selector_chain.length;i++){
444
- var candidate=scrollContext.container_selector_chain[i];
445
- if(!candidate||typeof candidate.selector!=='string') continue;
446
- var nodes=querySelectorAllSafe(document,candidate.selector);
447
- var match=pickBestElement(nodes,{coord_fallback:null},getMetrics(),null);
448
- if(match&&match.element){
449
- container=match.element;
450
- break;
451
- }
452
- }
453
- }
454
- if(!container&&scrollContext.container_fingerprint){
455
- var fp=resolveFromFingerprint(scrollContext.container_fingerprint,{coord_fallback:null},getMetrics(),document);
456
- container=fp&&fp.element?fp.element:null;
457
- }
458
- if(!container||!container.getBoundingClientRect) return null;
459
- var rect=container.getBoundingClientRect();
460
- var relX=typeof scrollContext.container_x_percent==='number'?clampPercent(scrollContext.container_x_percent)/100:0.5;
461
- var relY=typeof scrollContext.container_y_percent==='number'?clampPercent(scrollContext.container_y_percent)/100:0.5;
462
- return {
463
- clientX:rect.left+(rect.width*relX),
464
- clientY:rect.top+(rect.height*relY)
465
- };
466
- }
467
-
468
485
  function buildCoordFallback(clientX,clientY,metrics){
469
486
  var safeViewW=metrics.viewWidth||1;
470
487
  var safeViewH=metrics.viewHeight||1;
@@ -485,6 +502,9 @@ function trackingScript() {
485
502
  }
486
503
 
487
504
  function buildAnchorPayload(el,clientX,clientY){
505
+ var sourceTarget=findBestSourceAnchorTarget(el);
506
+ var anchorEl=sourceTarget.element||el;
507
+ var sourceAnchor=sourceTarget.sourceAnchor||getSourceAnchor(anchorEl)||null;
488
508
  var metrics=getMetrics();
489
509
  var textSelf='';
490
510
  var textParent='';
@@ -494,7 +514,7 @@ function trackingScript() {
494
514
  }catch(e){}
495
515
  var fallback=buildCoordFallback(clientX,clientY,metrics);
496
516
  try{
497
- var rect=el&&el.getBoundingClientRect?el.getBoundingClientRect():null;
517
+ var rect=anchorEl&&anchorEl.getBoundingClientRect?anchorEl.getBoundingClientRect():null;
498
518
  if(rect){
499
519
  var safeW=Math.max(1,rect.width||1);
500
520
  var safeH=Math.max(1,rect.height||1);
@@ -503,16 +523,15 @@ function trackingScript() {
503
523
  }
504
524
  }catch(e){}
505
525
  return {
506
- selector_chain:getSelectorChain(el),
507
- dom_fingerprint:getDomFingerprint(el),
526
+ selector_chain:[],
527
+ dom_fingerprint:null,
508
528
  text_context:{
509
529
  self:textSelf,
510
530
  parent:textParent
511
531
  },
512
- container_hint:getContainerHint(el),
532
+ container_hint:null,
513
533
  transient_context:getTransientContext(el),
514
- view_context:getViewContext(),
515
- scroll_context:getScrollContext(el,clientX,clientY),
534
+ source_anchor:sourceAnchor,
516
535
  coord_fallback:fallback
517
536
  };
518
537
  }
@@ -587,6 +606,176 @@ function trackingScript() {
587
606
  return dist;
588
607
  }
589
608
 
609
+ function getReactFiberNode(el){
610
+ if(!el||typeof el!=='object') return null;
611
+ try{
612
+ var keys=Object.keys(el);
613
+ for(var i=0;i<keys.length;i++){
614
+ var key=keys[i];
615
+ if(key.indexOf('__reactFiber$')===0||key.indexOf('__reactInternalInstance$')===0){
616
+ return el[key];
617
+ }
618
+ }
619
+ }catch(e){}
620
+ return null;
621
+ }
622
+
623
+ function getReactFiberName(fiber){
624
+ try{
625
+ if(!fiber) return '';
626
+ var t=fiber.type||fiber.elementType;
627
+ if(typeof t==='string') return t;
628
+ if(t&&typeof t.displayName==='string'&&t.displayName) return t.displayName;
629
+ if(t&&typeof t.name==='string'&&t.name) return t.name;
630
+ }catch(e){}
631
+ return '';
632
+ }
633
+
634
+ function getReactOwnerPath(el){
635
+ var fiber=getReactFiberNode(el);
636
+ if(!fiber) return [];
637
+ var path=[];
638
+ var current=fiber;
639
+ var steps=0;
640
+ while(current&&steps<24){
641
+ var name=getReactFiberName(current);
642
+ if(name){
643
+ var keyVal=current.key!=null?String(current.key):'';
644
+ path.push(keyVal?name+'#'+keyVal:name);
645
+ }
646
+ current=current.return||null;
647
+ steps++;
648
+ }
649
+ path.reverse();
650
+ if(path.length>12){
651
+ path=path.slice(path.length-12);
652
+ }
653
+ return path;
654
+ }
655
+
656
+ function getExplicitAnchorId(el){
657
+ if(!el||typeof el.closest!=='function') return '';
658
+ var holder=el.closest('[data-checkpoint-anchor]');
659
+ if(!holder||!holder.getAttribute) return '';
660
+ return holder.getAttribute('data-checkpoint-anchor')||'';
661
+ }
662
+
663
+ function getSourceAnchor(el){
664
+ var fiber=getReactFiberNode(el);
665
+ var ownerPath=getReactOwnerPath(el);
666
+ var source=getSourceDebugInfo(fiber);
667
+ var component=getReactFiberName(fiber);
668
+ var explicitId=getExplicitAnchorId(el);
669
+ var reactKey=fiber&&fiber.key!=null?String(fiber.key):'';
670
+ var hasData=!!(explicitId||component||ownerPath.length>0||source||reactKey);
671
+ if(!hasData) return null;
672
+ return {
673
+ explicit_id:explicitId||undefined,
674
+ component_name:component||undefined,
675
+ owner_path:ownerPath,
676
+ source_file:source&&source.fileName?String(source.fileName):undefined,
677
+ source_line:source&&typeof source.lineNumber==='number'?source.lineNumber:undefined,
678
+ source_column:source&&typeof source.columnNumber==='number'?source.columnNumber:undefined,
679
+ react_key:reactKey||undefined,
680
+ host_tag:el&&el.tagName?String(el.tagName).toLowerCase():undefined
681
+ };
682
+ }
683
+
684
+ function getSourceAnchorStrength(anchor){
685
+ if(!anchor||typeof anchor!=='object') return 0;
686
+ var score=0;
687
+ if(anchor.explicit_id) score+=2000;
688
+ if(anchor.react_key) score+=400;
689
+ if(anchor.source_file) score+=420;
690
+ if(typeof anchor.source_line==='number') score+=260;
691
+ if(anchor.component_name) score+=160;
692
+ if(Array.isArray(anchor.owner_path)&&anchor.owner_path.length>0){
693
+ score+=Math.min(220,anchor.owner_path.length*28);
694
+ }
695
+ return score;
696
+ }
697
+
698
+ function findBestSourceAnchorTarget(el){
699
+ if(!el||el.nodeType!==1) return { element:el, sourceAnchor:null };
700
+ var current=el;
701
+ var bestEl=el;
702
+ var bestAnchor=null;
703
+ var bestScore=0;
704
+ var depth=0;
705
+ while(current&&current.nodeType===1&&depth<12){
706
+ var anchor=getSourceAnchor(current);
707
+ var score=getSourceAnchorStrength(anchor);
708
+ if(score>bestScore){
709
+ bestScore=score;
710
+ bestAnchor=anchor;
711
+ bestEl=current;
712
+ }
713
+ if(anchor&&anchor.explicit_id){
714
+ return { element:current, sourceAnchor:anchor };
715
+ }
716
+ current=current.parentElement;
717
+ depth++;
718
+ }
719
+ return { element:bestEl, sourceAnchor:bestAnchor };
720
+ }
721
+
722
+ function ownerPathSuffixMatches(current,expected){
723
+ if(!Array.isArray(current)||!Array.isArray(expected)||current.length===0||expected.length===0) return 0;
724
+ var i=current.length-1;
725
+ var j=expected.length-1;
726
+ var matches=0;
727
+ while(i>=0&&j>=0){
728
+ if(current[i]!==expected[j]) break;
729
+ matches++;
730
+ i--;
731
+ j--;
732
+ }
733
+ return matches;
734
+ }
735
+
736
+ function sourceMatchScore(node,sourceAnchor){
737
+ if(!sourceAnchor||typeof sourceAnchor!=='object') return 0;
738
+ var score=0;
739
+ var explicit=getExplicitAnchorId(node);
740
+ if(sourceAnchor.explicit_id){
741
+ if(explicit===sourceAnchor.explicit_id) score+=1400;
742
+ else if(explicit) score-=300;
743
+ }
744
+ var fiber=getReactFiberNode(node);
745
+ if(fiber){
746
+ var component=getReactFiberName(fiber);
747
+ if(sourceAnchor.component_name&&component===sourceAnchor.component_name){
748
+ score+=180;
749
+ }
750
+ if(sourceAnchor.react_key&&fiber.key!=null&&String(fiber.key)===sourceAnchor.react_key){
751
+ score+=260;
752
+ }
753
+ var currentPath=getReactOwnerPath(node);
754
+ var suffix=ownerPathSuffixMatches(currentPath,sourceAnchor.owner_path);
755
+ if(suffix>0){
756
+ score+=suffix*80;
757
+ }
758
+ var source=getSourceDebugInfo(fiber);
759
+ if(source&&sourceAnchor.source_file){
760
+ var currentFile=String(source.fileName||'');
761
+ var targetFile=String(sourceAnchor.source_file||'');
762
+ if(currentFile===targetFile){
763
+ score+=260;
764
+ if(typeof sourceAnchor.source_line==='number'&&source.lineNumber===sourceAnchor.source_line){
765
+ score+=280;
766
+ }
767
+ if(typeof sourceAnchor.source_column==='number'&&source.columnNumber===sourceAnchor.source_column){
768
+ score+=60;
769
+ }
770
+ }
771
+ }
772
+ }
773
+ if(sourceAnchor.host_tag&&node&&node.tagName&&String(node.tagName).toLowerCase()===sourceAnchor.host_tag){
774
+ score+=35;
775
+ }
776
+ return score;
777
+ }
778
+
590
779
  function textMatchScore(node,payload){
591
780
  if(!payload||!payload.text_context) return 0;
592
781
  var tc=payload.text_context;
@@ -605,17 +794,13 @@ function trackingScript() {
605
794
  }
606
795
 
607
796
  function pickBestElement(nodes,payload,metrics,fingerprintPath){
608
- if(!nodes||nodes.length===0) return { element:null, ambiguous:false, bestScore:Infinity };
797
+ if(!nodes||nodes.length===0) return { element:null, bestScore:Infinity };
609
798
  var target=getTargetDocPoint(payload,metrics);
610
799
  var best=null;
611
800
  var bestScore=Infinity;
612
- var secondScore=Infinity;
613
- var matchCount=0;
614
- var bestTextBonus=0;
615
801
  for(var i=0;i<nodes.length;i++){
616
802
  var node=nodes[i];
617
803
  if(!node||node.nodeType!==1) continue;
618
- matchCount++;
619
804
  var anchorPoint=getAnchorClientPoint(node,payload);
620
805
  var docX=anchorPoint.clientX+metrics.scrollX;
621
806
  var docY=anchorPoint.clientY+metrics.scrollY;
@@ -631,24 +816,12 @@ function trackingScript() {
631
816
  var tBonus=textMatchScore(node,payload);
632
817
  score=Math.max(0,score-tBonus);
633
818
  if(score<bestScore){
634
- secondScore=bestScore;
635
819
  best=node;
636
820
  bestScore=score;
637
- bestTextBonus=tBonus;
638
- }else if(score<secondScore){
639
- secondScore=score;
640
- }
641
- }
642
- if(!best) return { element:null, ambiguous:false, bestScore:Infinity };
643
- var ambiguous=false;
644
- if(matchCount>1){
645
- if(!target&&bestTextBonus===0){
646
- ambiguous=true;
647
- }else if((secondScore-bestScore)<30&&bestTextBonus<80){
648
- ambiguous=true;
649
821
  }
650
822
  }
651
- return { element:best, ambiguous:ambiguous, bestScore:bestScore };
823
+ if(!best) return { element:null, bestScore:Infinity };
824
+ return { element:best, bestScore:bestScore };
652
825
  }
653
826
 
654
827
  function querySelectorAllSafe(scope,selector){
@@ -661,14 +834,14 @@ function trackingScript() {
661
834
  }
662
835
 
663
836
  function resolveFromFingerprint(fingerprint,payload,metrics,scope){
664
- if(!fingerprint) return { element:null, ambiguous:false, bestScore:Infinity };
837
+ if(!fingerprint) return { element:null, bestScore:Infinity };
665
838
  var searchScope=scope||document;
666
839
  if(fingerprint.id){
667
840
  var byId=searchScope.getElementById?searchScope.getElementById(fingerprint.id):null;
668
841
  if(!byId&&searchScope.querySelector){
669
842
  try{ byId=searchScope.querySelector('#'+cssEscape(fingerprint.id)); }catch(e){}
670
843
  }
671
- if(byId) return { element:byId, ambiguous:false, bestScore:0 };
844
+ if(byId) return { element:byId, bestScore:0 };
672
845
  }
673
846
  var tag=fingerprint.tag||'*';
674
847
  var selector=tag;
@@ -679,6 +852,68 @@ function trackingScript() {
679
852
  return pickBestElement(nodes,payload,metrics,fingerprint.sibling_path);
680
853
  }
681
854
 
855
+ function resolveFromSourceAnchor(sourceAnchor,payload,metrics,scope){
856
+ if(!sourceAnchor||typeof sourceAnchor!=='object'){
857
+ return { element:null, score:0 };
858
+ }
859
+ var searchScope=scope||document;
860
+ var best=null;
861
+ var bestScore=0;
862
+ var target=getTargetDocPoint(payload,metrics);
863
+
864
+ function consider(nodes){
865
+ for(var i=0;i<nodes.length;i++){
866
+ var node=nodes[i];
867
+ if(!node||node.nodeType!==1) continue;
868
+ var score=sourceMatchScore(node,sourceAnchor);
869
+ if(score<=0) continue;
870
+ if(target){
871
+ var point=getAnchorClientPoint(node,payload);
872
+ var dx=(point.clientX+metrics.scrollX)-target.x;
873
+ var dy=(point.clientY+metrics.scrollY)-target.y;
874
+ var dist=Math.sqrt(dx*dx+dy*dy);
875
+ score-=Math.min(280,dist*0.7);
876
+ }
877
+ if(score>bestScore){
878
+ best=node;
879
+ bestScore=score;
880
+ }
881
+ }
882
+ }
883
+
884
+ if(sourceAnchor.explicit_id){
885
+ var explicitNodes=querySelectorAllSafe(searchScope,'[data-checkpoint-anchor=\"'+cssEscape(sourceAnchor.explicit_id)+'\"]');
886
+ consider(explicitNodes);
887
+ if(!best&&searchScope!==document){
888
+ consider(querySelectorAllSafe(document,'[data-checkpoint-anchor=\"'+cssEscape(sourceAnchor.explicit_id)+'\"]'));
889
+ }
890
+ if(best){
891
+ return { element:best, score:bestScore };
892
+ }
893
+ }
894
+
895
+ var tag=sourceAnchor.host_tag||((payload.dom_fingerprint&&payload.dom_fingerprint.tag)||'*');
896
+ var nodes=querySelectorAllSafe(searchScope,tag);
897
+ if(nodes.length>1800){
898
+ nodes=nodes.slice(0,1800);
899
+ }
900
+ consider(nodes);
901
+ if(!best&&searchScope!==document){
902
+ var docNodes=querySelectorAllSafe(document,tag);
903
+ if(docNodes.length>1800){
904
+ docNodes=docNodes.slice(0,1800);
905
+ }
906
+ consider(docNodes);
907
+ }
908
+ var minScore=160;
909
+ if(sourceAnchor.explicit_id) minScore=900;
910
+ else if(sourceAnchor.source_file) minScore=260;
911
+ if(bestScore<minScore){
912
+ return { element:null, score:bestScore };
913
+ }
914
+ return { element:best, score:bestScore };
915
+ }
916
+
682
917
  function isStrongContainerHint(hint){
683
918
  if(!hint||typeof hint!=='object') return false;
684
919
  if(typeof hint.id==='string'&&hint.id.length>0) return true;
@@ -710,19 +945,7 @@ function trackingScript() {
710
945
 
711
946
  function resolveAnchor(payload){
712
947
  if(!payload||typeof payload!=='object') return {coordFallback:null,strategy:'none',matchedSelector:null,status:'none'};
713
- if(!viewContextMatches(payload.view_context)){
714
- return {
715
- coordFallback:payload.coord_fallback||null,
716
- strategy:'context_mismatch',
717
- matchedSelector:null,
718
- status:'context_mismatch'
719
- };
720
- }
721
- var element=null;
722
- var strategy='none';
723
- var matchedSelector=null;
724
948
  var metrics=getMetrics();
725
- var ambiguous=false;
726
949
  var transientRoot=null;
727
950
  var hasTransientContext=!!(payload.transient_context&&typeof payload.transient_context==='object');
728
951
  if(hasTransientContext){
@@ -732,100 +955,32 @@ function trackingScript() {
732
955
  }
733
956
  }
734
957
 
735
- function trySelectorChain(scope,scopeTag){
736
- if(!Array.isArray(payload.selector_chain)) return false;
737
- for(var i=0;i<payload.selector_chain.length;i++){
738
- var candidate=payload.selector_chain[i];
739
- if(!candidate||typeof candidate.selector!=='string') continue;
740
- var nodes=querySelectorAllSafe(scope,candidate.selector);
741
- var found=pickBestElement(nodes,payload,metrics,null);
742
- if(found&&found.element){
743
- element=found.element;
744
- ambiguous=!!found.ambiguous;
745
- strategy='selector';
746
- matchedSelector=(scopeTag?scopeTag+': ':'')+candidate.selector;
747
- return true;
748
- }
749
- }
750
- return false;
751
- }
752
-
753
- if(transientRoot){
754
- trySelectorChain(transientRoot,'transient_root');
755
- }
756
- if(!element){
757
- trySelectorChain(document,'document');
758
- }
759
-
760
- if(
761
- !element&&
762
- payload.container_hint&&
763
- typeof payload.container_hint.selector==='string'&&
764
- isStrongContainerHint(payload.container_hint)
765
- ){
766
- var containerNodesInRoot=transientRoot?querySelectorAllSafe(transientRoot,payload.container_hint.selector):[];
767
- var containerMatch=pickBestElement(containerNodesInRoot,payload,metrics,null);
768
- if(!containerMatch.element){
769
- var containerNodes=querySelectorAllSafe(document,payload.container_hint.selector);
770
- containerMatch=pickBestElement(containerNodes,payload,metrics,null);
771
- }
772
- if(containerMatch.element){
773
- element=containerMatch.element;
774
- ambiguous=!!containerMatch.ambiguous;
775
- strategy='container';
776
- matchedSelector=payload.container_hint.selector;
777
- }
778
- }
779
- if(!element){
780
- var fpMatch=resolveFromFingerprint(payload.dom_fingerprint,payload,metrics,transientRoot||undefined);
781
- if(!fpMatch.element){
782
- fpMatch=resolveFromFingerprint(payload.dom_fingerprint,payload,metrics,document);
783
- }
784
- if(fpMatch.element){
785
- element=fpMatch.element;
786
- ambiguous=!!fpMatch.ambiguous;
787
- strategy='fingerprint';
788
- }
789
- }
790
- if(!element&&payload.scroll_context){
791
- var fromContainer=getPointFromScrollContext(payload.scroll_context);
792
- if(fromContainer){
958
+ if(!payload.source_anchor){
959
+ if(payload.coord_fallback){
793
960
  return {
794
- coordFallback:buildCoordFallback(fromContainer.clientX,fromContainer.clientY,metrics),
795
- strategy:'scroll_container',
796
- matchedSelector:null,
797
- status:'resolved'
961
+ coordFallback:payload.coord_fallback,
962
+ strategy:'fallback',
963
+ matchedSelector:'legacy_coord_fallback',
964
+ status:'fallback_only'
798
965
  };
799
966
  }
967
+ return {coordFallback:null,strategy:'none',matchedSelector:null,status:'none'};
800
968
  }
801
- if(!element) return {coordFallback:null,strategy:'fallback',matchedSelector:null,status:'fallback_only'};
802
- if(ambiguous){
803
- return {
804
- coordFallback:payload.coord_fallback||null,
805
- strategy:'ambiguous',
806
- matchedSelector:matchedSelector,
807
- status:'ambiguous'
808
- };
809
- }
810
- var target=getTargetDocPoint(payload,metrics);
811
- if(target){
812
- var anchorPoint=getAnchorClientPoint(element,payload);
813
- var distX=(anchorPoint.clientX+metrics.scrollX)-target.x;
814
- var distY=(anchorPoint.clientY+metrics.scrollY)-target.y;
815
- var dist=Math.sqrt(distX*distX+distY*distY);
816
- if(dist>220){
817
- return {
818
- coordFallback:payload.coord_fallback||null,
819
- strategy:'ambiguous',
820
- matchedSelector:matchedSelector,
821
- status:'ambiguous'
822
- };
823
- }
969
+
970
+ var sourceScope=transientRoot||document;
971
+ var sourceMatch=resolveFromSourceAnchor(payload.source_anchor,payload,metrics,sourceScope);
972
+ if(!sourceMatch||!sourceMatch.element){
973
+ return {coordFallback:null,strategy:'fallback',matchedSelector:'source_anchor_miss',status:'fallback_only'};
824
974
  }
825
- var point=getAnchorClientPoint(element,payload);
975
+ var point=getAnchorClientPoint(sourceMatch.element,payload);
826
976
  var clientX=point.clientX;
827
977
  var clientY=point.clientY;
828
- return {coordFallback:buildCoordFallback(clientX,clientY,metrics),strategy:strategy,matchedSelector:matchedSelector,status:'resolved'};
978
+ return {
979
+ coordFallback:buildCoordFallback(clientX,clientY,metrics),
980
+ strategy:'source',
981
+ matchedSelector:'source_anchor',
982
+ status:'resolved'
983
+ };
829
984
  }
830
985
 
831
986
  function setPickMode(enabled){
@@ -841,12 +996,22 @@ function trackingScript() {
841
996
  window.removeEventListener('click',pickClickHandler,true);
842
997
  pickClickHandler=null;
843
998
  }
999
+ if(pickMoveHandler){
1000
+ window.removeEventListener('mousemove',pickMoveHandler,true);
1001
+ pickMoveHandler=null;
1002
+ }
844
1003
  if(pickEscHandler){
845
1004
  window.removeEventListener('keydown',pickEscHandler,true);
846
1005
  pickEscHandler=null;
847
1006
  }
1007
+ hidePickInspectUi();
848
1008
  return;
849
1009
  }
1010
+ pickMoveHandler=function(ev){
1011
+ if(!pickMode) return;
1012
+ var target=ev.target&&ev.target.nodeType===1?ev.target:null;
1013
+ renderPickInspect(target,ev.clientX,ev.clientY);
1014
+ };
850
1015
  pickClickHandler=function(ev){
851
1016
  try{
852
1017
  if(!pickMode) return;
@@ -877,6 +1042,7 @@ function trackingScript() {
877
1042
  setPickMode(false);
878
1043
  }
879
1044
  };
1045
+ window.addEventListener('mousemove',pickMoveHandler,true);
880
1046
  window.addEventListener('click',pickClickHandler,true);
881
1047
  window.addEventListener('keydown',pickEscHandler,true);
882
1048
  }
@@ -1039,7 +1205,7 @@ function startInjectionProxy(targetPort) {
1039
1205
  proxyRes.on('data', (chunk) => chunks.push(chunk));
1040
1206
  proxyRes.on('end', () => {
1041
1207
  let body = Buffer.concat(chunks).toString('utf-8');
1042
- if (!body.includes('data-checkpoint')) {
1208
+ if (!body.includes('data-checkpoint-script="1"')) {
1043
1209
  if (body.includes('</head>')) {
1044
1210
  body = body.replace('</head>', SCRIPT + '\n</head>');
1045
1211
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "checkpoint-cli",
3
- "version": "0.1.9",
3
+ "version": "0.2.1",
4
4
  "description": "Share your localhost with reviewers — get visual feedback directly on the page",
5
5
  "keywords": [
6
6
  "checkpoint",