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=
|
|
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
|
-
|
|
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
|
-
|
|
1033
|
-
|
|
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
|
-
|
|
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 =
|
|
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=
|
|
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.
|
|
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&¤t.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
|
+
}
|