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