@merittdev/horus-lens 0.4.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser.cjs +449 -190
- package/dist/browser.cjs.map +1 -1
- package/dist/browser.d.cts +1 -1
- package/dist/browser.d.ts +1 -1
- package/dist/browser.js +2 -2
- package/dist/{chunk-B7QI3T3E.js → chunk-GVULQ2IF.js} +85 -21
- package/dist/{chunk-B7QI3T3E.js.map → chunk-GVULQ2IF.js.map} +1 -1
- package/dist/{chunk-GQREYSHC.js → chunk-MN44USB4.js} +367 -172
- package/dist/chunk-MN44USB4.js.map +1 -0
- package/dist/cli.cjs +25 -6
- package/dist/cli.cjs.map +1 -1
- package/dist/index.cjs +84 -20
- package/dist/index.cjs.map +1 -1
- package/dist/{index.d-Dyzc1E9T.d.ts → index.d-BYuZWEkh.d.ts} +7 -0
- package/dist/{index.d-BYG_Mjs-.d.cts → index.d-DeJ1mfh0.d.cts} +7 -0
- package/dist/index.d.cts +7 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +1 -1
- package/dist/lens.min.js +204 -130
- package/dist/react.cjs +449 -190
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +1 -1
- package/dist/react.d.ts +1 -1
- package/dist/react.js +2 -2
- package/package.json +3 -2
- package/dist/chunk-GQREYSHC.js.map +0 -1
- package/dist/lens.min.js.map +0 -1
package/dist/react.cjs
CHANGED
|
@@ -29083,7 +29083,7 @@ var ApiErrorSchema = external_exports.object({
|
|
|
29083
29083
|
|
|
29084
29084
|
// ../core/dist/index.js
|
|
29085
29085
|
var SDK_NAME = "@merittdev/horus-lens";
|
|
29086
|
-
var SDK_VERSION = true ? "0.
|
|
29086
|
+
var SDK_VERSION = true ? "0.5.1" : "0.0.0-dev";
|
|
29087
29087
|
var DEFAULT_ENDPOINT = "https://api.horus.sh";
|
|
29088
29088
|
var DEFAULT_REPLAY_WINDOW_MS = 15e3;
|
|
29089
29089
|
var DEFAULT_MAX_CONSOLE_ENTRIES = 200;
|
|
@@ -29144,11 +29144,15 @@ function startAutoCapture(client, options = {}) {
|
|
|
29144
29144
|
const last = lastSeen.get(signature);
|
|
29145
29145
|
if (last !== void 0 && now - last < AUTO_CAPTURE_COOLDOWN_MS) return;
|
|
29146
29146
|
if (sessionCount >= AUTO_CAPTURE_SESSION_CAP) return;
|
|
29147
|
+
const previousSeen = lastSeen.get(signature);
|
|
29147
29148
|
lastSeen.set(signature, now);
|
|
29148
29149
|
sessionCount += 1;
|
|
29149
29150
|
submitting = true;
|
|
29150
29151
|
options.onCapture?.(signature);
|
|
29151
29152
|
void Promise.resolve().then(() => client.submitReport({ trigger: "error", include: { screenshot: false } })).catch(() => {
|
|
29153
|
+
if (previousSeen === void 0) lastSeen.delete(signature);
|
|
29154
|
+
else lastSeen.set(signature, previousSeen);
|
|
29155
|
+
sessionCount -= 1;
|
|
29152
29156
|
}).finally(() => {
|
|
29153
29157
|
submitting = false;
|
|
29154
29158
|
});
|
|
@@ -29203,6 +29207,14 @@ var RingBuffer = class {
|
|
|
29203
29207
|
return this.items.length;
|
|
29204
29208
|
}
|
|
29205
29209
|
};
|
|
29210
|
+
var HttpError = class extends Error {
|
|
29211
|
+
constructor(status, message) {
|
|
29212
|
+
super(message);
|
|
29213
|
+
this.status = status;
|
|
29214
|
+
this.name = "HttpError";
|
|
29215
|
+
}
|
|
29216
|
+
status;
|
|
29217
|
+
};
|
|
29206
29218
|
var Transport = class {
|
|
29207
29219
|
constructor(config2) {
|
|
29208
29220
|
this.config = config2;
|
|
@@ -29210,18 +29222,23 @@ var Transport = class {
|
|
|
29210
29222
|
config;
|
|
29211
29223
|
async initiate(assets) {
|
|
29212
29224
|
const body = { accessId: this.config.accessId, assets };
|
|
29213
|
-
return this.postJson(
|
|
29225
|
+
return this.postJson(
|
|
29226
|
+
`${this.config.endpoint}/v1/reports/initiate`,
|
|
29227
|
+
body,
|
|
29228
|
+
{ retries: 0 }
|
|
29229
|
+
);
|
|
29214
29230
|
}
|
|
29215
29231
|
/** Record a late-uploaded asset (background screenshot) on a finalized report. */
|
|
29216
29232
|
async attachAsset(reportId, asset) {
|
|
29217
29233
|
const body = { accessId: this.config.accessId, ...asset };
|
|
29218
29234
|
await this.postJson(`${this.config.endpoint}/v1/reports/${encodeURIComponent(reportId)}/assets`, body);
|
|
29219
29235
|
}
|
|
29220
|
-
async uploadAsset(upload, blob2) {
|
|
29236
|
+
async uploadAsset(upload, blob2, signal) {
|
|
29221
29237
|
const response = await fetch(upload.url, {
|
|
29222
29238
|
method: upload.method,
|
|
29223
29239
|
headers: upload.headers,
|
|
29224
|
-
body: blob2
|
|
29240
|
+
body: blob2,
|
|
29241
|
+
signal
|
|
29225
29242
|
});
|
|
29226
29243
|
if (!response.ok) {
|
|
29227
29244
|
throw new Error(`Asset upload failed (${upload.kind}): ${response.status}`);
|
|
@@ -29229,12 +29246,20 @@ var Transport = class {
|
|
|
29229
29246
|
}
|
|
29230
29247
|
async finalize(reportId, metadata, uploadedAssets) {
|
|
29231
29248
|
const body = { accessId: this.config.accessId, metadata, uploadedAssets };
|
|
29232
|
-
|
|
29233
|
-
|
|
29234
|
-
|
|
29235
|
-
|
|
29249
|
+
try {
|
|
29250
|
+
return await this.postJson(
|
|
29251
|
+
`${this.config.endpoint}/v1/reports/${reportId}/finalize`,
|
|
29252
|
+
body
|
|
29253
|
+
);
|
|
29254
|
+
} catch (err) {
|
|
29255
|
+
if (err instanceof HttpError && err.status === 409) {
|
|
29256
|
+
return { reportId, status: "submitted" };
|
|
29257
|
+
}
|
|
29258
|
+
throw err;
|
|
29259
|
+
}
|
|
29236
29260
|
}
|
|
29237
|
-
async postJson(url2, body,
|
|
29261
|
+
async postJson(url2, body, opts = {}) {
|
|
29262
|
+
const retries = opts.retries ?? 1;
|
|
29238
29263
|
let lastError;
|
|
29239
29264
|
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
29240
29265
|
try {
|
|
@@ -29245,7 +29270,10 @@ var Transport = class {
|
|
|
29245
29270
|
});
|
|
29246
29271
|
if (!response.ok) {
|
|
29247
29272
|
const text = await response.text().catch(() => "");
|
|
29248
|
-
throw new
|
|
29273
|
+
throw new HttpError(
|
|
29274
|
+
response.status,
|
|
29275
|
+
`Request to ${url2} failed: ${response.status} ${text}`.trim()
|
|
29276
|
+
);
|
|
29249
29277
|
}
|
|
29250
29278
|
return await response.json();
|
|
29251
29279
|
} catch (err) {
|
|
@@ -29636,8 +29664,11 @@ function computeCropRect(crop, scale, rasterWidth, rasterHeight) {
|
|
|
29636
29664
|
async function captureScreenshot(excludeHost, crop, composite) {
|
|
29637
29665
|
if (typeof document === "undefined" || typeof window === "undefined") return null;
|
|
29638
29666
|
try {
|
|
29639
|
-
const
|
|
29640
|
-
const
|
|
29667
|
+
const viewportOnly = !composite && !crop;
|
|
29668
|
+
const captureW = composite ? composite.docWidth : window.innerWidth;
|
|
29669
|
+
const captureH = composite ? composite.docHeight : window.innerHeight;
|
|
29670
|
+
const capturePixels = captureW * captureH || 1;
|
|
29671
|
+
const pixelCap = Math.sqrt(SCREENSHOT_MAX_PIXELS / capturePixels);
|
|
29641
29672
|
const scale = Math.max(1, Math.min(window.devicePixelRatio || 1, SCREENSHOT_MAX_SCALE, pixelCap));
|
|
29642
29673
|
const capture = domToBlob(document.documentElement, {
|
|
29643
29674
|
scale,
|
|
@@ -29646,12 +29677,19 @@ async function captureScreenshot(excludeHost, crop, composite) {
|
|
|
29646
29677
|
// JPEG has no alpha — transparent page regions (and any capture box
|
|
29647
29678
|
// extending past short content) would otherwise encode as BLACK.
|
|
29648
29679
|
backgroundColor: "#ffffff",
|
|
29649
|
-
//
|
|
29650
|
-
//
|
|
29651
|
-
// sizes from documentElement's bounding box, which on pages SHORTER than
|
|
29652
|
-
// the viewport is the content height only — marks drawn in the lower
|
|
29653
|
-
// viewport band would fall off the raster and the crop would clamp short.
|
|
29680
|
+
// Composite (annotations): force the capture box to the editor's document
|
|
29681
|
+
// measurement so marks in the lower viewport band aren't clipped.
|
|
29654
29682
|
...composite ? { width: composite.docWidth, height: composite.docHeight } : {},
|
|
29683
|
+
// Plain report: clip the render to the viewport and shift the scrolled
|
|
29684
|
+
// content into it, so only the visible region is rasterized/fetched.
|
|
29685
|
+
...viewportOnly ? {
|
|
29686
|
+
width: window.innerWidth,
|
|
29687
|
+
height: window.innerHeight,
|
|
29688
|
+
style: {
|
|
29689
|
+
transform: `translate(${-window.scrollX}px, ${-window.scrollY}px)`,
|
|
29690
|
+
transformOrigin: "top left"
|
|
29691
|
+
}
|
|
29692
|
+
} : {},
|
|
29655
29693
|
// Exclude the overlay from the capture CLONE — never hide the live node.
|
|
29656
29694
|
filter: (node2) => node2 !== excludeHost,
|
|
29657
29695
|
// Per-resource fetch budget: a single slow cross-origin image must not
|
|
@@ -30121,12 +30159,22 @@ var LensClient = class {
|
|
|
30121
30159
|
input2.screenshotCrop,
|
|
30122
30160
|
input2.annotationComposite
|
|
30123
30161
|
) : Promise.resolve(null);
|
|
30162
|
+
const consumingManualRecording = wantReplay && (this.replay?.hasManualRecording() ?? false) && !(this.replay?.isManualRecording() ?? false);
|
|
30124
30163
|
const replayEvents = wantReplay ? this.replay?.snapshot() ?? [] : [];
|
|
30125
30164
|
const errorEntries = this.errorBuffer.drain();
|
|
30126
|
-
const consoleTail = this.consoleBuffer.tail(CONSOLE_TAIL_SIZE);
|
|
30127
|
-
const networkTail = this.networkBuffer.tail(NETWORK_TAIL_SIZE);
|
|
30128
30165
|
const consoleEntries = this.consoleBuffer.drain();
|
|
30129
30166
|
const networkEntries = this.networkBuffer.drain();
|
|
30167
|
+
this.errorBuffer.clear();
|
|
30168
|
+
this.consoleBuffer.clear();
|
|
30169
|
+
this.networkBuffer.clear();
|
|
30170
|
+
const consoleTail = consoleEntries.slice(-CONSOLE_TAIL_SIZE);
|
|
30171
|
+
const isNetFailure = (e) => e.ok !== true || Boolean(e.error);
|
|
30172
|
+
const recentFailures = networkEntries.filter(isNetFailure).slice(-NETWORK_TAIL_SIZE);
|
|
30173
|
+
const keep = /* @__PURE__ */ new Set([
|
|
30174
|
+
...recentFailures,
|
|
30175
|
+
...networkEntries.slice(-NETWORK_TAIL_SIZE)
|
|
30176
|
+
]);
|
|
30177
|
+
const networkTail = networkEntries.filter((e) => keep.has(e)).slice(-NETWORK_TAIL_SIZE);
|
|
30130
30178
|
let metadata = {
|
|
30131
30179
|
annotations: input2.annotations ?? [],
|
|
30132
30180
|
env: collectEnv(),
|
|
@@ -30180,13 +30228,14 @@ var LensClient = class {
|
|
|
30180
30228
|
}
|
|
30181
30229
|
if (!asset.blob) continue;
|
|
30182
30230
|
try {
|
|
30183
|
-
await this.
|
|
30231
|
+
await this.uploadAssetWithTimeout(upload, asset.blob);
|
|
30184
30232
|
uploadedAssets.push({ kind: upload.kind, key: upload.key, byteSize: asset.blob.size });
|
|
30185
30233
|
} catch (err) {
|
|
30186
30234
|
this.warn(`asset upload failed (${upload.kind})`, err);
|
|
30187
30235
|
}
|
|
30188
30236
|
}
|
|
30189
30237
|
const finalized = await this.transport.finalize(initiated.reportId, metadata, uploadedAssets);
|
|
30238
|
+
if (consumingManualRecording) this.replay?.discardManualRecording();
|
|
30190
30239
|
if (screenshotUpload) {
|
|
30191
30240
|
const upload = screenshotUpload;
|
|
30192
30241
|
void screenshotPromise.then(async (blob2) => {
|
|
@@ -30204,6 +30253,21 @@ var LensClient = class {
|
|
|
30204
30253
|
}
|
|
30205
30254
|
return { reportId: finalized.reportId };
|
|
30206
30255
|
}
|
|
30256
|
+
/**
|
|
30257
|
+
* Upload a foreground asset with a bounded timeout. Without this a stuck S3
|
|
30258
|
+
* PUT (dropped connection, half-open socket) would hang `submitReport`
|
|
30259
|
+
* forever; the timeout aborts the individual PUT so it fails as one asset
|
|
30260
|
+
* (caught by the caller) instead of wedging the whole submit.
|
|
30261
|
+
*/
|
|
30262
|
+
async uploadAssetWithTimeout(upload, blob2) {
|
|
30263
|
+
const controller = new AbortController();
|
|
30264
|
+
const timer = setTimeout(() => controller.abort(), SCREENSHOT_TIMEOUT_MS);
|
|
30265
|
+
try {
|
|
30266
|
+
await this.transport.uploadAsset(upload, blob2, controller.signal);
|
|
30267
|
+
} finally {
|
|
30268
|
+
clearTimeout(timer);
|
|
30269
|
+
}
|
|
30270
|
+
}
|
|
30207
30271
|
warn(message, err) {
|
|
30208
30272
|
if (!this.debug) return;
|
|
30209
30273
|
if (err !== void 0) console.warn(`[lens] ${message}`, err);
|
|
@@ -30332,6 +30396,113 @@ function el(tag, props = {}, children = []) {
|
|
|
30332
30396
|
function clear(node2) {
|
|
30333
30397
|
while (node2.firstChild) node2.removeChild(node2.firstChild);
|
|
30334
30398
|
}
|
|
30399
|
+
var MARGIN = 6;
|
|
30400
|
+
var DRAG_THRESHOLD = 3;
|
|
30401
|
+
var savedPositions = /* @__PURE__ */ new Map();
|
|
30402
|
+
function isInteractiveTarget(target) {
|
|
30403
|
+
return target instanceof Element && target.closest(
|
|
30404
|
+
'button, a, input, textarea, select, [role="switch"], [role="button"], [contenteditable="true"]'
|
|
30405
|
+
) !== null;
|
|
30406
|
+
}
|
|
30407
|
+
function clampToViewport(element, left, top) {
|
|
30408
|
+
const rect = element.getBoundingClientRect();
|
|
30409
|
+
const vw = window.innerWidth;
|
|
30410
|
+
const vh = window.innerHeight;
|
|
30411
|
+
const maxLeft = Math.max(MARGIN, vw - rect.width - MARGIN);
|
|
30412
|
+
const maxTop = Math.max(MARGIN, vh - rect.height - MARGIN);
|
|
30413
|
+
return {
|
|
30414
|
+
left: Math.min(Math.max(left, MARGIN), maxLeft),
|
|
30415
|
+
top: Math.min(Math.max(top, MARGIN), maxTop)
|
|
30416
|
+
};
|
|
30417
|
+
}
|
|
30418
|
+
function makeDraggable(element, opts) {
|
|
30419
|
+
const { handle, key } = opts;
|
|
30420
|
+
const ignore = opts.ignore ?? isInteractiveTarget;
|
|
30421
|
+
let startX = 0;
|
|
30422
|
+
let startY = 0;
|
|
30423
|
+
let originLeft = 0;
|
|
30424
|
+
let originTop = 0;
|
|
30425
|
+
let dragging = false;
|
|
30426
|
+
let armed = false;
|
|
30427
|
+
let pointerId = null;
|
|
30428
|
+
const apply = (left, top) => {
|
|
30429
|
+
const p = clampToViewport(element, left, top);
|
|
30430
|
+
element.style.left = `${p.left}px`;
|
|
30431
|
+
element.style.top = `${p.top}px`;
|
|
30432
|
+
element.style.right = "auto";
|
|
30433
|
+
element.style.bottom = "auto";
|
|
30434
|
+
element.style.transform = "none";
|
|
30435
|
+
savedPositions.set(key, p);
|
|
30436
|
+
opts.onMove?.();
|
|
30437
|
+
};
|
|
30438
|
+
const onPointerDown = (e) => {
|
|
30439
|
+
if (e.button !== 0 && e.pointerType === "mouse") return;
|
|
30440
|
+
if (ignore(e.target)) return;
|
|
30441
|
+
armed = true;
|
|
30442
|
+
dragging = false;
|
|
30443
|
+
pointerId = e.pointerId;
|
|
30444
|
+
startX = e.clientX;
|
|
30445
|
+
startY = e.clientY;
|
|
30446
|
+
const rect = element.getBoundingClientRect();
|
|
30447
|
+
originLeft = rect.left;
|
|
30448
|
+
originTop = rect.top;
|
|
30449
|
+
window.addEventListener("pointermove", onPointerMove, true);
|
|
30450
|
+
window.addEventListener("pointerup", onPointerUp, true);
|
|
30451
|
+
window.addEventListener("pointercancel", onPointerUp, true);
|
|
30452
|
+
};
|
|
30453
|
+
const onPointerMove = (e) => {
|
|
30454
|
+
if (!armed || e.pointerId !== pointerId) return;
|
|
30455
|
+
const dx = e.clientX - startX;
|
|
30456
|
+
const dy = e.clientY - startY;
|
|
30457
|
+
if (!dragging) {
|
|
30458
|
+
if (Math.abs(dx) + Math.abs(dy) < DRAG_THRESHOLD) return;
|
|
30459
|
+
dragging = true;
|
|
30460
|
+
element.classList.add("is-dragging");
|
|
30461
|
+
}
|
|
30462
|
+
e.preventDefault();
|
|
30463
|
+
apply(originLeft + dx, originTop + dy);
|
|
30464
|
+
};
|
|
30465
|
+
const onPointerUp = (e) => {
|
|
30466
|
+
if (e.pointerId !== pointerId) return;
|
|
30467
|
+
armed = false;
|
|
30468
|
+
if (dragging) {
|
|
30469
|
+
dragging = false;
|
|
30470
|
+
element.classList.remove("is-dragging");
|
|
30471
|
+
}
|
|
30472
|
+
pointerId = null;
|
|
30473
|
+
window.removeEventListener("pointermove", onPointerMove, true);
|
|
30474
|
+
window.removeEventListener("pointerup", onPointerUp, true);
|
|
30475
|
+
window.removeEventListener("pointercancel", onPointerUp, true);
|
|
30476
|
+
};
|
|
30477
|
+
const onResize = () => {
|
|
30478
|
+
const p = savedPositions.get(key);
|
|
30479
|
+
if (p) apply(p.left, p.top);
|
|
30480
|
+
};
|
|
30481
|
+
handle.classList.add("lens-drag-handle");
|
|
30482
|
+
handle.addEventListener("pointerdown", onPointerDown);
|
|
30483
|
+
window.addEventListener("resize", onResize);
|
|
30484
|
+
const stored = savedPositions.get(key);
|
|
30485
|
+
if (stored) requestAnimationFrame(() => apply(stored.left, stored.top));
|
|
30486
|
+
return {
|
|
30487
|
+
reclamp: onResize,
|
|
30488
|
+
reset() {
|
|
30489
|
+
savedPositions.delete(key);
|
|
30490
|
+
element.style.left = "";
|
|
30491
|
+
element.style.top = "";
|
|
30492
|
+
element.style.right = "";
|
|
30493
|
+
element.style.bottom = "";
|
|
30494
|
+
element.style.transform = "";
|
|
30495
|
+
element.classList.remove("is-dragging");
|
|
30496
|
+
},
|
|
30497
|
+
destroy() {
|
|
30498
|
+
handle.removeEventListener("pointerdown", onPointerDown);
|
|
30499
|
+
window.removeEventListener("pointermove", onPointerMove, true);
|
|
30500
|
+
window.removeEventListener("pointerup", onPointerUp, true);
|
|
30501
|
+
window.removeEventListener("pointercancel", onPointerUp, true);
|
|
30502
|
+
window.removeEventListener("resize", onResize);
|
|
30503
|
+
}
|
|
30504
|
+
};
|
|
30505
|
+
}
|
|
30335
30506
|
var THIN_MIN_DIST = 3;
|
|
30336
30507
|
var MAX_POINTS = 2e3;
|
|
30337
30508
|
var HIGHLIGHTER_WIDTH = 14;
|
|
@@ -30482,6 +30653,8 @@ var iconTrash = svg(
|
|
|
30482
30653
|
'<polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/>'
|
|
30483
30654
|
);
|
|
30484
30655
|
var iconAnnotate = iconPencil;
|
|
30656
|
+
var iconGrip = `<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><circle cx="9" cy="6" r="1.6"/><circle cx="15" cy="6" r="1.6"/><circle cx="9" cy="12" r="1.6"/><circle cx="15" cy="12" r="1.6"/><circle cx="9" cy="18" r="1.6"/><circle cx="15" cy="18" r="1.6"/></svg>`;
|
|
30657
|
+
var iconReport = svg('<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2Z"/>');
|
|
30485
30658
|
var SVG_NS = "http://www.w3.org/2000/svg";
|
|
30486
30659
|
var FONT = "-apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif";
|
|
30487
30660
|
var MIN_DRAG = 6;
|
|
@@ -30510,6 +30683,7 @@ var AnnotationEditor = class {
|
|
|
30510
30683
|
svg = null;
|
|
30511
30684
|
bg = null;
|
|
30512
30685
|
toolbar = null;
|
|
30686
|
+
toolbarDrag = null;
|
|
30513
30687
|
textInputEl = null;
|
|
30514
30688
|
textAnchor = null;
|
|
30515
30689
|
textColor = COLORS[0];
|
|
@@ -30555,6 +30729,7 @@ var AnnotationEditor = class {
|
|
|
30555
30729
|
this.markNodes = [];
|
|
30556
30730
|
this.annotations = [];
|
|
30557
30731
|
this.stepCounter = 0;
|
|
30732
|
+
this.sessionBase = Math.min(this.sessionBase, this.annotations.length);
|
|
30558
30733
|
if (!this.active) this.teardownLayer();
|
|
30559
30734
|
this.syncActionButtons();
|
|
30560
30735
|
}
|
|
@@ -30569,6 +30744,7 @@ var AnnotationEditor = class {
|
|
|
30569
30744
|
this.sizeLayer();
|
|
30570
30745
|
this.updateTransform();
|
|
30571
30746
|
svg2.style.pointerEvents = "auto";
|
|
30747
|
+
if (this.bg) this.bg.style.pointerEvents = "all";
|
|
30572
30748
|
svg2.style.touchAction = "none";
|
|
30573
30749
|
svg2.style.cursor = "crosshair";
|
|
30574
30750
|
svg2.addEventListener("pointerdown", this.onPointerDown);
|
|
@@ -30589,6 +30765,7 @@ var AnnotationEditor = class {
|
|
|
30589
30765
|
if (svg2) {
|
|
30590
30766
|
svg2.removeEventListener("pointerdown", this.onPointerDown);
|
|
30591
30767
|
svg2.style.pointerEvents = "none";
|
|
30768
|
+
if (this.bg) this.bg.style.pointerEvents = "none";
|
|
30592
30769
|
svg2.style.touchAction = "";
|
|
30593
30770
|
svg2.style.cursor = "";
|
|
30594
30771
|
}
|
|
@@ -30598,15 +30775,8 @@ var AnnotationEditor = class {
|
|
|
30598
30775
|
this.onExit = null;
|
|
30599
30776
|
cb?.();
|
|
30600
30777
|
}
|
|
30601
|
-
/** Cancel — discard marks drawn during this session, then exit. */
|
|
30602
|
-
cancel() {
|
|
30603
|
-
this.cancelTextInput();
|
|
30604
|
-
const removed = this.markNodes.splice(this.sessionBase);
|
|
30605
|
-
for (const node2 of removed) node2.remove();
|
|
30606
|
-
this.annotations.splice(this.sessionBase);
|
|
30607
|
-
this.exit();
|
|
30608
|
-
}
|
|
30609
30778
|
undo() {
|
|
30779
|
+
if (this.markNodes.length <= this.sessionBase) return;
|
|
30610
30780
|
const node2 = this.markNodes.pop();
|
|
30611
30781
|
if (!node2) return;
|
|
30612
30782
|
node2.remove();
|
|
@@ -30822,39 +30992,45 @@ var AnnotationEditor = class {
|
|
|
30822
30992
|
on: { click: () => this.clear() }
|
|
30823
30993
|
});
|
|
30824
30994
|
const editGroup = el("div", { class: "lens-tool-group" }, [this.undoBtn, this.clearBtn]);
|
|
30825
|
-
const
|
|
30826
|
-
|
|
30827
|
-
|
|
30828
|
-
|
|
30829
|
-
|
|
30830
|
-
|
|
30831
|
-
}
|
|
30832
|
-
|
|
30833
|
-
|
|
30834
|
-
|
|
30835
|
-
|
|
30836
|
-
|
|
30837
|
-
|
|
30995
|
+
const closeBtn = el("button", {
|
|
30996
|
+
class: "lens-tool-close",
|
|
30997
|
+
ariaLabel: "Done",
|
|
30998
|
+
title: "Done",
|
|
30999
|
+
html: iconClose,
|
|
31000
|
+
attrs: { type: "button" },
|
|
31001
|
+
on: { click: () => this.exit() }
|
|
31002
|
+
});
|
|
31003
|
+
const grip = el("span", {
|
|
31004
|
+
class: "lens-tool-grip",
|
|
31005
|
+
html: iconGrip,
|
|
31006
|
+
title: "Drag to move toolbar",
|
|
31007
|
+
ariaLabel: "Drag to move toolbar"
|
|
31008
|
+
});
|
|
31009
|
+
const track = el("div", { class: "lens-tool-track" }, [
|
|
31010
|
+
toolGroup,
|
|
31011
|
+
el("span", { class: "lens-tool-sep" }),
|
|
31012
|
+
colorGroup,
|
|
31013
|
+
el("span", { class: "lens-tool-sep" }),
|
|
31014
|
+
widthGroup,
|
|
31015
|
+
el("span", { class: "lens-tool-sep" }),
|
|
31016
|
+
editGroup
|
|
30838
31017
|
]);
|
|
30839
31018
|
this.toolbar = el(
|
|
30840
31019
|
"div",
|
|
30841
31020
|
{ class: "lens-annotate-toolbar", attrs: { role: "toolbar", "aria-label": "Annotation tools" } },
|
|
30842
|
-
[
|
|
30843
|
-
toolGroup,
|
|
30844
|
-
el("span", { class: "lens-tool-sep" }),
|
|
30845
|
-
colorGroup,
|
|
30846
|
-
el("span", { class: "lens-tool-sep" }),
|
|
30847
|
-
widthGroup,
|
|
30848
|
-
el("span", { class: "lens-tool-sep" }),
|
|
30849
|
-
editGroup,
|
|
30850
|
-
el("span", { class: "lens-tool-sep" }),
|
|
30851
|
-
doneGroup
|
|
30852
|
-
]
|
|
31021
|
+
[grip, track, closeBtn]
|
|
30853
31022
|
);
|
|
30854
31023
|
this.host.append(this.toolbar);
|
|
31024
|
+
this.toolbarDrag = makeDraggable(this.toolbar, {
|
|
31025
|
+
handle: grip,
|
|
31026
|
+
key: "lens-annotate-toolbar",
|
|
31027
|
+
ignore: isInteractiveTarget
|
|
31028
|
+
});
|
|
30855
31029
|
this.syncActionButtons();
|
|
30856
31030
|
}
|
|
30857
31031
|
removeToolbar() {
|
|
31032
|
+
this.toolbarDrag?.destroy();
|
|
31033
|
+
this.toolbarDrag = null;
|
|
30858
31034
|
this.toolbar?.remove();
|
|
30859
31035
|
this.toolbar = null;
|
|
30860
31036
|
this.toolBtns.clear();
|
|
@@ -30891,7 +31067,7 @@ var AnnotationEditor = class {
|
|
|
30891
31067
|
}
|
|
30892
31068
|
syncActionButtons() {
|
|
30893
31069
|
const empty = this.markNodes.length === 0;
|
|
30894
|
-
if (this.undoBtn) this.undoBtn.disabled =
|
|
31070
|
+
if (this.undoBtn) this.undoBtn.disabled = this.markNodes.length <= this.sessionBase;
|
|
30895
31071
|
if (this.clearBtn) this.clearBtn.disabled = empty;
|
|
30896
31072
|
}
|
|
30897
31073
|
// --------------------------------------------------------------------------
|
|
@@ -30905,6 +31081,7 @@ var AnnotationEditor = class {
|
|
|
30905
31081
|
}
|
|
30906
31082
|
onPointerDown = (e) => {
|
|
30907
31083
|
if (!this.active) return;
|
|
31084
|
+
if (this.drag) return;
|
|
30908
31085
|
if (e.button !== void 0 && e.button !== 0) return;
|
|
30909
31086
|
e.preventDefault();
|
|
30910
31087
|
const p = this.toDoc(e);
|
|
@@ -30954,6 +31131,7 @@ var AnnotationEditor = class {
|
|
|
30954
31131
|
this.drag = drag;
|
|
30955
31132
|
window.addEventListener("pointermove", this.onPointerMove, true);
|
|
30956
31133
|
window.addEventListener("pointerup", this.onPointerUp, true);
|
|
31134
|
+
window.addEventListener("pointercancel", this.onPointerCancel, true);
|
|
30957
31135
|
};
|
|
30958
31136
|
onPointerMove = (e) => {
|
|
30959
31137
|
const d = this.drag;
|
|
@@ -31063,9 +31241,18 @@ var AnnotationEditor = class {
|
|
|
31063
31241
|
d.node.removeAttribute("data-lens-draft");
|
|
31064
31242
|
this.commitMark(annotation, d.node);
|
|
31065
31243
|
};
|
|
31244
|
+
/** Pointer capture was cancelled (OS gesture, page hidden, etc.): drop the
|
|
31245
|
+
* in-progress draft node and detach listeners so nothing leaks. */
|
|
31246
|
+
onPointerCancel = () => {
|
|
31247
|
+
const d = this.drag;
|
|
31248
|
+
this.detachDragListeners();
|
|
31249
|
+
this.drag = null;
|
|
31250
|
+
d?.node.remove();
|
|
31251
|
+
};
|
|
31066
31252
|
detachDragListeners() {
|
|
31067
31253
|
window.removeEventListener("pointermove", this.onPointerMove, true);
|
|
31068
31254
|
window.removeEventListener("pointerup", this.onPointerUp, true);
|
|
31255
|
+
window.removeEventListener("pointercancel", this.onPointerCancel, true);
|
|
31069
31256
|
}
|
|
31070
31257
|
commitMark(annotation, node2) {
|
|
31071
31258
|
this.annotations.push(annotation);
|
|
@@ -31259,29 +31446,31 @@ var AnnotationEditor = class {
|
|
|
31259
31446
|
return `lens-${++this.idSeq}`;
|
|
31260
31447
|
}
|
|
31261
31448
|
};
|
|
31262
|
-
var INCLUDE_ITEMS = [
|
|
31263
|
-
{ key: "replay", label: "Replay" },
|
|
31264
|
-
{ key: "console", label: "Console" },
|
|
31265
|
-
{ key: "network", label: "Network" },
|
|
31266
|
-
{ key: "errors", label: "Errors" },
|
|
31267
|
-
{ key: "screenshot", label: "Screenshot" }
|
|
31268
|
-
];
|
|
31269
31449
|
var Panel = class {
|
|
31270
31450
|
constructor(cb) {
|
|
31271
31451
|
this.cb = cb;
|
|
31272
31452
|
this.body = el("div", { class: "lens-panel__body" });
|
|
31273
|
-
|
|
31274
|
-
el("
|
|
31275
|
-
|
|
31276
|
-
|
|
31277
|
-
|
|
31278
|
-
|
|
31279
|
-
|
|
31280
|
-
|
|
31281
|
-
|
|
31282
|
-
|
|
31283
|
-
|
|
31453
|
+
const head = el("div", { class: "lens-panel__head" }, [
|
|
31454
|
+
el("span", {
|
|
31455
|
+
class: "lens-panel__grip",
|
|
31456
|
+
html: iconGrip,
|
|
31457
|
+
title: "Drag to move",
|
|
31458
|
+
ariaLabel: "Drag to move panel"
|
|
31459
|
+
}),
|
|
31460
|
+
el("div", { class: "lens-panel__title", text: "Report an issue" }),
|
|
31461
|
+
el("button", {
|
|
31462
|
+
class: "lens-panel__x",
|
|
31463
|
+
ariaLabel: "Close",
|
|
31464
|
+
html: iconClose,
|
|
31465
|
+
on: { click: () => this.cb.onClose() }
|
|
31466
|
+
})
|
|
31284
31467
|
]);
|
|
31468
|
+
this.root = el(
|
|
31469
|
+
"div",
|
|
31470
|
+
{ class: "lens-panel", attrs: { role: "dialog", "aria-label": "Report an issue" } },
|
|
31471
|
+
[head, this.body]
|
|
31472
|
+
);
|
|
31473
|
+
this.drag = makeDraggable(this.root, { handle: head, key: "lens-panel" });
|
|
31285
31474
|
this.renderForm();
|
|
31286
31475
|
}
|
|
31287
31476
|
cb;
|
|
@@ -31289,6 +31478,7 @@ var Panel = class {
|
|
|
31289
31478
|
body;
|
|
31290
31479
|
textarea;
|
|
31291
31480
|
submitBtn;
|
|
31481
|
+
drag = null;
|
|
31292
31482
|
autoCloseTimer = null;
|
|
31293
31483
|
submitting = false;
|
|
31294
31484
|
/** Selected capture toggles; every flag starts ON. Persists across refresh. */
|
|
@@ -31308,6 +31498,8 @@ var Panel = class {
|
|
|
31308
31498
|
}
|
|
31309
31499
|
destroy() {
|
|
31310
31500
|
if (this.autoCloseTimer) clearTimeout(this.autoCloseTimer);
|
|
31501
|
+
this.drag?.destroy();
|
|
31502
|
+
this.drag = null;
|
|
31311
31503
|
this.root.remove();
|
|
31312
31504
|
}
|
|
31313
31505
|
renderForm() {
|
|
@@ -31322,7 +31514,6 @@ var Panel = class {
|
|
|
31322
31514
|
this.body.append(
|
|
31323
31515
|
this.textarea,
|
|
31324
31516
|
this.buildContext(),
|
|
31325
|
-
this.buildIncludes(),
|
|
31326
31517
|
this.buildChips(),
|
|
31327
31518
|
this.buildActions()
|
|
31328
31519
|
);
|
|
@@ -31342,39 +31533,6 @@ var Panel = class {
|
|
|
31342
31533
|
});
|
|
31343
31534
|
return el("div", { class: "lens-context", title: s.url }, nodes);
|
|
31344
31535
|
}
|
|
31345
|
-
/** Real toggles ("switch"): which captured data to attach. All ON by default. */
|
|
31346
|
-
buildIncludes() {
|
|
31347
|
-
const s = this.cb.getStats();
|
|
31348
|
-
const counts = {
|
|
31349
|
-
console: s.console,
|
|
31350
|
-
network: s.network,
|
|
31351
|
-
errors: s.errors
|
|
31352
|
-
};
|
|
31353
|
-
const toggles = INCLUDE_ITEMS.map((item) => this.buildToggle(item.key, item.label, counts[item.key]));
|
|
31354
|
-
return el("div", { class: "lens-includes" }, [
|
|
31355
|
-
el("span", { class: "lens-includes__label", text: "Include:" }),
|
|
31356
|
-
...toggles
|
|
31357
|
-
]);
|
|
31358
|
-
}
|
|
31359
|
-
buildToggle(key, label, count) {
|
|
31360
|
-
const on2 = this.include[key];
|
|
31361
|
-
const btn = el("button", {
|
|
31362
|
-
class: `lens-tgl${on2 ? " is-on" : ""}`,
|
|
31363
|
-
attrs: { type: "button", role: "switch", "aria-checked": String(on2) },
|
|
31364
|
-
html: `<span class="lens-tgl__check" aria-hidden="true">${iconCheck}</span>`
|
|
31365
|
-
});
|
|
31366
|
-
btn.append(el("span", { class: "lens-tgl__label", text: label }));
|
|
31367
|
-
if (typeof count === "number" && count > 0) {
|
|
31368
|
-
btn.append(el("span", { class: "lens-tgl__count", text: String(count) }));
|
|
31369
|
-
}
|
|
31370
|
-
btn.addEventListener("click", () => {
|
|
31371
|
-
const next = !this.include[key];
|
|
31372
|
-
this.include[key] = next;
|
|
31373
|
-
btn.classList.toggle("is-on", next);
|
|
31374
|
-
btn.setAttribute("aria-checked", String(next));
|
|
31375
|
-
});
|
|
31376
|
-
return btn;
|
|
31377
|
-
}
|
|
31378
31536
|
/** Removable context chips: annotations, retained recording, picked element. */
|
|
31379
31537
|
buildChips() {
|
|
31380
31538
|
const row = el("div", { class: "lens-chips" });
|
|
@@ -31679,14 +31837,28 @@ var OVERLAY_CSS = (
|
|
|
31679
31837
|
`
|
|
31680
31838
|
:host {
|
|
31681
31839
|
all: initial;
|
|
31682
|
-
|
|
31683
|
-
|
|
31840
|
+
/* Black & white, mirroring the Horus Cloud dashboard: the accent is near-black
|
|
31841
|
+
in light and near-white in dark; the always-dark pill/toolbar use explicit
|
|
31842
|
+
light highlights (defined at their rules). Annotation swatch colors and
|
|
31843
|
+
status red/green are content, not brand, and keep their hues. */
|
|
31844
|
+
--lens-accent: #18181b;
|
|
31845
|
+
--lens-accent-strong: #000000;
|
|
31846
|
+
--lens-on-accent: #ffffff;
|
|
31847
|
+
--lens-focus: rgba(0, 0, 0, 0.16);
|
|
31684
31848
|
--lens-radius: 8px;
|
|
31685
31849
|
--lens-font: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
31686
31850
|
--lens-z: 2147483000;
|
|
31687
31851
|
font-family: var(--lens-font);
|
|
31688
31852
|
line-height: 1.4;
|
|
31689
31853
|
}
|
|
31854
|
+
@media (prefers-color-scheme: dark) {
|
|
31855
|
+
:host {
|
|
31856
|
+
--lens-accent: #fafafa;
|
|
31857
|
+
--lens-accent-strong: #ffffff;
|
|
31858
|
+
--lens-on-accent: #18181b;
|
|
31859
|
+
--lens-focus: rgba(255, 255, 255, 0.22);
|
|
31860
|
+
}
|
|
31861
|
+
}
|
|
31690
31862
|
|
|
31691
31863
|
*, *::before, *::after { box-sizing: border-box; }
|
|
31692
31864
|
|
|
@@ -31706,7 +31878,7 @@ var OVERLAY_CSS = (
|
|
|
31706
31878
|
left: 50%;
|
|
31707
31879
|
transform: translateX(-50%);
|
|
31708
31880
|
max-width: calc(100vw - 16px);
|
|
31709
|
-
flex-wrap:
|
|
31881
|
+
flex-wrap: nowrap;
|
|
31710
31882
|
justify-content: center;
|
|
31711
31883
|
display: inline-flex;
|
|
31712
31884
|
align-items: center;
|
|
@@ -31730,7 +31902,7 @@ var OVERLAY_CSS = (
|
|
|
31730
31902
|
height: 26px;
|
|
31731
31903
|
margin-left: 4px;
|
|
31732
31904
|
border-radius: 999px;
|
|
31733
|
-
color: #
|
|
31905
|
+
color: #f4f4f5;
|
|
31734
31906
|
}
|
|
31735
31907
|
.lens-btn {
|
|
31736
31908
|
display: inline-flex;
|
|
@@ -31749,11 +31921,14 @@ var OVERLAY_CSS = (
|
|
|
31749
31921
|
transition: background 150ms ease, color 150ms ease;
|
|
31750
31922
|
}
|
|
31751
31923
|
.lens-btn:hover { background: rgba(255, 255, 255, 0.1); }
|
|
31752
|
-
.lens-btn:focus-visible { outline: 2px solid
|
|
31753
|
-
.lens-btn--primary { background:
|
|
31754
|
-
.lens-btn--primary:hover { background:
|
|
31924
|
+
.lens-btn:focus-visible { outline: 2px solid rgba(255, 255, 255, 0.7); outline-offset: 1px; }
|
|
31925
|
+
.lens-btn--primary { background: #fff; color: #18181b; }
|
|
31926
|
+
.lens-btn--primary:hover { background: #e4e4e7; }
|
|
31927
|
+
.lens-btn--report svg { flex: none; }
|
|
31928
|
+
.lens-btn__label { white-space: nowrap; }
|
|
31755
31929
|
.lens-btn--icon { width: 30px; padding: 0; justify-content: center; }
|
|
31756
|
-
|
|
31930
|
+
/* Always-dark pill: an active control reads as a light (white) highlight. */
|
|
31931
|
+
.lens-btn--icon.is-active { background: #fff; color: #18181b; }
|
|
31757
31932
|
.lens-pill__sep {
|
|
31758
31933
|
width: 1px;
|
|
31759
31934
|
height: 18px;
|
|
@@ -31767,7 +31942,10 @@ var OVERLAY_CSS = (
|
|
|
31767
31942
|
z-index: calc(var(--lens-z) - 2);
|
|
31768
31943
|
pointer-events: none;
|
|
31769
31944
|
border: 2px solid var(--lens-accent);
|
|
31770
|
-
|
|
31945
|
+
/* A light outer ring so the (black-in-light / white-in-dark) border stays
|
|
31946
|
+
visible over any page background. */
|
|
31947
|
+
box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.6), 0 0 0 3px rgba(0, 0, 0, 0.15);
|
|
31948
|
+
background: rgba(127, 127, 127, 0.14);
|
|
31771
31949
|
border-radius: 3px;
|
|
31772
31950
|
transition: all 60ms linear;
|
|
31773
31951
|
display: none;
|
|
@@ -31779,7 +31957,7 @@ var OVERLAY_CSS = (
|
|
|
31779
31957
|
padding: 1px 6px;
|
|
31780
31958
|
border-radius: 4px;
|
|
31781
31959
|
background: var(--lens-accent);
|
|
31782
|
-
color:
|
|
31960
|
+
color: var(--lens-on-accent);
|
|
31783
31961
|
font: 500 11px/1.5 var(--lens-font);
|
|
31784
31962
|
white-space: nowrap;
|
|
31785
31963
|
}
|
|
@@ -31858,7 +32036,7 @@ var OVERLAY_CSS = (
|
|
|
31858
32036
|
padding: 14px 16px;
|
|
31859
32037
|
border-bottom: 1px solid rgba(0, 0, 0, 0.07);
|
|
31860
32038
|
}
|
|
31861
|
-
.lens-panel__title { font-size: 14px; font-weight: 600; }
|
|
32039
|
+
.lens-panel__title { font-size: 14px; font-weight: 600; margin-right: auto; }
|
|
31862
32040
|
.lens-panel__x {
|
|
31863
32041
|
display: inline-flex;
|
|
31864
32042
|
align-items: center;
|
|
@@ -31896,7 +32074,7 @@ var OVERLAY_CSS = (
|
|
|
31896
32074
|
.lens-textarea:focus {
|
|
31897
32075
|
outline: none;
|
|
31898
32076
|
border-color: var(--lens-accent);
|
|
31899
|
-
box-shadow: 0 0 0 3px
|
|
32077
|
+
box-shadow: 0 0 0 3px var(--lens-focus);
|
|
31900
32078
|
}
|
|
31901
32079
|
/* ---- Context row: static, non-interactive page facts (muted, dot-joined) ---- */
|
|
31902
32080
|
.lens-context {
|
|
@@ -31911,49 +32089,6 @@ var OVERLAY_CSS = (
|
|
|
31911
32089
|
.lens-context__item { white-space: nowrap; }
|
|
31912
32090
|
.lens-context__dot { opacity: 0.55; }
|
|
31913
32091
|
|
|
31914
|
-
/* ---- Include row: real toggles (role=switch) for which data to attach ---- */
|
|
31915
|
-
.lens-includes {
|
|
31916
|
-
display: flex;
|
|
31917
|
-
flex-wrap: wrap;
|
|
31918
|
-
align-items: center;
|
|
31919
|
-
gap: 6px;
|
|
31920
|
-
}
|
|
31921
|
-
.lens-includes__label {
|
|
31922
|
-
font-size: 11.5px;
|
|
31923
|
-
font-weight: 600;
|
|
31924
|
-
color: #71717a;
|
|
31925
|
-
margin-right: 2px;
|
|
31926
|
-
}
|
|
31927
|
-
.lens-tgl {
|
|
31928
|
-
display: inline-flex;
|
|
31929
|
-
align-items: center;
|
|
31930
|
-
gap: 5px;
|
|
31931
|
-
padding: 4px 10px;
|
|
31932
|
-
border-radius: 999px;
|
|
31933
|
-
border: 1px solid rgba(0, 0, 0, 0.14);
|
|
31934
|
-
background: #fafafa;
|
|
31935
|
-
color: #a1a1aa;
|
|
31936
|
-
font-family: var(--lens-font);
|
|
31937
|
-
font-size: 11.5px;
|
|
31938
|
-
font-weight: 600;
|
|
31939
|
-
white-space: nowrap;
|
|
31940
|
-
cursor: pointer;
|
|
31941
|
-
opacity: 0.75;
|
|
31942
|
-
transition: background 150ms ease, border-color 150ms ease, color 150ms ease, opacity 150ms ease;
|
|
31943
|
-
}
|
|
31944
|
-
.lens-tgl:hover { opacity: 1; }
|
|
31945
|
-
.lens-tgl:focus-visible { outline: 2px solid var(--lens-accent); outline-offset: 1px; }
|
|
31946
|
-
.lens-tgl__check { display: none; align-items: center; }
|
|
31947
|
-
.lens-tgl__check svg { width: 13px; height: 13px; }
|
|
31948
|
-
.lens-tgl__count { font-weight: 500; opacity: 0.75; font-variant-numeric: tabular-nums; }
|
|
31949
|
-
.lens-tgl.is-on {
|
|
31950
|
-
opacity: 1;
|
|
31951
|
-
color: var(--lens-accent-strong);
|
|
31952
|
-
border-color: rgba(99, 102, 241, 0.5);
|
|
31953
|
-
background: #eef2ff;
|
|
31954
|
-
}
|
|
31955
|
-
.lens-tgl.is-on .lens-tgl__check { display: inline-flex; }
|
|
31956
|
-
|
|
31957
32092
|
.lens-chips { display: flex; flex-wrap: wrap; gap: 6px; }
|
|
31958
32093
|
.lens-chip {
|
|
31959
32094
|
display: inline-flex;
|
|
@@ -31974,9 +32109,9 @@ var OVERLAY_CSS = (
|
|
|
31974
32109
|
.lens-chip--ok { color: #15803d; background: #f0fdf4; border-color: rgba(21, 128, 61, 0.18); }
|
|
31975
32110
|
.lens-chip--target {
|
|
31976
32111
|
max-width: 100%;
|
|
31977
|
-
background:
|
|
31978
|
-
border-color: rgba(
|
|
31979
|
-
color:
|
|
32112
|
+
background: rgba(0, 0, 0, 0.05);
|
|
32113
|
+
border-color: rgba(0, 0, 0, 0.16);
|
|
32114
|
+
color: #18181b;
|
|
31980
32115
|
}
|
|
31981
32116
|
.lens-chip__code {
|
|
31982
32117
|
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
@@ -32024,7 +32159,7 @@ var OVERLAY_CSS = (
|
|
|
32024
32159
|
.lens-abtn svg { flex: none; }
|
|
32025
32160
|
.lens-abtn--ghost { background: transparent; border-color: rgba(0, 0, 0, 0.14); color: #3f3f46; }
|
|
32026
32161
|
.lens-abtn--ghost:hover { background: rgba(0, 0, 0, 0.04); }
|
|
32027
|
-
.lens-abtn--primary { background: var(--lens-accent); color:
|
|
32162
|
+
.lens-abtn--primary { background: var(--lens-accent); color: var(--lens-on-accent); }
|
|
32028
32163
|
.lens-abtn--primary:hover { background: var(--lens-accent-strong); }
|
|
32029
32164
|
.lens-abtn:disabled { opacity: 0.55; cursor: default; }
|
|
32030
32165
|
.lens-abtn:focus-visible { outline: 2px solid var(--lens-accent); outline-offset: 1px; }
|
|
@@ -32078,12 +32213,9 @@ var OVERLAY_CSS = (
|
|
|
32078
32213
|
.lens-panel__x:hover { background: rgba(255, 255, 255, 0.08); }
|
|
32079
32214
|
.lens-textarea { background: #26262a; border-color: rgba(255, 255, 255, 0.14); color: #f4f4f5; }
|
|
32080
32215
|
.lens-context { color: #a1a1aa; }
|
|
32081
|
-
.lens-includes__label { color: #a1a1aa; }
|
|
32082
|
-
.lens-tgl { background: #26262a; border-color: rgba(255, 255, 255, 0.12); color: #a1a1aa; }
|
|
32083
|
-
.lens-tgl.is-on { background: rgba(99, 102, 241, 0.18); border-color: rgba(99, 102, 241, 0.55); color: #c7d2fe; }
|
|
32084
32216
|
.lens-chip { background: #26262a; border-color: rgba(255, 255, 255, 0.08); color: #d4d4d8; }
|
|
32085
32217
|
.lens-chip--ok { color: #4ade80; background: rgba(34, 197, 94, 0.12); border-color: rgba(74, 222, 128, 0.22); }
|
|
32086
|
-
.lens-chip--target { background: rgba(
|
|
32218
|
+
.lens-chip--target { background: rgba(255, 255, 255, 0.1); border-color: rgba(255, 255, 255, 0.18); color: #f4f4f5; }
|
|
32087
32219
|
.lens-abtn--ghost { border-color: rgba(255, 255, 255, 0.16); color: #d4d4d8; }
|
|
32088
32220
|
.lens-abtn--ghost:hover { background: rgba(255, 255, 255, 0.06); }
|
|
32089
32221
|
.lens-state__msg { color: #a1a1aa; }
|
|
@@ -32117,7 +32249,9 @@ var OVERLAY_CSS = (
|
|
|
32117
32249
|
.lens-annotate-chips { z-index: var(--lens-z); }
|
|
32118
32250
|
|
|
32119
32251
|
/* ---------------- Annotation toolbar ---------------- */
|
|
32120
|
-
/*
|
|
32252
|
+
/* Floating top-center anchor. A fixed drag grip sits left of a tools "track":
|
|
32253
|
+
the track wraps on desktop and becomes a single horizontal-scroll row on
|
|
32254
|
+
narrow screens, so the bar stays one slim height instead of a bulky block. */
|
|
32121
32255
|
.lens-annotate-toolbar {
|
|
32122
32256
|
position: fixed;
|
|
32123
32257
|
top: calc(16px + env(safe-area-inset-top));
|
|
@@ -32125,11 +32259,10 @@ var OVERLAY_CSS = (
|
|
|
32125
32259
|
transform: translateX(-50%);
|
|
32126
32260
|
z-index: var(--lens-z);
|
|
32127
32261
|
display: inline-flex;
|
|
32128
|
-
flex-wrap:
|
|
32262
|
+
flex-wrap: nowrap;
|
|
32129
32263
|
align-items: center;
|
|
32130
|
-
justify-content: center;
|
|
32131
32264
|
gap: 6px;
|
|
32132
|
-
max-width: calc(100vw -
|
|
32265
|
+
max-width: calc(100vw - 16px);
|
|
32133
32266
|
padding: 6px 8px;
|
|
32134
32267
|
border-radius: 14px;
|
|
32135
32268
|
background: rgba(20, 20, 23, 0.92);
|
|
@@ -32139,12 +32272,50 @@ var OVERLAY_CSS = (
|
|
|
32139
32272
|
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.35);
|
|
32140
32273
|
color: #f4f4f5;
|
|
32141
32274
|
}
|
|
32275
|
+
/* Tools lay out as ONE horizontal row that scrolls if it can't fit, so the bar
|
|
32276
|
+
is always a single slim height \u2014 never a wrapping (or, worse, min-content
|
|
32277
|
+
collapsed vertical) block. The grip sits outside this track. */
|
|
32278
|
+
.lens-tool-track {
|
|
32279
|
+
display: flex;
|
|
32280
|
+
align-items: center;
|
|
32281
|
+
flex-wrap: nowrap;
|
|
32282
|
+
justify-content: flex-start;
|
|
32283
|
+
gap: 6px;
|
|
32284
|
+
flex: 1 1 auto;
|
|
32285
|
+
min-width: 0;
|
|
32286
|
+
overflow-x: auto;
|
|
32287
|
+
overflow-y: hidden;
|
|
32288
|
+
scrollbar-width: none;
|
|
32289
|
+
-webkit-overflow-scrolling: touch;
|
|
32290
|
+
}
|
|
32291
|
+
.lens-tool-track::-webkit-scrollbar { display: none; }
|
|
32142
32292
|
.lens-tool-group {
|
|
32143
32293
|
display: inline-flex;
|
|
32144
32294
|
align-items: center;
|
|
32145
32295
|
gap: 4px;
|
|
32146
|
-
flex-wrap:
|
|
32296
|
+
flex-wrap: nowrap;
|
|
32297
|
+
flex: none;
|
|
32147
32298
|
}
|
|
32299
|
+
/* Single close (=Done, keeps marks) pinned at the right, always reachable
|
|
32300
|
+
while the tools scroll. Reads as a light control on the dark bar. */
|
|
32301
|
+
.lens-tool-close {
|
|
32302
|
+
display: inline-flex;
|
|
32303
|
+
align-items: center;
|
|
32304
|
+
justify-content: center;
|
|
32305
|
+
flex: none;
|
|
32306
|
+
width: 32px;
|
|
32307
|
+
height: 30px;
|
|
32308
|
+
padding: 0;
|
|
32309
|
+
border: 0;
|
|
32310
|
+
border-radius: 8px;
|
|
32311
|
+
background: rgba(255, 255, 255, 0.14);
|
|
32312
|
+
color: #f4f4f5;
|
|
32313
|
+
cursor: pointer;
|
|
32314
|
+
transition: background 150ms ease;
|
|
32315
|
+
}
|
|
32316
|
+
.lens-tool-close:hover { background: rgba(255, 255, 255, 0.26); }
|
|
32317
|
+
.lens-tool-close:focus-visible { outline: 2px solid rgba(255, 255, 255, 0.7); outline-offset: 1px; }
|
|
32318
|
+
.lens-tool-close svg { width: 18px; height: 18px; }
|
|
32148
32319
|
.lens-tool-sep {
|
|
32149
32320
|
width: 1px;
|
|
32150
32321
|
align-self: stretch;
|
|
@@ -32152,6 +32323,51 @@ var OVERLAY_CSS = (
|
|
|
32152
32323
|
background: rgba(255, 255, 255, 0.16);
|
|
32153
32324
|
margin: 0 2px;
|
|
32154
32325
|
}
|
|
32326
|
+
|
|
32327
|
+
/* ---------------- Draggable surfaces ---------------- */
|
|
32328
|
+
/* Every drag handle: no page pan/scroll gesture starts here, and the cursor
|
|
32329
|
+
signals grabbability. Applied to the pill, panel header, toolbar grip, chips. */
|
|
32330
|
+
.lens-drag-handle { touch-action: none; cursor: grab; }
|
|
32331
|
+
.lens-drag-handle:active { cursor: grabbing; }
|
|
32332
|
+
.is-dragging, .is-dragging * { cursor: grabbing !important; }
|
|
32333
|
+
.is-dragging { user-select: none; }
|
|
32334
|
+
/* While dragging, drop the entry animation/transition so it tracks 1:1. */
|
|
32335
|
+
.lens-pill.is-dragging, .lens-panel.is-dragging,
|
|
32336
|
+
.lens-annotate-toolbar.is-dragging, .lens-rec-chip.is-dragging {
|
|
32337
|
+
animation: none;
|
|
32338
|
+
transition: none;
|
|
32339
|
+
}
|
|
32340
|
+
|
|
32341
|
+
/* Toolbar drag grip \u2014 the only grab region, kept out of the scroll track. */
|
|
32342
|
+
.lens-tool-grip {
|
|
32343
|
+
display: inline-flex;
|
|
32344
|
+
align-items: center;
|
|
32345
|
+
justify-content: center;
|
|
32346
|
+
flex: none;
|
|
32347
|
+
width: 22px;
|
|
32348
|
+
height: 30px;
|
|
32349
|
+
margin-right: 2px;
|
|
32350
|
+
border-radius: 6px;
|
|
32351
|
+
color: #a1a1aa;
|
|
32352
|
+
}
|
|
32353
|
+
.lens-tool-grip:hover { color: #f4f4f5; background: rgba(255, 255, 255, 0.08); }
|
|
32354
|
+
.lens-tool-grip svg { width: 16px; height: 16px; }
|
|
32355
|
+
|
|
32356
|
+
/* Pill grip (the eye dot) + panel header grip. */
|
|
32357
|
+
.lens-pill__grip { cursor: grab; }
|
|
32358
|
+
.lens-panel__grip {
|
|
32359
|
+
display: inline-flex;
|
|
32360
|
+
align-items: center;
|
|
32361
|
+
justify-content: center;
|
|
32362
|
+
flex: none;
|
|
32363
|
+
width: 22px;
|
|
32364
|
+
height: 22px;
|
|
32365
|
+
margin-right: 6px;
|
|
32366
|
+
color: #a1a1aa;
|
|
32367
|
+
}
|
|
32368
|
+
.lens-panel__grip svg { width: 16px; height: 16px; }
|
|
32369
|
+
.lens-panel__head { cursor: grab; }
|
|
32370
|
+
.lens-panel__head:active { cursor: grabbing; }
|
|
32155
32371
|
.lens-tool-btn {
|
|
32156
32372
|
display: inline-flex;
|
|
32157
32373
|
align-items: center;
|
|
@@ -32167,11 +32383,11 @@ var OVERLAY_CSS = (
|
|
|
32167
32383
|
transition: background 150ms ease, color 150ms ease, border-color 150ms ease;
|
|
32168
32384
|
}
|
|
32169
32385
|
.lens-tool-btn:hover { background: rgba(255, 255, 255, 0.1); color: #fff; }
|
|
32170
|
-
.lens-tool-btn:focus-visible { outline: 2px solid
|
|
32386
|
+
.lens-tool-btn:focus-visible { outline: 2px solid rgba(255, 255, 255, 0.7); outline-offset: 1px; }
|
|
32171
32387
|
.lens-tool-btn.is-active {
|
|
32172
|
-
background:
|
|
32173
|
-
border-color:
|
|
32174
|
-
color: #
|
|
32388
|
+
background: #fff;
|
|
32389
|
+
border-color: #fff;
|
|
32390
|
+
color: #18181b;
|
|
32175
32391
|
}
|
|
32176
32392
|
.lens-tool-btn:disabled { opacity: 0.4; cursor: default; }
|
|
32177
32393
|
.lens-tool-btn:disabled:hover { background: transparent; color: #d4d4d8; }
|
|
@@ -32211,17 +32427,18 @@ var OVERLAY_CSS = (
|
|
|
32211
32427
|
transition: background 150ms ease, color 150ms ease, border-color 150ms ease;
|
|
32212
32428
|
}
|
|
32213
32429
|
.lens-width-btn:hover { background: rgba(255, 255, 255, 0.1); color: #fff; }
|
|
32214
|
-
.lens-width-btn:focus-visible { outline: 2px solid
|
|
32430
|
+
.lens-width-btn:focus-visible { outline: 2px solid rgba(255, 255, 255, 0.7); outline-offset: 1px; }
|
|
32215
32431
|
.lens-width-btn.is-active {
|
|
32216
|
-
background:
|
|
32217
|
-
border-color:
|
|
32218
|
-
color: #
|
|
32432
|
+
background: #fff;
|
|
32433
|
+
border-color: #fff;
|
|
32434
|
+
color: #18181b;
|
|
32219
32435
|
}
|
|
32220
32436
|
|
|
32221
32437
|
/* Coarse pointers: bump the tool targets toward 40px. */
|
|
32222
32438
|
@media (pointer: coarse) {
|
|
32223
32439
|
.lens-tool-btn, .lens-width-btn { width: 38px; height: 38px; }
|
|
32224
32440
|
.lens-swatch { width: 30px; height: 30px; }
|
|
32441
|
+
.lens-tool-grip { width: 30px; height: 38px; }
|
|
32225
32442
|
}
|
|
32226
32443
|
|
|
32227
32444
|
/* Stacking inside the shadow layer: the panel, pill and floating chips always
|
|
@@ -32246,8 +32463,8 @@ var OVERLAY_CSS = (
|
|
|
32246
32463
|
transition: background 150ms ease;
|
|
32247
32464
|
}
|
|
32248
32465
|
.lens-pick-chip__btn:hover { background: rgba(255, 255, 255, 0.2); }
|
|
32249
|
-
.lens-pick-chip__btn--primary { background:
|
|
32250
|
-
.lens-pick-chip__btn--primary:hover { background:
|
|
32466
|
+
.lens-pick-chip__btn--primary { background: #fff; color: #18181b; }
|
|
32467
|
+
.lens-pick-chip__btn--primary:hover { background: #e4e4e7; }
|
|
32251
32468
|
|
|
32252
32469
|
/* ---------------- Recording chip ---------------- */
|
|
32253
32470
|
.lens-rec-chip {
|
|
@@ -32302,21 +32519,29 @@ var OVERLAY_CSS = (
|
|
|
32302
32519
|
.lens-rec-chip__stop:hover { background: #dc2626; }
|
|
32303
32520
|
.lens-chip--rec { gap: 6px; }
|
|
32304
32521
|
|
|
32522
|
+
/* Very narrow screens: collapse the pill's "Report issue" label to its icon so
|
|
32523
|
+
the pill stays a single slim row (never wraps into a 2-row blob). */
|
|
32524
|
+
@media (max-width: 400px) {
|
|
32525
|
+
.lens-btn__label { display: none; }
|
|
32526
|
+
.lens-btn--report { width: 44px; padding: 0; justify-content: center; }
|
|
32527
|
+
}
|
|
32528
|
+
|
|
32305
32529
|
/* ---------------- Coarse pointers (touch): \u226544px targets ---------------- */
|
|
32306
32530
|
@media (pointer: coarse) {
|
|
32307
32531
|
.lens-btn { height: 44px; padding: 0 16px; }
|
|
32308
32532
|
.lens-btn--icon { width: 44px; }
|
|
32533
|
+
.lens-tool-close { width: 44px; height: 38px; }
|
|
32309
32534
|
.lens-pill__dot { width: 40px; height: 40px; }
|
|
32310
32535
|
.lens-abtn { height: 44px; }
|
|
32311
|
-
.lens-tgl { min-height: 40px; padding: 4px 14px; }
|
|
32312
32536
|
.lens-panel__x { width: 44px; height: 44px; }
|
|
32313
32537
|
.lens-chip__x { min-width: 32px; min-height: 32px; align-items: center; justify-content: center; }
|
|
32314
32538
|
.lens-pick-chip__btn, .lens-rec-chip__stop { height: 40px; padding: 0 18px; }
|
|
32315
32539
|
}
|
|
32316
32540
|
|
|
32317
|
-
/* ----------------
|
|
32541
|
+
/* ---------------- Narrow screens (\u2264640px) ---------------- */
|
|
32318
32542
|
@media (max-width: 640px) {
|
|
32319
|
-
|
|
32543
|
+
/* Report panel as a bottom sheet (unless the user has dragged it loose). */
|
|
32544
|
+
.lens-panel:not(.is-dragging) {
|
|
32320
32545
|
left: 0;
|
|
32321
32546
|
right: 0;
|
|
32322
32547
|
bottom: 0;
|
|
@@ -32333,6 +32558,14 @@ var OVERLAY_CSS = (
|
|
|
32333
32558
|
to { opacity: 1; transform: none; }
|
|
32334
32559
|
}
|
|
32335
32560
|
.lens-textarea { font-size: 16px; }
|
|
32561
|
+
|
|
32562
|
+
/* On narrow screens the single-row tool track usually overflows: fade its
|
|
32563
|
+
trailing edge so it reads as "more tools this way" (the grip sits outside
|
|
32564
|
+
the track and stays fully opaque). */
|
|
32565
|
+
.lens-tool-track {
|
|
32566
|
+
-webkit-mask-image: linear-gradient(90deg, #000 calc(100% - 20px), transparent 100%);
|
|
32567
|
+
mask-image: linear-gradient(90deg, #000 calc(100% - 20px), transparent 100%);
|
|
32568
|
+
}
|
|
32336
32569
|
}
|
|
32337
32570
|
`
|
|
32338
32571
|
);
|
|
@@ -32350,8 +32583,14 @@ var Overlay = class {
|
|
|
32350
32583
|
this.shadow.append(this.layer);
|
|
32351
32584
|
this.picker = new ElementPicker(this.layer, this.host);
|
|
32352
32585
|
this.annotator = new AnnotationEditor(this.layer);
|
|
32353
|
-
this.
|
|
32586
|
+
const pill = this.buildPill();
|
|
32587
|
+
this.layer.append(pill);
|
|
32354
32588
|
document.documentElement.append(this.host);
|
|
32589
|
+
this.pillDrag = makeDraggable(pill, {
|
|
32590
|
+
handle: pill,
|
|
32591
|
+
key: "lens-pill",
|
|
32592
|
+
ignore: isInteractiveTarget
|
|
32593
|
+
});
|
|
32355
32594
|
window.addEventListener("keydown", this.onGlobalKey, true);
|
|
32356
32595
|
}
|
|
32357
32596
|
deps;
|
|
@@ -32364,7 +32603,9 @@ var Overlay = class {
|
|
|
32364
32603
|
panel = null;
|
|
32365
32604
|
target = null;
|
|
32366
32605
|
destroyed = false;
|
|
32606
|
+
pillDrag = null;
|
|
32367
32607
|
recordingChip = null;
|
|
32608
|
+
recordingDrag = null;
|
|
32368
32609
|
recordingTimerEl = null;
|
|
32369
32610
|
recordingInterval = null;
|
|
32370
32611
|
recordingStartTs = 0;
|
|
@@ -32379,10 +32620,16 @@ var Overlay = class {
|
|
|
32379
32620
|
on: { click: () => this.togglePicker() }
|
|
32380
32621
|
});
|
|
32381
32622
|
return el("div", { class: "lens-pill", attrs: { role: "toolbar", "aria-label": "Lens" } }, [
|
|
32382
|
-
el("span", {
|
|
32623
|
+
el("span", {
|
|
32624
|
+
class: "lens-pill__dot lens-pill__grip",
|
|
32625
|
+
html: iconEye,
|
|
32626
|
+
title: "Drag to move",
|
|
32627
|
+
ariaLabel: "Drag to move Lens"
|
|
32628
|
+
}),
|
|
32383
32629
|
el("button", {
|
|
32384
|
-
class: "lens-btn",
|
|
32385
|
-
|
|
32630
|
+
class: "lens-btn lens-btn--report",
|
|
32631
|
+
html: iconReport + '<span class="lens-btn__label">Report issue</span>',
|
|
32632
|
+
ariaLabel: "Report issue",
|
|
32386
32633
|
on: { click: () => this.open() }
|
|
32387
32634
|
}),
|
|
32388
32635
|
this.pickBtn,
|
|
@@ -32436,6 +32683,7 @@ var Overlay = class {
|
|
|
32436
32683
|
this.stopPicker();
|
|
32437
32684
|
return;
|
|
32438
32685
|
}
|
|
32686
|
+
if (this.annotator.isActive) this.annotator.exit();
|
|
32439
32687
|
this.close();
|
|
32440
32688
|
this.pickBtn.classList.add("is-active");
|
|
32441
32689
|
this.picker.start(
|
|
@@ -32497,6 +32745,11 @@ var Overlay = class {
|
|
|
32497
32745
|
})
|
|
32498
32746
|
]);
|
|
32499
32747
|
this.layer.append(this.recordingChip);
|
|
32748
|
+
this.recordingDrag = makeDraggable(this.recordingChip, {
|
|
32749
|
+
handle: this.recordingChip,
|
|
32750
|
+
key: "lens-rec-chip",
|
|
32751
|
+
ignore: isInteractiveTarget
|
|
32752
|
+
});
|
|
32500
32753
|
this.recordingInterval = setInterval(() => this.tickRecording(), 250);
|
|
32501
32754
|
}
|
|
32502
32755
|
tickRecording() {
|
|
@@ -32509,6 +32762,10 @@ var Overlay = class {
|
|
|
32509
32762
|
clearInterval(this.recordingInterval);
|
|
32510
32763
|
this.recordingInterval = null;
|
|
32511
32764
|
}
|
|
32765
|
+
if (this.recordingDrag) {
|
|
32766
|
+
this.recordingDrag.destroy();
|
|
32767
|
+
this.recordingDrag = null;
|
|
32768
|
+
}
|
|
32512
32769
|
if (this.recordingChip) {
|
|
32513
32770
|
this.recordingChip.remove();
|
|
32514
32771
|
this.recordingChip = null;
|
|
@@ -32549,6 +32806,8 @@ var Overlay = class {
|
|
|
32549
32806
|
if (this.destroyed) return;
|
|
32550
32807
|
this.destroyed = true;
|
|
32551
32808
|
window.removeEventListener("keydown", this.onGlobalKey, true);
|
|
32809
|
+
this.pillDrag?.destroy();
|
|
32810
|
+
this.pillDrag = null;
|
|
32552
32811
|
this.stopPicker();
|
|
32553
32812
|
if (this.deps.client.isRecording()) this.deps.client.stopRecording();
|
|
32554
32813
|
this.hideRecordingChip();
|