checkpoint-cli 0.1.6 → 0.1.7

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 +263 -48
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -208,6 +208,129 @@ function trackingScript() {
208
208
  };
209
209
  }
210
210
 
211
+ function parseZIndex(value){
212
+ var n=parseInt(String(value||''),10);
213
+ return isNaN(n)?0:n;
214
+ }
215
+
216
+ function detectTransientKind(el){
217
+ if(!el||!el.tagName) return 'unknown';
218
+ try{
219
+ var role=(el.getAttribute&&el.getAttribute('role')||'').toLowerCase();
220
+ var classes=(el.className&&typeof el.className==='string'?el.className:'').toLowerCase();
221
+ var id=(el.id||'').toLowerCase();
222
+ var label=(role+' '+classes+' '+id).trim();
223
+ if(role==='dialog'||el.getAttribute('aria-modal')==='true'||label.indexOf('modal')>=0||label.indexOf('dialog')>=0){
224
+ return 'dialog';
225
+ }
226
+ if(label.indexOf('drawer')>=0||label.indexOf('sheet')>=0||label.indexOf('sidebar')>=0){
227
+ return 'drawer';
228
+ }
229
+ if(label.indexOf('popover')>=0) return 'popover';
230
+ if(role==='menu'||label.indexOf('menu')>=0||label.indexOf('dropdown')>=0) return 'menu';
231
+ if(role==='tooltip'||label.indexOf('tooltip')>=0) return 'tooltip';
232
+ }catch(e){}
233
+ return 'unknown';
234
+ }
235
+
236
+ function isTransientRootCandidate(el){
237
+ if(!el||!el.tagName) return false;
238
+ try{
239
+ var role=(el.getAttribute&&el.getAttribute('role')||'').toLowerCase();
240
+ var ariaModal=(el.getAttribute&&el.getAttribute('aria-modal')||'').toLowerCase();
241
+ var dataState=(el.getAttribute&&el.getAttribute('data-state')||'').toLowerCase();
242
+ var classes=(el.className&&typeof el.className==='string'?el.className:'').toLowerCase();
243
+ var id=(el.id||'').toLowerCase();
244
+ var style=window.getComputedStyle?window.getComputedStyle(el):null;
245
+ var position=style?style.position:'';
246
+ var zIndex=parseZIndex(style?style.zIndex:0);
247
+ if(role==='dialog'||role==='menu'||role==='tooltip') return true;
248
+ if(ariaModal==='true') return true;
249
+ if(dataState==='open'&&(classes.indexOf('modal')>=0||classes.indexOf('popover')>=0||classes.indexOf('drawer')>=0)) return true;
250
+ if(classes.indexOf('modal')>=0||classes.indexOf('dialog')>=0||classes.indexOf('drawer')>=0||classes.indexOf('popover')>=0||classes.indexOf('tooltip')>=0||classes.indexOf('dropdown')>=0) return true;
251
+ if(id.indexOf('modal')>=0||id.indexOf('dialog')>=0||id.indexOf('drawer')>=0||id.indexOf('popover')>=0) return true;
252
+ if((position==='fixed'||position==='sticky')&&zIndex>=100) return true;
253
+ }catch(e){}
254
+ return false;
255
+ }
256
+
257
+ function isLikelyPortaled(el){
258
+ if(!el||!el.parentElement) return false;
259
+ try{
260
+ var root=el;
261
+ var steps=0;
262
+ while(root&&root.parentElement&&steps<8){
263
+ root=root.parentElement;
264
+ steps++;
265
+ }
266
+ if(!root||!root.parentElement) return false;
267
+ if(root===document.body) return true;
268
+ if(el.parentElement===document.body) return true;
269
+ if(el.parentElement&&el.parentElement.id&&String(el.parentElement.id).toLowerCase().indexOf('portal')>=0) return true;
270
+ var classes=(el.className&&typeof el.className==='string'?el.className:'').toLowerCase();
271
+ if(classes.indexOf('portal')>=0) return true;
272
+ }catch(e){}
273
+ return false;
274
+ }
275
+
276
+ function getOpenerCandidates(){
277
+ var out=[];
278
+ var seen={};
279
+ var selectors=[
280
+ '[aria-haspopup=\"dialog\"]',
281
+ '[aria-controls]',
282
+ '[data-modal-trigger]',
283
+ '[data-dialog-trigger]',
284
+ 'button',
285
+ '[role=\"button\"]',
286
+ 'a'
287
+ ];
288
+ for(var s=0;s<selectors.length;s++){
289
+ var selector=selectors[s];
290
+ try{
291
+ var nodes=document.querySelectorAll(selector);
292
+ for(var i=0;i<nodes.length&&out.length<8;i++){
293
+ var node=nodes[i];
294
+ if(!node||node.nodeType!==1) continue;
295
+ var sel=elementSelector(node);
296
+ if(!sel||seen[sel]) continue;
297
+ seen[sel]=true;
298
+ var score=0.3;
299
+ var label=(node.getAttribute&&node.getAttribute('aria-label')||'').toLowerCase();
300
+ var text=shortText(node.textContent).toLowerCase();
301
+ if(label.indexOf('setting')>=0||text.indexOf('setting')>=0) score=0.9;
302
+ else if(label.indexOf('open')>=0||text.indexOf('open')>=0) score=0.7;
303
+ out.push({selector:sel,score:score});
304
+ }
305
+ }catch(e){}
306
+ }
307
+ out.sort(function(a,b){ return b.score-a.score; });
308
+ return out.slice(0,6);
309
+ }
310
+
311
+ function getTransientContext(el){
312
+ if(!el||typeof el.closest!=='function') return null;
313
+ var current=el;
314
+ var depth=0;
315
+ while(current&&current.nodeType===1&&depth<8){
316
+ if(isTransientRootCandidate(current)){
317
+ var style=window.getComputedStyle?window.getComputedStyle(current):null;
318
+ return {
319
+ kind:detectTransientKind(current),
320
+ root_selector_chain:getSelectorChain(current),
321
+ root_fingerprint:getDomFingerprint(current),
322
+ is_portaled:isLikelyPortaled(current),
323
+ z_index:parseZIndex(style?style.zIndex:0),
324
+ is_fixed:style?style.position==='fixed':false,
325
+ opener_candidates:getOpenerCandidates()
326
+ };
327
+ }
328
+ current=current.parentElement;
329
+ depth++;
330
+ }
331
+ return null;
332
+ }
333
+
211
334
  function buildCoordFallback(clientX,clientY,metrics){
212
335
  var safeViewW=metrics.viewWidth||1;
213
336
  var safeViewH=metrics.viewHeight||1;
@@ -253,6 +376,7 @@ function trackingScript() {
253
376
  parent:textParent
254
377
  },
255
378
  container_hint:getContainerHint(el),
379
+ transient_context:getTransientContext(el),
256
380
  coord_fallback:fallback
257
381
  };
258
382
  }
@@ -355,10 +479,23 @@ function trackingScript() {
355
479
  return best;
356
480
  }
357
481
 
358
- function resolveFromFingerprint(fingerprint,payload,metrics){
482
+ function querySelectorAllSafe(scope,selector){
483
+ if(!scope||!selector) return [];
484
+ try{
485
+ var nodeList=scope.querySelectorAll(selector);
486
+ return nodeList?Array.prototype.slice.call(nodeList):[];
487
+ }catch(e){}
488
+ return [];
489
+ }
490
+
491
+ function resolveFromFingerprint(fingerprint,payload,metrics,scope){
359
492
  if(!fingerprint) return null;
493
+ var searchScope=scope||document;
360
494
  if(fingerprint.id){
361
- var byId=document.getElementById(fingerprint.id);
495
+ var byId=searchScope.getElementById?searchScope.getElementById(fingerprint.id):null;
496
+ if(!byId&&searchScope.querySelector){
497
+ try{ byId=searchScope.querySelector('#'+cssEscape(fingerprint.id)); }catch(e){}
498
+ }
362
499
  if(byId) return byId;
363
500
  }
364
501
  var tag=fingerprint.tag||'*';
@@ -366,11 +503,8 @@ function trackingScript() {
366
503
  if(Array.isArray(fingerprint.classes)&&fingerprint.classes.length>0){
367
504
  selector+= '.'+fingerprint.classes.map(cssEscape).join('.');
368
505
  }
369
- try{
370
- var nodes=document.querySelectorAll(selector);
371
- return pickBestElement(nodes,payload,metrics,fingerprint.sibling_path);
372
- }catch(e){}
373
- return null;
506
+ var nodes=querySelectorAllSafe(searchScope,selector);
507
+ return pickBestElement(nodes,payload,metrics,fingerprint.sibling_path);
374
508
  }
375
509
 
376
510
  function isStrongContainerHint(hint){
@@ -384,56 +518,95 @@ function trackingScript() {
384
518
  return false;
385
519
  }
386
520
 
521
+ function resolveTransientRoot(payload,metrics){
522
+ var transient=payload&&payload.transient_context;
523
+ if(!transient||typeof transient!=='object') return null;
524
+ var root=null;
525
+ if(Array.isArray(transient.root_selector_chain)){
526
+ for(var i=0;i<transient.root_selector_chain.length;i++){
527
+ var candidate=transient.root_selector_chain[i];
528
+ if(!candidate||typeof candidate.selector!=='string') continue;
529
+ var nodes=querySelectorAllSafe(document,candidate.selector);
530
+ root=pickBestElement(nodes,payload,metrics,transient.root_fingerprint&&transient.root_fingerprint.sibling_path);
531
+ if(root) return root;
532
+ }
533
+ }
534
+ root=resolveFromFingerprint(transient.root_fingerprint,payload,metrics,document);
535
+ return root;
536
+ }
537
+
387
538
  function resolveAnchor(payload){
388
- if(!payload||typeof payload!=='object') return {coordFallback:null,strategy:'none',matchedSelector:null};
539
+ if(!payload||typeof payload!=='object') return {coordFallback:null,strategy:'none',matchedSelector:null,status:'none'};
389
540
  var element=null;
390
541
  var strategy='none';
391
542
  var matchedSelector=null;
392
543
  var metrics=getMetrics();
393
- if(Array.isArray(payload.selector_chain)){
544
+ var transientRoot=null;
545
+ var hasTransientContext=!!(payload.transient_context&&typeof payload.transient_context==='object');
546
+ if(hasTransientContext){
547
+ transientRoot=resolveTransientRoot(payload,metrics);
548
+ if(!transientRoot){
549
+ return {coordFallback:null,strategy:'transient_missing',matchedSelector:null,status:'transient_missing'};
550
+ }
551
+ }
552
+
553
+ function trySelectorChain(scope,scopeTag){
554
+ if(!Array.isArray(payload.selector_chain)) return false;
394
555
  for(var i=0;i<payload.selector_chain.length;i++){
395
556
  var candidate=payload.selector_chain[i];
396
557
  if(!candidate||typeof candidate.selector!=='string') continue;
397
- try{
398
- var nodes=document.querySelectorAll(candidate.selector);
399
- var found=pickBestElement(nodes,payload,metrics,null);
400
- if(found){
401
- element=found;
402
- strategy='selector';
403
- matchedSelector=candidate.selector;
404
- break;
405
- }
406
- }catch(e){}
558
+ var nodes=querySelectorAllSafe(scope,candidate.selector);
559
+ var found=pickBestElement(nodes,payload,metrics,null);
560
+ if(found){
561
+ element=found;
562
+ strategy='selector';
563
+ matchedSelector=(scopeTag?scopeTag+': ':'')+candidate.selector;
564
+ return true;
565
+ }
407
566
  }
567
+ return false;
408
568
  }
569
+
570
+ if(transientRoot){
571
+ trySelectorChain(transientRoot,'transient_root');
572
+ }
573
+ if(!element){
574
+ trySelectorChain(document,'document');
575
+ }
576
+
409
577
  if(
410
578
  !element&&
411
579
  payload.container_hint&&
412
580
  typeof payload.container_hint.selector==='string'&&
413
581
  isStrongContainerHint(payload.container_hint)
414
582
  ){
415
- try{
416
- var containerNodes=document.querySelectorAll(payload.container_hint.selector);
417
- var containerEl=pickBestElement(containerNodes,payload,metrics,null);
418
- if(containerEl){
419
- element=containerEl;
420
- strategy='container';
421
- matchedSelector=payload.container_hint.selector;
422
- }
423
- }catch(e){}
583
+ var containerNodesInRoot=transientRoot?querySelectorAllSafe(transientRoot,payload.container_hint.selector):[];
584
+ var containerEl=pickBestElement(containerNodesInRoot,payload,metrics,null);
585
+ if(!containerEl){
586
+ var containerNodes=querySelectorAllSafe(document,payload.container_hint.selector);
587
+ containerEl=pickBestElement(containerNodes,payload,metrics,null);
588
+ }
589
+ if(containerEl){
590
+ element=containerEl;
591
+ strategy='container';
592
+ matchedSelector=payload.container_hint.selector;
593
+ }
424
594
  }
425
595
  if(!element){
426
- var fpEl=resolveFromFingerprint(payload.dom_fingerprint,payload,metrics);
596
+ var fpEl=resolveFromFingerprint(payload.dom_fingerprint,payload,metrics,transientRoot||undefined);
597
+ if(!fpEl){
598
+ fpEl=resolveFromFingerprint(payload.dom_fingerprint,payload,metrics,document);
599
+ }
427
600
  if(fpEl){
428
601
  element=fpEl;
429
602
  strategy='fingerprint';
430
603
  }
431
604
  }
432
- if(!element) return {coordFallback:null,strategy:'none',matchedSelector:null};
605
+ if(!element) return {coordFallback:null,strategy:'fallback',matchedSelector:null,status:'fallback_only'};
433
606
  var point=getAnchorClientPoint(element,payload);
434
607
  var clientX=point.clientX;
435
608
  var clientY=point.clientY;
436
- return {coordFallback:buildCoordFallback(clientX,clientY,metrics),strategy:strategy,matchedSelector:matchedSelector};
609
+ return {coordFallback:buildCoordFallback(clientX,clientY,metrics),strategy:strategy,matchedSelector:matchedSelector,status:'resolved'};
437
610
  }
438
611
 
439
612
  function setPickMode(enabled){
@@ -526,8 +699,59 @@ function trackingScript() {
526
699
  });
527
700
  }
528
701
  }
702
+
703
+ var mutationTick=false;
704
+ function reportDomMutation(reason){
705
+ try{
706
+ window.parent.postMessage({
707
+ type:'checkpoint:domMutation',
708
+ reason:reason||'mutation',
709
+ path:location.pathname+location.search+location.hash
710
+ },'*');
711
+ }catch(e){}
712
+ }
713
+
714
+ function onMutation(){
715
+ if(mutationTick) return;
716
+ mutationTick=true;
717
+ requestAnimationFrame(function(){
718
+ mutationTick=false;
719
+ reportScroll();
720
+ reportDomMutation('mutation');
721
+ });
722
+ }
723
+
724
+ function setupMutationObserver(){
725
+ try{
726
+ if(!window.MutationObserver||!document.documentElement) return;
727
+ var observer=new MutationObserver(function(mutations){
728
+ for(var i=0;i<mutations.length;i++){
729
+ var m=mutations[i];
730
+ if(m.type==='childList'){
731
+ if((m.addedNodes&&m.addedNodes.length>0)||(m.removedNodes&&m.removedNodes.length>0)){
732
+ onMutation();
733
+ return;
734
+ }
735
+ }
736
+ if(m.type==='attributes'){
737
+ onMutation();
738
+ return;
739
+ }
740
+ }
741
+ });
742
+ observer.observe(document.documentElement,{
743
+ childList:true,
744
+ subtree:true,
745
+ attributes:true,
746
+ attributeFilter:['class','style','aria-hidden','aria-modal','data-state','open']
747
+ });
748
+ window.addEventListener('beforeunload',function(){ try{observer.disconnect();}catch(e){}; });
749
+ }catch(e){}
750
+ }
751
+
529
752
  reportNav();
530
753
  reportScroll();
754
+ setupMutationObserver();
531
755
  var _ps=history.pushState,_rs=history.replaceState;
532
756
  history.pushState=function(){_ps.apply(this,arguments);reportNav();};
533
757
  history.replaceState=function(){_rs.apply(this,arguments);reportNav();};
@@ -549,23 +773,14 @@ function trackingScript() {
549
773
  var item=e.data.items[i];
550
774
  if(!item||typeof item.id!=='string') continue;
551
775
  var result=resolveAnchor(item.anchorPayload);
552
- if(result.coordFallback){
553
- positions.push({
554
- id:item.id,
555
- found:true,
556
- strategy:result.strategy,
557
- matchedSelector:result.matchedSelector,
558
- coordFallback:result.coordFallback
559
- });
560
- } else {
561
- positions.push({
562
- id:item.id,
563
- found:false,
564
- strategy:'fallback',
565
- matchedSelector:null,
566
- coordFallback:null
567
- });
568
- }
776
+ positions.push({
777
+ id:item.id,
778
+ found:!!result.coordFallback,
779
+ status:result.status||'none',
780
+ strategy:result.strategy,
781
+ matchedSelector:result.matchedSelector,
782
+ coordFallback:result.coordFallback||null
783
+ });
569
784
  }
570
785
  window.parent.postMessage({
571
786
  type:'checkpoint:anchorsResolved',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "checkpoint-cli",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
4
4
  "description": "Share your localhost with reviewers — get visual feedback directly on the page",
5
5
  "keywords": [
6
6
  "checkpoint",