checkpoint-cli 0.2.2 → 0.3.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/dist/index.js CHANGED
@@ -11,6 +11,7 @@ const readline_1 = __importDefault(require("readline"));
11
11
  const chalk_1 = __importDefault(require("chalk"));
12
12
  const supabase_js_1 = require("@supabase/supabase-js");
13
13
  const config_js_1 = require("./config.js");
14
+ const tracking_script_minimal_js_1 = require("./tracking-script-minimal.js");
14
15
  /* ── Authenticated Supabase Client ── */
15
16
  function getSupabase() {
16
17
  const config = (0, config_js_1.loadConfig)();
@@ -90,6 +91,10 @@ function trackingScript() {
90
91
  var pickMoveHandler=null;
91
92
  var pickInspectBox=null;
92
93
  var pickInspectLabel=null;
94
+ var pickPointerDownHandler=null;
95
+ var pickHoverRaf=0;
96
+ var pickLastMouseX=0;
97
+ var pickLastMouseY=0;
93
98
 
94
99
  function getMetrics(){
95
100
  var de=document.documentElement;
@@ -205,7 +210,7 @@ function trackingScript() {
205
210
  var sourceTarget=findBestSourceAnchorTarget(target);
206
211
  var anchorEl=sourceTarget.element||target;
207
212
  var sourceAnchor=sourceTarget.sourceAnchor||getSourceAnchor(anchorEl);
208
- var rect=anchorEl.getBoundingClientRect?anchorEl.getBoundingClientRect():null;
213
+ var rect=target.getBoundingClientRect?target.getBoundingClientRect():null;
209
214
  if(!rect||rect.width<1||rect.height<1){
210
215
  hidePickInspectUi();
211
216
  return;
@@ -252,6 +257,16 @@ function trackingScript() {
252
257
 
253
258
  function getEventElement(ev){
254
259
  if(!ev) return null;
260
+ try{
261
+ if(typeof ev.composedPath==='function'){
262
+ var path=ev.composedPath();
263
+ if(path&&path.length>0){
264
+ var first=path[0];
265
+ if(first&&first.nodeType===1) return first;
266
+ if(first&&first.nodeType===3&&first.parentElement) return first.parentElement;
267
+ }
268
+ }
269
+ }catch(e){}
255
270
  var target=ev.target||null;
256
271
  if(target&&target.nodeType===1){
257
272
  return target;
@@ -270,6 +285,18 @@ function trackingScript() {
270
285
  return null;
271
286
  }
272
287
 
288
+ function getElementAtPoint(x,y){
289
+ try{
290
+ if(typeof x==='number'&&typeof y==='number'&&document.elementFromPoint){
291
+ var node=document.elementFromPoint(x,y);
292
+ if(node&&node.nodeType===1){
293
+ return node;
294
+ }
295
+ }
296
+ }catch(e){}
297
+ return null;
298
+ }
299
+
273
300
  function elementSelector(el){
274
301
  if(!el||!el.tagName) return '';
275
302
  var tag=el.tagName.toLowerCase();
@@ -1016,21 +1043,60 @@ function trackingScript() {
1016
1043
  window.removeEventListener('click',pickClickHandler,true);
1017
1044
  pickClickHandler=null;
1018
1045
  }
1046
+ if(pickPointerDownHandler){
1047
+ window.removeEventListener('pointerdown',pickPointerDownHandler,true);
1048
+ pickPointerDownHandler=null;
1049
+ }
1019
1050
  if(pickMoveHandler){
1020
- window.removeEventListener('mousemove',pickMoveHandler,true);
1051
+ document.removeEventListener('mousemove',pickMoveHandler,true);
1021
1052
  pickMoveHandler=null;
1022
1053
  }
1023
1054
  if(pickEscHandler){
1024
1055
  window.removeEventListener('keydown',pickEscHandler,true);
1025
1056
  pickEscHandler=null;
1026
1057
  }
1058
+ if(pickHoverRaf){
1059
+ cancelAnimationFrame(pickHoverRaf);
1060
+ pickHoverRaf=0;
1061
+ }
1027
1062
  hidePickInspectUi();
1028
1063
  return;
1029
1064
  }
1030
1065
  pickMoveHandler=function(ev){
1031
1066
  if(!pickMode) return;
1032
- var target=getEventElement(ev);
1033
- renderPickInspect(target,ev.clientX,ev.clientY);
1067
+ pickLastMouseX=ev.clientX;
1068
+ pickLastMouseY=ev.clientY;
1069
+ if(pickHoverRaf) return;
1070
+ pickHoverRaf=requestAnimationFrame(function(){
1071
+ pickHoverRaf=0;
1072
+ var target=getElementAtPoint(pickLastMouseX,pickLastMouseY)||getEventElement(ev);
1073
+ renderPickInspect(target,pickLastMouseX,pickLastMouseY);
1074
+ });
1075
+ };
1076
+ pickPointerDownHandler=function(ev){
1077
+ if(!pickMode) return;
1078
+ var target=getElementAtPoint(ev.clientX,ev.clientY)||getEventElement(ev);
1079
+ if(!target) return;
1080
+ ev.preventDefault();
1081
+ ev.stopPropagation();
1082
+ if(typeof ev.stopImmediatePropagation==='function'){
1083
+ ev.stopImmediatePropagation();
1084
+ }
1085
+ try{
1086
+ var payload=buildAnchorPayload(target,ev.clientX,ev.clientY);
1087
+ window.parent.postMessage({
1088
+ type:'checkpoint:pickResult',
1089
+ path:location.pathname+location.search+location.hash,
1090
+ anchorPayload:payload,
1091
+ point:{
1092
+ xPercent:payload.coord_fallback&&typeof payload.coord_fallback.x_percent==='number'?payload.coord_fallback.x_percent:50,
1093
+ yPercent:payload.coord_fallback&&typeof payload.coord_fallback.y_percent==='number'?payload.coord_fallback.y_percent:50,
1094
+ scrollY:payload.coord_fallback&&typeof payload.coord_fallback.scroll_y==='number'?payload.coord_fallback.scroll_y:0,
1095
+ viewHeight:payload.coord_fallback&&typeof payload.coord_fallback.viewport_height==='number'?payload.coord_fallback.viewport_height:0
1096
+ }
1097
+ },'*');
1098
+ }catch(e){}
1099
+ setPickMode(false);
1034
1100
  };
1035
1101
  pickClickHandler=function(ev){
1036
1102
  try{
@@ -1062,7 +1128,8 @@ function trackingScript() {
1062
1128
  setPickMode(false);
1063
1129
  }
1064
1130
  };
1065
- window.addEventListener('mousemove',pickMoveHandler,true);
1131
+ document.addEventListener('mousemove',pickMoveHandler,true);
1132
+ window.addEventListener('pointerdown',pickPointerDownHandler,true);
1066
1133
  window.addEventListener('click',pickClickHandler,true);
1067
1134
  window.addEventListener('keydown',pickEscHandler,true);
1068
1135
  }
@@ -1202,7 +1269,7 @@ function trackingScript() {
1202
1269
  }
1203
1270
  /* ── Injection Proxy ── */
1204
1271
  function startInjectionProxy(targetPort) {
1205
- const SCRIPT = trackingScript();
1272
+ const SCRIPT = (0, tracking_script_minimal_js_1.buildMinimalTrackingScript)();
1206
1273
  return new Promise((resolve, reject) => {
1207
1274
  const server = http_1.default.createServer((req, res) => {
1208
1275
  const fwdHeaders = { ...req.headers, host: `localhost:${targetPort}` };
@@ -1225,7 +1292,7 @@ function startInjectionProxy(targetPort) {
1225
1292
  proxyRes.on('data', (chunk) => chunks.push(chunk));
1226
1293
  proxyRes.on('end', () => {
1227
1294
  let body = Buffer.concat(chunks).toString('utf-8');
1228
- if (!body.includes('data-checkpoint-script="1"')) {
1295
+ if (!body.includes('data-checkpoint-script=')) {
1229
1296
  if (body.includes('</head>')) {
1230
1297
  body = body.replace('</head>', SCRIPT + '\n</head>');
1231
1298
  }
@@ -1538,7 +1605,7 @@ const program = new commander_1.Command();
1538
1605
  program
1539
1606
  .name('checkpoint')
1540
1607
  .description('Share your localhost with reviewers — get visual feedback directly on the page')
1541
- .version('0.1.0');
1608
+ .version('0.3.0');
1542
1609
  // ── checkpoint login ──
1543
1610
  program
1544
1611
  .command('login')
@@ -0,0 +1 @@
1
+ export declare function buildMinimalTrackingScript(): string;
@@ -0,0 +1,599 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildMinimalTrackingScript = buildMinimalTrackingScript;
4
+ function buildMinimalTrackingScript() {
5
+ return String.raw `
6
+ <script data-checkpoint-script="1">
7
+ (function(){
8
+ var pickMode=false;
9
+ var pickMoveHandler=null;
10
+ var pickPointerDownHandler=null;
11
+ var pickClickHandler=null;
12
+ var pickEscHandler=null;
13
+ var hoverRaf=0;
14
+ var lastMouseX=0;
15
+ var lastMouseY=0;
16
+ var inspectBox=null;
17
+ var inspectLabel=null;
18
+ var lastPath='';
19
+ var mutationTick=false;
20
+
21
+ function getMetrics(){
22
+ var de=document.documentElement;
23
+ return {
24
+ scrollX:window.scrollX||window.pageXOffset||0,
25
+ scrollY:window.scrollY||window.pageYOffset||0,
26
+ docWidth:de.scrollWidth||0,
27
+ docHeight:de.scrollHeight||0,
28
+ viewWidth:de.clientWidth||window.innerWidth||0,
29
+ viewHeight:de.clientHeight||window.innerHeight||0
30
+ };
31
+ }
32
+
33
+ function clampPercent(v){
34
+ if(v<0) return 0;
35
+ if(v>100) return 100;
36
+ return v;
37
+ }
38
+
39
+ function shortText(value){
40
+ if(!value) return '';
41
+ return String(value).replace(/\s+/g,' ').trim().slice(0,120);
42
+ }
43
+
44
+ function cssEscape(value){
45
+ try{
46
+ if(window.CSS&&typeof window.CSS.escape==='function') return window.CSS.escape(value);
47
+ }catch(e){}
48
+ return String(value).replace(/[^a-zA-Z0-9_-]/g,'\\$&');
49
+ }
50
+
51
+ function getReactFiberNode(el){
52
+ if(!el||typeof el!=='object') return null;
53
+ try{
54
+ var keys=Object.keys(el);
55
+ for(var i=0;i<keys.length;i++){
56
+ var key=keys[i];
57
+ if(key.indexOf('__reactFiber$')===0||key.indexOf('__reactInternalInstance$')===0){
58
+ return el[key];
59
+ }
60
+ }
61
+ }catch(e){}
62
+ return null;
63
+ }
64
+
65
+ function getReactFiberName(fiber){
66
+ try{
67
+ if(!fiber) return '';
68
+ var t=fiber.type||fiber.elementType;
69
+ if(typeof t==='string') return t;
70
+ if(t&&typeof t.displayName==='string'&&t.displayName) return t.displayName;
71
+ if(t&&typeof t.name==='string'&&t.name) return t.name;
72
+ }catch(e){}
73
+ return '';
74
+ }
75
+
76
+ function getSourceDebugInfo(fiber){
77
+ var current=fiber;
78
+ var steps=0;
79
+ while(current&&steps<24){
80
+ if(current._debugSource) return current._debugSource;
81
+ current=current.return||null;
82
+ steps++;
83
+ }
84
+ return null;
85
+ }
86
+
87
+ function getReactOwnerPathFromFiber(fiber){
88
+ if(!fiber) return [];
89
+ var path=[];
90
+ var current=fiber;
91
+ var steps=0;
92
+ while(current&&steps<24){
93
+ var name=getReactFiberName(current);
94
+ if(name){
95
+ var keyVal=current.key!=null?String(current.key):'';
96
+ path.push(keyVal?name+'#'+keyVal:name);
97
+ }
98
+ current=current.return||null;
99
+ steps++;
100
+ }
101
+ path.reverse();
102
+ return path.slice(-12);
103
+ }
104
+
105
+ function getExplicitAnchorId(el){
106
+ if(!el||typeof el.closest!=='function') return '';
107
+ var holder=el.closest('[data-checkpoint-anchor]');
108
+ if(!holder||!holder.getAttribute) return '';
109
+ return holder.getAttribute('data-checkpoint-anchor')||'';
110
+ }
111
+
112
+ function getSourceAnchorFromElement(el){
113
+ if(!el||el.nodeType!==1) return null;
114
+ var fiber=getReactFiberNode(el);
115
+ var ownerPath=getReactOwnerPathFromFiber(fiber);
116
+ var source=getSourceDebugInfo(fiber);
117
+ var component=getReactFiberName(fiber);
118
+ var explicitId=getExplicitAnchorId(el);
119
+ var reactKey=fiber&&fiber.key!=null?String(fiber.key):'';
120
+ var hasData=!!(explicitId||component||ownerPath.length>0||source||reactKey);
121
+ if(!hasData) return null;
122
+ return {
123
+ explicit_id:explicitId||undefined,
124
+ component_name:component||undefined,
125
+ owner_path:ownerPath,
126
+ source_file:source&&source.fileName?String(source.fileName):undefined,
127
+ source_line:source&&typeof source.lineNumber==='number'?source.lineNumber:undefined,
128
+ source_column:source&&typeof source.columnNumber==='number'?source.columnNumber:undefined,
129
+ react_key:reactKey||undefined,
130
+ host_tag:el&&el.tagName?String(el.tagName).toLowerCase():undefined
131
+ };
132
+ }
133
+
134
+ function getSourceAnchorStrength(anchor){
135
+ if(!anchor||typeof anchor!=='object') return 0;
136
+ var score=0;
137
+ if(anchor.explicit_id) score+=2200;
138
+ if(anchor.source_file) score+=500;
139
+ if(typeof anchor.source_line==='number') score+=340;
140
+ if(anchor.react_key) score+=320;
141
+ if(anchor.component_name) score+=180;
142
+ if(Array.isArray(anchor.owner_path)&&anchor.owner_path.length>0){
143
+ score+=Math.min(260,anchor.owner_path.length*26);
144
+ }
145
+ return score;
146
+ }
147
+
148
+ function findBestAnchorableElement(el){
149
+ if(!el||el.nodeType!==1) return { element:el, sourceAnchor:null };
150
+ var current=el;
151
+ var bestEl=el;
152
+ var bestAnchor=null;
153
+ var bestScore=0;
154
+ var depth=0;
155
+ while(current&&current.nodeType===1&&depth<12){
156
+ var anchor=getSourceAnchorFromElement(current);
157
+ var score=getSourceAnchorStrength(anchor);
158
+ if(score>bestScore){
159
+ bestScore=score;
160
+ bestAnchor=anchor;
161
+ bestEl=current;
162
+ }
163
+ if(anchor&&anchor.explicit_id){
164
+ return { element:current, sourceAnchor:anchor };
165
+ }
166
+ current=current.parentElement;
167
+ depth++;
168
+ }
169
+ return { element:bestEl, sourceAnchor:bestAnchor };
170
+ }
171
+
172
+ function ownerSuffixScore(current,expected){
173
+ if(!Array.isArray(current)||!Array.isArray(expected)||current.length===0||expected.length===0) return 0;
174
+ var i=current.length-1;
175
+ var j=expected.length-1;
176
+ var score=0;
177
+ while(i>=0&&j>=0){
178
+ if(current[i]!==expected[j]) break;
179
+ score+=100;
180
+ i--;
181
+ j--;
182
+ }
183
+ return score;
184
+ }
185
+
186
+ function sourceMatchScore(el,sourceAnchor){
187
+ if(!el||!sourceAnchor) return 0;
188
+ var score=0;
189
+ var explicit=getExplicitAnchorId(el);
190
+ if(sourceAnchor.explicit_id){
191
+ if(explicit===sourceAnchor.explicit_id) score+=2400;
192
+ else if(explicit) score-=200;
193
+ }
194
+ var fiber=getReactFiberNode(el);
195
+ var component=getReactFiberName(fiber);
196
+ var source=getSourceDebugInfo(fiber);
197
+ var ownerPath=getReactOwnerPathFromFiber(fiber);
198
+ if(sourceAnchor.component_name&&component===sourceAnchor.component_name) score+=240;
199
+ if(sourceAnchor.react_key&&fiber&&fiber.key!=null&&String(fiber.key)===sourceAnchor.react_key) score+=360;
200
+ score+=ownerSuffixScore(ownerPath,sourceAnchor.owner_path||[]);
201
+ if(sourceAnchor.source_file&&source&&String(source.fileName||'')===String(sourceAnchor.source_file)){
202
+ score+=420;
203
+ if(typeof sourceAnchor.source_line==='number'&&source.lineNumber===sourceAnchor.source_line) score+=420;
204
+ if(typeof sourceAnchor.source_column==='number'&&source.columnNumber===sourceAnchor.source_column) score+=80;
205
+ }
206
+ if(sourceAnchor.host_tag&&el.tagName&&String(el.tagName).toLowerCase()===sourceAnchor.host_tag) score+=40;
207
+ return score;
208
+ }
209
+
210
+ function getElementAtPoint(x,y){
211
+ try{
212
+ if(typeof x==='number'&&typeof y==='number'&&document.elementFromPoint){
213
+ var node=document.elementFromPoint(x,y);
214
+ if(node&&node.nodeType===1) return node;
215
+ }
216
+ }catch(e){}
217
+ return null;
218
+ }
219
+
220
+ function buildCoordFallback(clientX,clientY,metrics,anchorEl){
221
+ var safeViewW=metrics.viewWidth||1;
222
+ var safeViewH=metrics.viewHeight||1;
223
+ var docW=metrics.docWidth||safeViewW;
224
+ var docH=metrics.docHeight||safeViewH;
225
+ var docX=clientX+metrics.scrollX;
226
+ var docY=clientY+metrics.scrollY;
227
+ var fallback={
228
+ x_percent:clampPercent((clientX/safeViewW)*100),
229
+ y_percent:clampPercent((clientY/safeViewH)*100),
230
+ scroll_x:metrics.scrollX,
231
+ scroll_y:metrics.scrollY,
232
+ viewport_width:safeViewW,
233
+ viewport_height:safeViewH,
234
+ doc_width:docW,
235
+ doc_height:docH,
236
+ doc_x_percent:clampPercent((docX/docW)*100),
237
+ doc_y_percent:clampPercent((docY/docH)*100)
238
+ };
239
+ try{
240
+ var rect=anchorEl&&anchorEl.getBoundingClientRect?anchorEl.getBoundingClientRect():null;
241
+ if(rect){
242
+ var safeW=Math.max(1,rect.width||1);
243
+ var safeH=Math.max(1,rect.height||1);
244
+ fallback.element_x_percent=clampPercent(((clientX-rect.left)/safeW)*100);
245
+ fallback.element_y_percent=clampPercent(((clientY-rect.top)/safeH)*100);
246
+ }
247
+ }catch(e){}
248
+ return fallback;
249
+ }
250
+
251
+ function buildAnchorPayload(el,clientX,clientY){
252
+ var metrics=getMetrics();
253
+ var sourceTarget=findBestAnchorableElement(el);
254
+ var anchorEl=sourceTarget.element||el;
255
+ return {
256
+ selector_chain:[],
257
+ dom_fingerprint:null,
258
+ text_context:null,
259
+ container_hint:null,
260
+ transient_context:null,
261
+ source_anchor:sourceTarget.sourceAnchor||getSourceAnchorFromElement(anchorEl),
262
+ coord_fallback:buildCoordFallback(clientX,clientY,metrics,anchorEl)
263
+ };
264
+ }
265
+
266
+ function findBySource(sourceAnchor,scope){
267
+ if(!sourceAnchor||typeof sourceAnchor!=='object') return null;
268
+ var searchScope=scope||document;
269
+ if(sourceAnchor.explicit_id){
270
+ var explicit=searchScope.querySelector('[data-checkpoint-anchor="'+cssEscape(sourceAnchor.explicit_id)+'"]');
271
+ if(explicit) return explicit;
272
+ }
273
+ var tag=sourceAnchor.host_tag||'*';
274
+ var nodes=[];
275
+ try{
276
+ nodes=Array.prototype.slice.call(searchScope.querySelectorAll(tag));
277
+ }catch(e){ nodes=[]; }
278
+ if(nodes.length>1800) nodes=nodes.slice(0,1800);
279
+ var best=null;
280
+ var bestScore=0;
281
+ for(var i=0;i<nodes.length;i++){
282
+ var node=nodes[i];
283
+ var score=sourceMatchScore(node,sourceAnchor);
284
+ if(score>bestScore){
285
+ best=node;
286
+ bestScore=score;
287
+ }
288
+ }
289
+ return bestScore>=240?best:null;
290
+ }
291
+
292
+ function resolveAnchor(payload){
293
+ if(!payload||typeof payload!=='object'){
294
+ return { coordFallback:null, strategy:'none', matchedSelector:null, status:'none' };
295
+ }
296
+ if(payload.source_anchor){
297
+ var match=findBySource(payload.source_anchor,document);
298
+ if(match&&match.getBoundingClientRect){
299
+ var rect=match.getBoundingClientRect();
300
+ var clientX=rect.left+Math.max(1,Math.min(Math.max(2,rect.width)-1,rect.width/2));
301
+ var clientY=rect.top+Math.max(1,Math.min(Math.max(2,rect.height)-1,rect.height/2));
302
+ return {
303
+ coordFallback:buildCoordFallback(clientX,clientY,getMetrics(),match),
304
+ strategy:'source',
305
+ matchedSelector:'source_anchor',
306
+ status:'resolved'
307
+ };
308
+ }
309
+ }
310
+ if(payload.coord_fallback){
311
+ return {
312
+ coordFallback:payload.coord_fallback,
313
+ strategy:'fallback',
314
+ matchedSelector:'coord_fallback',
315
+ status:'fallback_only'
316
+ };
317
+ }
318
+ return { coordFallback:null, strategy:'none', matchedSelector:null, status:'none' };
319
+ }
320
+
321
+ function getInspectLabelText(target){
322
+ if(!target||!target.tagName) return '';
323
+ var tag=String(target.tagName).toLowerCase();
324
+ var id=target.id?('#'+target.id):'';
325
+ var cls='';
326
+ try{
327
+ var classes=(target.className&&typeof target.className==='string')
328
+ ? target.className.trim().split(/\s+/).filter(Boolean).slice(0,3)
329
+ : [];
330
+ if(classes.length>0) cls='.'+classes.join('.');
331
+ }catch(e){}
332
+ var selector=shortText(tag+id+cls);
333
+ var anchor=findBestAnchorableElement(target).sourceAnchor;
334
+ var source='(no source)';
335
+ if(anchor){
336
+ var file=anchor.source_file?String(anchor.source_file).split(/[\\/]/).pop():'';
337
+ if(file&&typeof anchor.source_line==='number') source=file+':'+anchor.source_line;
338
+ else if(file) source=file;
339
+ else if(anchor.component_name) source=anchor.component_name;
340
+ if(anchor.explicit_id) source=source+' · #'+anchor.explicit_id;
341
+ }
342
+ return selector+'\n'+source;
343
+ }
344
+
345
+ function ensureInspectUi(){
346
+ if(!document.body) return;
347
+ if(!inspectBox){
348
+ inspectBox=document.createElement('div');
349
+ inspectBox.style.position='fixed';
350
+ inspectBox.style.left='0';
351
+ inspectBox.style.top='0';
352
+ inspectBox.style.width='0';
353
+ inspectBox.style.height='0';
354
+ inspectBox.style.border='2px solid #6366f1';
355
+ inspectBox.style.background='rgba(99,102,241,0.12)';
356
+ inspectBox.style.boxSizing='border-box';
357
+ inspectBox.style.borderRadius='4px';
358
+ inspectBox.style.pointerEvents='none';
359
+ inspectBox.style.zIndex='2147483646';
360
+ inspectBox.style.display='none';
361
+ document.body.appendChild(inspectBox);
362
+ }
363
+ if(!inspectLabel){
364
+ inspectLabel=document.createElement('div');
365
+ inspectLabel.style.position='fixed';
366
+ inspectLabel.style.left='0';
367
+ inspectLabel.style.top='0';
368
+ inspectLabel.style.padding='6px 8px';
369
+ inspectLabel.style.maxWidth='420px';
370
+ inspectLabel.style.borderRadius='8px';
371
+ inspectLabel.style.border='1px solid rgba(148,163,184,0.45)';
372
+ inspectLabel.style.background='rgba(15,23,42,0.96)';
373
+ inspectLabel.style.color='#e2e8f0';
374
+ inspectLabel.style.font='12px/1.35 ui-monospace, SFMono-Regular, Menlo, monospace';
375
+ inspectLabel.style.whiteSpace='pre-line';
376
+ inspectLabel.style.wordBreak='break-word';
377
+ inspectLabel.style.pointerEvents='none';
378
+ inspectLabel.style.zIndex='2147483647';
379
+ inspectLabel.style.display='none';
380
+ inspectLabel.style.boxShadow='0 6px 20px rgba(0,0,0,0.35)';
381
+ document.body.appendChild(inspectLabel);
382
+ }
383
+ }
384
+
385
+ function hideInspectUi(){
386
+ if(inspectBox) inspectBox.style.display='none';
387
+ if(inspectLabel) inspectLabel.style.display='none';
388
+ }
389
+
390
+ function renderInspectAt(x,y){
391
+ ensureInspectUi();
392
+ if(!inspectBox||!inspectLabel){
393
+ return;
394
+ }
395
+ var target=getElementAtPoint(x,y);
396
+ if(!target||!target.getBoundingClientRect){
397
+ hideInspectUi();
398
+ return;
399
+ }
400
+ var rect=target.getBoundingClientRect();
401
+ if(!rect||rect.width<1||rect.height<1){
402
+ hideInspectUi();
403
+ return;
404
+ }
405
+ inspectBox.style.display='block';
406
+ inspectBox.style.left=Math.max(0,rect.left)+'px';
407
+ inspectBox.style.top=Math.max(0,rect.top)+'px';
408
+ inspectBox.style.width=Math.max(1,rect.width)+'px';
409
+ inspectBox.style.height=Math.max(1,rect.height)+'px';
410
+ inspectLabel.textContent=getInspectLabelText(target);
411
+ inspectLabel.style.display='block';
412
+
413
+ var vw=window.innerWidth||document.documentElement.clientWidth||0;
414
+ var vh=window.innerHeight||document.documentElement.clientHeight||0;
415
+ var lr=inspectLabel.getBoundingClientRect();
416
+ var left=x+12;
417
+ var top=y+12;
418
+ if(left+lr.width>vw-8) left=Math.max(8,x-lr.width-12);
419
+ if(top+lr.height>vh-8) top=Math.max(8,y-lr.height-12);
420
+ inspectLabel.style.left=left+'px';
421
+ inspectLabel.style.top=top+'px';
422
+ }
423
+
424
+ function postPickResult(target,clientX,clientY){
425
+ var payload=buildAnchorPayload(target,clientX,clientY);
426
+ window.parent.postMessage({
427
+ type:'checkpoint:pickResult',
428
+ path:location.pathname+location.search+location.hash,
429
+ anchorPayload:payload,
430
+ point:{
431
+ xPercent:payload.coord_fallback&&typeof payload.coord_fallback.x_percent==='number'?payload.coord_fallback.x_percent:50,
432
+ yPercent:payload.coord_fallback&&typeof payload.coord_fallback.y_percent==='number'?payload.coord_fallback.y_percent:50,
433
+ scrollY:payload.coord_fallback&&typeof payload.coord_fallback.scroll_y==='number'?payload.coord_fallback.scroll_y:0,
434
+ viewHeight:payload.coord_fallback&&typeof payload.coord_fallback.viewport_height==='number'?payload.coord_fallback.viewport_height:0
435
+ }
436
+ },'*');
437
+ }
438
+
439
+ function setPickMode(enabled){
440
+ var next=!!enabled;
441
+ if(pickMode===next) return;
442
+ pickMode=next;
443
+ document.documentElement.style.cursor=pickMode?'crosshair':'';
444
+ if(document.body) document.body.style.cursor=pickMode?'crosshair':'';
445
+
446
+ if(!pickMode){
447
+ if(pickMoveHandler) document.removeEventListener('mousemove',pickMoveHandler,true);
448
+ if(pickPointerDownHandler) window.removeEventListener('pointerdown',pickPointerDownHandler,true);
449
+ if(pickClickHandler) window.removeEventListener('click',pickClickHandler,true);
450
+ if(pickEscHandler) window.removeEventListener('keydown',pickEscHandler,true);
451
+ pickMoveHandler=null;
452
+ pickPointerDownHandler=null;
453
+ pickClickHandler=null;
454
+ pickEscHandler=null;
455
+ if(hoverRaf){
456
+ cancelAnimationFrame(hoverRaf);
457
+ hoverRaf=0;
458
+ }
459
+ hideInspectUi();
460
+ return;
461
+ }
462
+
463
+ pickMoveHandler=function(ev){
464
+ if(!pickMode) return;
465
+ lastMouseX=ev.clientX;
466
+ lastMouseY=ev.clientY;
467
+ if(hoverRaf) return;
468
+ hoverRaf=requestAnimationFrame(function(){
469
+ hoverRaf=0;
470
+ renderInspectAt(lastMouseX,lastMouseY);
471
+ });
472
+ };
473
+ pickPointerDownHandler=function(ev){
474
+ if(!pickMode) return;
475
+ var target=getElementAtPoint(ev.clientX,ev.clientY);
476
+ if(!target) return;
477
+ ev.preventDefault();
478
+ ev.stopPropagation();
479
+ if(typeof ev.stopImmediatePropagation==='function') ev.stopImmediatePropagation();
480
+ try{
481
+ postPickResult(target,ev.clientX,ev.clientY);
482
+ }catch(e){}
483
+ setPickMode(false);
484
+ };
485
+ pickClickHandler=function(ev){
486
+ if(!pickMode) return;
487
+ ev.preventDefault();
488
+ ev.stopPropagation();
489
+ if(typeof ev.stopImmediatePropagation==='function') ev.stopImmediatePropagation();
490
+ };
491
+ pickEscHandler=function(ev){
492
+ if(ev.key==='Escape') setPickMode(false);
493
+ };
494
+
495
+ document.addEventListener('mousemove',pickMoveHandler,true);
496
+ window.addEventListener('pointerdown',pickPointerDownHandler,true);
497
+ window.addEventListener('click',pickClickHandler,true);
498
+ window.addEventListener('keydown',pickEscHandler,true);
499
+ }
500
+
501
+ function reportScroll(){
502
+ try{
503
+ var m=getMetrics();
504
+ window.parent.postMessage({
505
+ type:'checkpoint:scroll',
506
+ scrollX:m.scrollX,
507
+ scrollY:m.scrollY,
508
+ docWidth:m.docWidth,
509
+ docHeight:m.docHeight,
510
+ viewWidth:m.viewWidth,
511
+ viewHeight:m.viewHeight
512
+ },'*');
513
+ }catch(e){}
514
+ }
515
+
516
+ function reportNav(){
517
+ try{
518
+ var p=location.pathname+location.search+location.hash;
519
+ if(p===lastPath) return;
520
+ lastPath=p;
521
+ window.parent.postMessage({ type:'checkpoint:navigate', path:p },'*');
522
+ reportScroll();
523
+ }catch(e){}
524
+ }
525
+
526
+ function reportDomMutation(){
527
+ if(mutationTick) return;
528
+ mutationTick=true;
529
+ requestAnimationFrame(function(){
530
+ mutationTick=false;
531
+ window.parent.postMessage({
532
+ type:'checkpoint:domMutation',
533
+ reason:'mutation',
534
+ path:location.pathname+location.search+location.hash
535
+ },'*');
536
+ });
537
+ }
538
+
539
+ function setupMutationObserver(){
540
+ try{
541
+ if(!window.MutationObserver||!document.documentElement) return;
542
+ var observer=new MutationObserver(function(){ reportDomMutation(); });
543
+ observer.observe(document.documentElement,{ childList:true, subtree:true, attributes:true });
544
+ window.addEventListener('beforeunload',function(){ try{observer.disconnect();}catch(e){}; });
545
+ }catch(e){}
546
+ }
547
+
548
+ reportNav();
549
+ reportScroll();
550
+ setupMutationObserver();
551
+ var _ps=history.pushState,_rs=history.replaceState;
552
+ history.pushState=function(){ _ps.apply(this,arguments); reportNav(); };
553
+ history.replaceState=function(){ _rs.apply(this,arguments); reportNav(); };
554
+ window.addEventListener('popstate',reportNav);
555
+ window.addEventListener('hashchange',reportNav);
556
+ window.addEventListener('scroll',function(){ requestAnimationFrame(reportScroll); },{passive:true});
557
+ document.addEventListener('scroll',function(){ requestAnimationFrame(reportScroll); },true);
558
+ window.addEventListener('resize',reportScroll);
559
+
560
+ window.addEventListener('message',function(e){
561
+ try{
562
+ var d=e.data;
563
+ if(!d||typeof d!=='object') return;
564
+ if(d.type==='checkpoint:scrollTo'&&typeof d.y==='number'){
565
+ window.scrollTo({ top:d.y, behavior:'smooth' });
566
+ return;
567
+ }
568
+ if(d.type==='checkpoint:pickMode'){
569
+ setPickMode(!!d.enabled);
570
+ return;
571
+ }
572
+ if(d.type==='checkpoint:resolveAnchors'&&Array.isArray(d.items)){
573
+ var positions=[];
574
+ for(var i=0;i<d.items.length;i++){
575
+ var item=d.items[i];
576
+ if(!item||typeof item.id!=='string') continue;
577
+ var result=resolveAnchor(item.anchorPayload);
578
+ positions.push({
579
+ id:item.id,
580
+ found:!!result.coordFallback,
581
+ status:result.status||'none',
582
+ strategy:result.strategy||'none',
583
+ matchedSelector:result.matchedSelector||null,
584
+ coordFallback:result.coordFallback||null
585
+ });
586
+ }
587
+ window.parent.postMessage({
588
+ type:'checkpoint:anchorsResolved',
589
+ path:location.pathname+location.search+location.hash,
590
+ positions:positions
591
+ },'*');
592
+ }
593
+ }catch(ex){}
594
+ });
595
+
596
+ setInterval(reportNav,500);
597
+ })();
598
+ </script>`.trim();
599
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "checkpoint-cli",
3
- "version": "0.2.2",
3
+ "version": "0.3.0",
4
4
  "description": "Share your localhost with reviewers — get visual feedback directly on the page",
5
5
  "keywords": [
6
6
  "checkpoint",