@codeam/shared 2.76.35 → 2.76.36

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -63,6 +63,8 @@ __export(index_exports, {
63
63
  PACK_STATUS_COMMAND: () => PACK_STATUS_COMMAND,
64
64
  PACK_WORKFLOW_ARTICLE: () => PACK_WORKFLOW_ARTICLE,
65
65
  PLUGIN_AGENT_STATUSES: () => PLUGIN_AGENT_STATUSES,
66
+ POINT_HTML_MAX: () => POINT_HTML_MAX,
67
+ POINT_TEXT_MAX: () => POINT_TEXT_MAX,
66
68
  PREVIEW_DETECT_PROMPT: () => PREVIEW_DETECT_PROMPT,
67
69
  PROTOCOL_VERSION: () => PROTOCOL_VERSION,
68
70
  PUBLIC_TO_INTERNAL: () => PUBLIC_TO_INTERNAL,
@@ -118,6 +120,7 @@ __export(index_exports, {
118
120
  normalizeGuardrailPolicy: () => normalizeGuardrailPolicy,
119
121
  orderDeployChoices: () => orderDeployChoices,
120
122
  parsePackFindings: () => parsePackFindings,
123
+ pointPickerScript: () => pointPickerScript,
121
124
  publicToInternal: () => publicToInternal,
122
125
  recommendForDeps: () => recommendForDeps,
123
126
  renderToLines: () => renderToLines,
@@ -3336,6 +3339,724 @@ function isPluginAgentStatus(x) {
3336
3339
  var DEFAULT_FEATURE_GATES = {
3337
3340
  beads: false
3338
3341
  };
3342
+
3343
+ // src/point-picker/script.ts
3344
+ var POINT_TEXT_MAX = 120;
3345
+ var POINT_HTML_MAX = 600;
3346
+ var DEFAULT_ACCENT = "#7C5CFF";
3347
+ var HEX = /^#[0-9A-Fa-f]{6}$/;
3348
+ var IDENT = /^[A-Za-z_$][\w$]*$/;
3349
+ function js(v) {
3350
+ return JSON.stringify(v).replace(/</g, "\\u003c");
3351
+ }
3352
+ function pointPickerScript(opts) {
3353
+ const multi = opts.mode === "multi";
3354
+ const maxMarks = opts.maxMarks ?? 4;
3355
+ const accent = opts.accent ?? DEFAULT_ACCENT;
3356
+ if (opts.channelField !== "channel" && opts.channelField !== "source") {
3357
+ throw new Error(`pointPickerScript: bad channelField ${String(opts.channelField)}`);
3358
+ }
3359
+ if (!HEX.test(accent))
3360
+ throw new Error(`pointPickerScript: accent must be #RRGGBB, got ${accent}`);
3361
+ if (!Number.isInteger(maxMarks) || maxMarks < 1) {
3362
+ throw new Error(`pointPickerScript: maxMarks must be a positive integer, got ${maxMarks}`);
3363
+ }
3364
+ if (opts.globalName !== void 0 && !IDENT.test(opts.globalName)) {
3365
+ throw new Error(`pointPickerScript: globalName must be an identifier, got ${opts.globalName}`);
3366
+ }
3367
+ return `(function () {
3368
+ 'use strict';
3369
+ var W = window, D = document;
3370
+ var CH = ${js(opts.channel)};
3371
+ var FIELD = ${js(opts.channelField)};
3372
+ var ALLOWED = ${js(opts.allowedParents)};
3373
+ var MULTI = ${multi}, MAX_MARKS = ${maxMarks}, AGENT = ${!!opts.agentHighlights}, REACT_SOURCE = ${!!opts.reactSource};
3374
+ var GLOBAL = ${js(opts.globalName ?? null)};
3375
+ var DEFAULT_ACCENT = ${js(accent)};
3376
+ var GUARD = '__pointPicker:' + CH;
3377
+ if (W[GUARD]) return;
3378
+ try { Object.defineProperty(W, GUARD, { value: true }); } catch (e) { return; }
3379
+ try {
3380
+ var TEXT_MAX = ${POINT_TEXT_MAX}, HTML_MAX = ${POINT_HTML_MAX}, ATTR_MAX = 60;
3381
+ var ACCENT_RE = /^#[0-9A-Fa-f]{6}$/;
3382
+ var AGENT_ACCENT = '#22d3ee';
3383
+ var EASE = 'cubic-bezier(.2,.8,.2,1)';
3384
+ var CHIP_FONT = 'italic 500 13px/1.3 Georgia,"Times New Roman",serif';
3385
+ var CONTROLS = { BUTTON: 1, A: 1, INPUT: 1, SELECT: 1, TEXTAREA: 1, LABEL: 1, SUMMARY: 1 };
3386
+ var ROLES = { button: 1, link: 1, tab: 1, checkbox: 1, switch: 1, menuitem: 1, option: 1, radio: 1, slider: 1 };
3387
+ var VOID = { AREA: 1, BR: 1, COL: 1, EMBED: 1, HR: 1, IMG: 1, INPUT: 1, LINK: 1, META: 1, SOURCE: 1, TRACK: 1, WBR: 1 };
3388
+ var GRAIN = "url(\\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 .08 0 0 0 0 .08 0 0 0 0 .09 0 0 0 .12 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E\\")";
3389
+
3390
+ var on = false, driver = null, accent = DEFAULT_ACCENT;
3391
+ var host = null, veil = null, ring = null, chip = null, style = null, leaving = null;
3392
+ var current = null, down = null, tapped = false, bound = [], swallow = null;
3393
+ // Multi mode: the pinned marks, drawn in the page and anchored to the document.
3394
+ // Mark ids carry a per-load prefix: after a reload \`seq\` restarts, and the
3395
+ // app may still hold the previous page's p\u2026-1.
3396
+ var picked = [], seq = 0, marksHost = null, marksRoot = null;
3397
+ var LOAD = (Math.random().toString(36).slice(2) + '0000').slice(0, 4);
3398
+ // The agent's highlights: separate from the person's marks, never numbered.
3399
+ var agentMarks = [], agentHost = null, agentRoot = null, agentLayer = null, agentCursor = null, agentListening = false;
3400
+
3401
+ // Inside a React Native WebView the document is TOP-LEVEL (window.parent ===
3402
+ // window), so the only way out is the bridge RN injects. It carries STRINGS.
3403
+ function rnBridge() {
3404
+ if (!GLOBAL) return null;
3405
+ var rn = W.ReactNativeWebView;
3406
+ return rn && typeof rn.postMessage === 'function' ? rn : null;
3407
+ }
3408
+
3409
+ function send(type, extra) {
3410
+ if (!driver) return;
3411
+ var msg = {};
3412
+ msg[FIELD] = CH;
3413
+ msg.type = type;
3414
+ if (extra) for (var k in extra) msg[k] = extra[k];
3415
+ var rn = rnBridge();
3416
+ if (rn) {
3417
+ try { rn.postMessage(JSON.stringify(msg)); } catch (e) {}
3418
+ return;
3419
+ }
3420
+ if (W.parent === W) return;
3421
+ try { W.parent.postMessage(msg, driver); } catch (e) {}
3422
+ }
3423
+
3424
+ function clip(s, max) { return s.length > max ? s.slice(0, max - 1) + '\\u2026' : s; }
3425
+ function squash(s) { return String(s == null ? '' : s).replace(/\\s+/g, ' ').trim(); }
3426
+ function attr(el, n) { var v = el.getAttribute ? el.getAttribute(n) : null; return v == null ? '' : String(v); }
3427
+ function tagOf(el) { return String(el.tagName || '').toLowerCase(); }
3428
+ function reduced() {
3429
+ try { return !!(W.matchMedia && W.matchMedia('(prefers-reduced-motion: reduce)').matches); } catch (e) { return false; }
3430
+ }
3431
+ function drop(node) { if (node && node.parentNode) node.parentNode.removeChild(node); }
3432
+ function shadowOf(el) { return el.attachShadow ? el.attachShadow({ mode: 'closed' }) : el; }
3433
+ function clear(node) { while (node && node.childNodes && node.childNodes.length) node.removeChild(node.childNodes[0]); }
3434
+
3435
+ // A tap lands on the innermost node (the <span> in a button, the <path> in
3436
+ // an icon); the person means the control around it.
3437
+ function target(el) {
3438
+ var n = el;
3439
+ for (var depth = 0; n && n !== D.body && n !== D.documentElement && depth < 6; depth++) {
3440
+ if (CONTROLS[String(n.tagName).toUpperCase()] || ROLES[attr(n, 'role')]) return n;
3441
+ n = n.parentElement;
3442
+ }
3443
+ n = el;
3444
+ while (n.ownerSVGElement) n = n.ownerSVGElement;
3445
+ return n;
3446
+ }
3447
+
3448
+ function pickable(el) {
3449
+ return !!el && el.nodeType === 1 && el !== D.documentElement && el !== D.body &&
3450
+ el !== host && el !== marksHost && el !== agentHost;
3451
+ }
3452
+
3453
+ // CSS.escape where the browser has it; else the same algorithm (CSSOM).
3454
+ // Without it a Tailwind class (\`md:flex\`, \`w-1/2\`) makes an invalid selector.
3455
+ function cssEsc(v) {
3456
+ v = String(v);
3457
+ try { if (W.CSS && typeof W.CSS.escape === 'function') return W.CSS.escape(v); } catch (e) {}
3458
+ var out = '';
3459
+ for (var i = 0; i < v.length; i++) {
3460
+ var ch = v.charAt(i), code = v.charCodeAt(i);
3461
+ if (code === 0) out += '\\uFFFD';
3462
+ else if ((code >= 1 && code <= 31) || code === 127 || (i === 0 && code >= 48 && code <= 57) ||
3463
+ (i === 1 && code >= 48 && code <= 57 && v.charAt(0) === '-')) out += '\\\\' + code.toString(16) + ' ';
3464
+ else if (i === 0 && ch === '-' && v.length === 1) out += '\\\\-';
3465
+ else if (code >= 128 || ch === '-' || ch === '_' || /[0-9A-Za-z]/.test(ch)) out += ch;
3466
+ else out += '\\\\' + ch;
3467
+ }
3468
+ return out;
3469
+ }
3470
+
3471
+ // One step of the path. \`lean\` (tag + position only) is the fallback for
3472
+ // a step that alone would not fit the budget.
3473
+ function step(el, lean) {
3474
+ var t = tagOf(el);
3475
+ if (el.id && !lean) return t + '#' + cssEsc(el.id);
3476
+ var c = lean ? [] : squash(attr(el, 'class')).split(' ').filter(Boolean).slice(0, 2);
3477
+ var s = t;
3478
+ for (var k = 0; k < c.length; k++) s += '.' + cssEsc(c[k]);
3479
+ var p = el.parentElement;
3480
+ if (p && p.children) {
3481
+ var same = 0, index = 0;
3482
+ for (var i = 0; i < p.children.length; i++) {
3483
+ if (p.children[i].tagName === el.tagName) {
3484
+ same++;
3485
+ if (p.children[i] === el) index = same;
3486
+ }
3487
+ }
3488
+ if (same > 1) s += ':nth-of-type(' + index + ')';
3489
+ }
3490
+ return s;
3491
+ }
3492
+
3493
+ // Never cut with an ellipsis (that would be no selector at all): drop the
3494
+ // outermost ancestors until the path fits, so it always matches the element.
3495
+ function selectorFor(el) {
3496
+ var parts = [], n = el;
3497
+ while (n && n.nodeType === 1 && n !== D.body && n !== D.documentElement && parts.length < 4) {
3498
+ parts.unshift(step(n));
3499
+ if (n.id) break;
3500
+ n = n.parentElement;
3501
+ }
3502
+ while (parts.length > 1 && parts.join(' > ').length > 200) parts.shift();
3503
+ if (parts[0].length > 200) parts[0] = step(el, true);
3504
+ return parts.join(' > ');
3505
+ }
3506
+
3507
+ function textOf(el) {
3508
+ return clip(squash(attr(el, 'aria-label') || el.textContent || attr(el, 'placeholder') || attr(el, 'alt') || attr(el, 'title')), TEXT_MAX);
3509
+ }
3510
+
3511
+ function esc(s) { return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/"/g, '&quot;'); }
3512
+
3513
+ // The element's markup within a budget: attributes trimmed, never more than
3514
+ // HTML_MAX characters walked, so a huge container costs nothing.
3515
+ function htmlOf(el) {
3516
+ var out = [], left = HTML_MAX + 1;
3517
+ function put(s) {
3518
+ if (left <= 0) return;
3519
+ out.push(s.length > left ? s.slice(0, left) : s);
3520
+ left -= s.length;
3521
+ }
3522
+ function walk(n, depth) {
3523
+ if (left <= 0) return;
3524
+ if (n.nodeType === 3) {
3525
+ var text = String(n.nodeValue == null ? '' : n.nodeValue).replace(/\\s+/g, ' ');
3526
+ if (text.trim()) put(esc(text));
3527
+ return;
3528
+ }
3529
+ if (n.nodeType !== 1) return;
3530
+ var t = tagOf(n);
3531
+ put('<' + t);
3532
+ var a = n.attributes || [];
3533
+ for (var i = 0; i < a.length; i++) put(' ' + a[i].name + '="' + esc(clip(String(a[i].value), ATTR_MAX)) + '"');
3534
+ put('>');
3535
+ if (VOID[t.toUpperCase()]) return;
3536
+ if (t === 'script' || t === 'style') put('\\u2026');
3537
+ else if (depth >= 4) { if (n.childNodes && n.childNodes.length) put('\\u2026'); }
3538
+ else for (var c = 0; n.childNodes && c < n.childNodes.length && left > 0; c++) walk(n.childNodes[c], depth + 1);
3539
+ put('</' + t + '>');
3540
+ }
3541
+ walk(el, 0);
3542
+ var s = out.join('');
3543
+ return s.length > HTML_MAX ? s.slice(0, HTML_MAX - 1) + '\\u2026' : s;
3544
+ }
3545
+
3546
+ // The React component's file and line, when a dev build exposes them: the
3547
+ // agent gets "components/buy-button.tsx:42" instead of a CSS path.
3548
+ function reactSourceOf(el) {
3549
+ try {
3550
+ for (var key in el) {
3551
+ if (key.indexOf('__reactFiber$') !== 0) continue;
3552
+ var fiber = el[key];
3553
+ while (fiber) {
3554
+ var src = fiber._debugSource;
3555
+ if (src && src.fileName) return { file: String(src.fileName), line: Number(src.lineNumber) || null };
3556
+ fiber = fiber.return;
3557
+ }
3558
+ }
3559
+ } catch (e) {}
3560
+ return null;
3561
+ }
3562
+
3563
+ function describe(el) {
3564
+ var r = el.getBoundingClientRect();
3565
+ var out = {
3566
+ tag: tagOf(el),
3567
+ id: el.id ? clip(String(el.id), 100) : null,
3568
+ classes: clip(squash(attr(el, 'class')), 200),
3569
+ selector: selectorFor(el),
3570
+ text: textOf(el),
3571
+ html: htmlOf(el),
3572
+ rect: { x: Math.round(r.left), y: Math.round(r.top), w: Math.round(r.width), h: Math.round(r.height) }
3573
+ };
3574
+ if (MULTI) out.viewport = { w: W.innerWidth, h: W.innerHeight };
3575
+ if (REACT_SOURCE) out.source = reactSourceOf(el);
3576
+ return out;
3577
+ }
3578
+
3579
+ function kindOf(el) {
3580
+ var t = tagOf(el), role = attr(el, 'role'), type = attr(el, 'type');
3581
+ if (t === 'button' || role === 'button' || (t === 'input' && /^(button|submit|reset)$/.test(type))) return 'button';
3582
+ if (t === 'a' || role === 'link') return 'link';
3583
+ if (t === 'input' || t === 'textarea' || t === 'select') return 'field';
3584
+ if (t === 'img' || t === 'svg' || t === 'picture' || t === 'canvas') return 'image';
3585
+ if (/^h[1-6]$/.test(t)) return 'heading';
3586
+ return t;
3587
+ }
3588
+
3589
+ function label(el) {
3590
+ var text = squash(textOf(el));
3591
+ var kind = kindOf(el);
3592
+ return text ? '\\u201c' + clip(text, 28) + '\\u201d ' + kind : kind;
3593
+ }
3594
+
3595
+ function cursor() {
3596
+ var svg = "<svg xmlns='http://www.w3.org/2000/svg' width='32' height='32'><circle cx='16' cy='16' r='9.5' fill='" + accent + "' fill-opacity='.14' stroke='" + accent + "' stroke-width='2'/><path d='M16 1v7M16 24v7M1 16h7M24 16h7' stroke='" + accent + "' stroke-width='1.6' stroke-linecap='round'/><circle cx='16' cy='16' r='1.6' fill='" + accent + "'/></svg>";
3597
+ return 'url("data:image/svg+xml,' + encodeURIComponent(svg) + '") 16 16, crosshair';
3598
+ }
3599
+
3600
+ function canHole() {
3601
+ try { return !!(W.CSS && W.CSS.supports && W.CSS.supports('clip-path', 'path(evenodd, "M0 0H1V1Z")')); } catch (e) { return false; }
3602
+ }
3603
+
3604
+ // The veil covers the app except a rounded window around the element.
3605
+ function holePath(r) {
3606
+ var w = Math.round(W.innerWidth), h = Math.round(W.innerHeight), p = 6;
3607
+ var x = Math.round(r.left - p), y = Math.round(r.top - p);
3608
+ var rw = Math.round(r.width + 2 * p), rh = Math.round(r.height + 2 * p);
3609
+ var k = Math.max(0, Math.min(12, Math.floor(rw / 2), Math.floor(rh / 2)));
3610
+ var arc = 'A' + k + ' ' + k + ' 0 0 1 ';
3611
+ return 'path(evenodd, "M0 0H' + w + 'V' + h + 'H0Z' +
3612
+ 'M' + (x + k) + ' ' + y + 'H' + (x + rw - k) + arc + (x + rw) + ' ' + (y + k) +
3613
+ 'V' + (y + rh - k) + arc + (x + rw - k) + ' ' + (y + rh) +
3614
+ 'H' + (x + k) + arc + x + ' ' + (y + rh - k) +
3615
+ 'V' + (y + k) + arc + (x + k) + ' ' + y + 'Z")';
3616
+ }
3617
+
3618
+ // The Remix ring and chip, shared by the pointer, the marks and the agent.
3619
+ function ringCss(color) {
3620
+ return 'box-sizing:border-box;border:2px solid ' + color + ';border-radius:12px;box-shadow:0 0 0 5px ' + color + '29';
3621
+ }
3622
+ function chipCss(color) {
3623
+ return 'white-space:nowrap;padding:4px 10px;border-radius:999px;background:#141416;color:#f6f3ee;font:' + CHIP_FONT +
3624
+ ';box-shadow:0 0 0 1px ' + color;
3625
+ }
3626
+
3627
+ // A host for our nodes: a custom element, max z-index, its own closed
3628
+ // shadow root so the page's CSS can't restyle (or hide) what we draw. Last
3629
+ // child of <html>, so at an equal z-index it still paints on top.
3630
+ function makeHost(role, position) {
3631
+ var el = D.createElement('remix-point');
3632
+ if (role === 'point') el.setAttribute('data-remix-point', '');
3633
+ el.setAttribute('data-codeam', role);
3634
+ el.style.cssText = 'all:initial;position:' + position + ';top:0;left:0;width:0;height:0;pointer-events:none;z-index:2147483647';
3635
+ return el;
3636
+ }
3637
+
3638
+ function build() {
3639
+ if (leaving && leaving.parentNode) leaving.parentNode.removeChild(leaving);
3640
+ leaving = null;
3641
+ var motion = reduced() ? 'none' : 'transform .16s ' + EASE + ', width .16s ' + EASE + ', height .16s ' + EASE + ', opacity .16s';
3642
+ host = makeHost('point', 'fixed');
3643
+ var root = shadowOf(host);
3644
+ veil = D.createElement('div');
3645
+ veil.setAttribute('data-codeam', 'veil');
3646
+ veil.style.cssText = 'position:fixed;top:0;left:0;right:0;bottom:0;pointer-events:none;opacity:0;' +
3647
+ 'background:rgba(246,243,238,.46) ' + GRAIN + ';-webkit-backdrop-filter:grayscale(.7);backdrop-filter:grayscale(.7);' +
3648
+ 'transition:' + (reduced() ? 'none' : 'opacity .2s');
3649
+ ring = D.createElement('div');
3650
+ ring.setAttribute('data-codeam', 'ring');
3651
+ ring.style.cssText = 'position:fixed;top:0;left:0;width:0;height:0;pointer-events:none;opacity:0;' +
3652
+ ringCss(accent) + ';transition:' + motion;
3653
+ chip = D.createElement('div');
3654
+ chip.setAttribute('data-codeam', 'chip');
3655
+ chip.style.cssText = 'position:fixed;top:0;left:0;pointer-events:none;opacity:0;' + chipCss(accent) + ';transition:' + motion;
3656
+ root.appendChild(veil);
3657
+ root.appendChild(ring);
3658
+ root.appendChild(chip);
3659
+ D.documentElement.appendChild(host);
3660
+ style = D.createElement('style');
3661
+ style.textContent = 'html[data-remix-pointing],html[data-remix-pointing] *{cursor:' + cursor() + ' !important;' +
3662
+ 'user-select:none !important;-webkit-user-select:none !important;-webkit-touch-callout:none !important}';
3663
+ (D.head || D.documentElement).appendChild(style);
3664
+ D.documentElement.setAttribute('data-remix-pointing', '');
3665
+ }
3666
+
3667
+ function paint(el) {
3668
+ current = el;
3669
+ var r = el.getBoundingClientRect();
3670
+ ring.style.transform = 'translate(' + Math.round(r.left - 4) + 'px,' + Math.round(r.top - 4) + 'px)';
3671
+ ring.style.width = Math.round(r.width + 8) + 'px';
3672
+ ring.style.height = Math.round(r.height + 8) + 'px';
3673
+ ring.style.opacity = '1';
3674
+ if (canHole()) {
3675
+ veil.style.clipPath = holePath(r);
3676
+ veil.style.opacity = '1';
3677
+ }
3678
+ chip.textContent = label(el);
3679
+ var top = r.top >= 34 ? r.top - 32 : r.top + r.height + 8;
3680
+ var left = Math.max(8, Math.min(r.left, W.innerWidth - 200));
3681
+ chip.style.transform = 'translate(' + Math.round(left) + 'px,' + Math.round(top) + 'px)';
3682
+ chip.style.opacity = '1';
3683
+ }
3684
+
3685
+ // Paint, and in multi mode tell the parent what is under the finger.
3686
+ function hoverTo(el) {
3687
+ var changed = el !== current;
3688
+ paint(el);
3689
+ if (changed && MULTI) send('hover', { element: describe(el) });
3690
+ }
3691
+
3692
+ // While pointing, the app never sees the gesture: no click, no focus, no
3693
+ // drag. Touch scrolling still works (its default is left alone).
3694
+ function block(e) {
3695
+ if (e.stopImmediatePropagation) e.stopImmediatePropagation();
3696
+ if (e.cancelable && e.type !== 'touchstart' && e.type !== 'touchmove') e.preventDefault();
3697
+ }
3698
+
3699
+ // What is under the pointer. Hit-testing first (a touch keeps targeting
3700
+ // the element it went down on); the event target where there is none.
3701
+ function at(e) {
3702
+ var el = null;
3703
+ if (D.elementFromPoint) {
3704
+ try { el = D.elementFromPoint(e.clientX, e.clientY); } catch (x) { el = null; }
3705
+ }
3706
+ if (!el && e.target && e.target.nodeType === 1) el = e.target;
3707
+ return pickable(el) ? target(el) : null;
3708
+ }
3709
+
3710
+ function onDown(e) {
3711
+ block(e);
3712
+ down = { id: e.pointerId };
3713
+ tapped = true;
3714
+ var el = at(e);
3715
+ if (el) hoverTo(el);
3716
+ }
3717
+
3718
+ function onMove(e) {
3719
+ block(e);
3720
+ var el = at(e);
3721
+ if (el && el !== current) hoverTo(el);
3722
+ }
3723
+
3724
+ // Browsers without pointer events (old WebViews) only send mouse events.
3725
+ // Never blocked: the app keeps its own mousemove, as it always did.
3726
+ function onMouseMove(e) {
3727
+ var el = at(e);
3728
+ if (el && el !== current) hoverTo(el);
3729
+ }
3730
+
3731
+ function onUp(e) {
3732
+ block(e);
3733
+ var d = down;
3734
+ down = null;
3735
+ if (!d || d.id !== e.pointerId || e.button > 0) return;
3736
+ var el = at(e) || current;
3737
+ if (el) {
3738
+ pick(el);
3739
+ armClickSwallow();
3740
+ }
3741
+ }
3742
+
3743
+ // The click that follows a tap was already picked on pointerup; a click
3744
+ // with no pointer gesture before it (an old WebView) is the pick itself.
3745
+ function onClick(e) {
3746
+ block(e);
3747
+ if (tapped) { tapped = false; return; }
3748
+ var el = at(e);
3749
+ if (el) pick(el);
3750
+ }
3751
+
3752
+ // The browser fires a click after the pointerup that picked. Once pointing
3753
+ // is off (single mode, or the parent turned it off in between) nothing of
3754
+ // ours would catch it, and a pointed-at link would navigate. One shot:
3755
+ // gone on the next click, or after 400 ms when no click comes.
3756
+ function armClickSwallow() {
3757
+ disarmClickSwallow();
3758
+ var fn = function (e) {
3759
+ block(e);
3760
+ disarmClickSwallow();
3761
+ };
3762
+ swallow = fn;
3763
+ W.addEventListener('click', fn, { capture: true, passive: false });
3764
+ W.setTimeout(function () { if (swallow === fn) disarmClickSwallow(); }, 400);
3765
+ }
3766
+
3767
+ function disarmClickSwallow() {
3768
+ if (!swallow) return;
3769
+ W.removeEventListener('click', swallow, { capture: true });
3770
+ swallow = null;
3771
+ }
3772
+
3773
+ // The browser took the touch over to scroll: that was not a tap.
3774
+ function onCancel(e) {
3775
+ block(e);
3776
+ down = null;
3777
+ tapped = false;
3778
+ }
3779
+
3780
+ function onKey(e) {
3781
+ block(e);
3782
+ // In multi mode the parent owns the toggle: Escape ends nothing it can't see.
3783
+ if (!MULTI && e.type === 'keydown' && (e.key === 'Escape' || e.key === 'Esc')) {
3784
+ send('cancel');
3785
+ off(false);
3786
+ }
3787
+ }
3788
+
3789
+ function onScroll() {
3790
+ if (current) paint(current);
3791
+ if (picked.length) drawMarks();
3792
+ }
3793
+
3794
+ var HANDLERS = [
3795
+ ['pointerdown', onDown], ['pointermove', onMove], ['pointerup', onUp], ['pointercancel', onCancel],
3796
+ ['mousedown', block], ['mouseup', block], ['mousemove', onMouseMove], ['click', onClick], ['dblclick', block], ['auxclick', block],
3797
+ ['contextmenu', block], ['touchstart', block], ['touchmove', block], ['touchend', block],
3798
+ ['submit', block], ['keydown', onKey], ['keyup', block], ['keypress', block]
3799
+ ];
3800
+
3801
+ function pick(el) {
3802
+ var element = describe(el);
3803
+ if (!MULTI) {
3804
+ off(true);
3805
+ send('pick', { element: element });
3806
+ return;
3807
+ }
3808
+ // Past the cap the pick still goes up (the app says "4 marks max") but
3809
+ // leaves nothing in the page the app would never be able to remove.
3810
+ if (picked.length < MAX_MARKS) {
3811
+ var id = 'p' + LOAD + '-' + (++seq);
3812
+ picked.push({ id: id, el: el });
3813
+ element.markId = id;
3814
+ drawMarks(id);
3815
+ }
3816
+ send('pick', { element: element });
3817
+ }
3818
+
3819
+ // The pinned marks: Remix rings with a numbered chip, anchored to the
3820
+ // DOCUMENT (absolute + scroll) so they travel with the content.
3821
+ function drawMarks(fresh) {
3822
+ if (!picked.length) {
3823
+ drop(marksHost);
3824
+ marksHost = marksRoot = null;
3825
+ return;
3826
+ }
3827
+ if (!marksHost || !marksHost.parentNode) {
3828
+ marksHost = makeHost('marks', 'absolute');
3829
+ marksRoot = shadowOf(marksHost);
3830
+ D.documentElement.appendChild(marksHost);
3831
+ }
3832
+ clear(marksRoot);
3833
+ var calm = reduced();
3834
+ if (!calm) {
3835
+ var keys = D.createElement('style');
3836
+ keys.textContent = '@keyframes pp-pin{from{opacity:0;transform:scale(1.04)}to{opacity:1;transform:scale(1)}}';
3837
+ marksRoot.appendChild(keys);
3838
+ }
3839
+ var sx = W.scrollX || W.pageXOffset || 0, sy = W.scrollY || W.pageYOffset || 0;
3840
+ for (var i = 0; i < picked.length; i++) {
3841
+ var r = picked[i].el.getBoundingClientRect();
3842
+ var box = D.createElement('div');
3843
+ box.setAttribute('data-codeam', 'mark');
3844
+ box.style.cssText = 'position:absolute;pointer-events:none;' + ringCss(accent) +
3845
+ ';left:' + Math.round(r.left + sx - 4) + 'px;top:' + Math.round(r.top + sy - 4) + 'px' +
3846
+ ';width:' + Math.round(r.width + 8) + 'px;height:' + Math.round(r.height + 8) + 'px' +
3847
+ (!calm && picked[i].id === fresh ? ';animation:pp-pin .32s ' + EASE : '');
3848
+ var badge = D.createElement('div');
3849
+ badge.setAttribute('data-codeam', 'mark-badge');
3850
+ badge.style.cssText = 'position:absolute;left:-11px;top:-11px;width:22px;height:22px;border-radius:11px;' +
3851
+ 'background:#141416;color:#f6f3ee;font:italic 600 12px/22px Georgia,"Times New Roman",serif;text-align:center;' +
3852
+ 'box-shadow:0 0 0 1px ' + accent + ';pointer-events:none';
3853
+ badge.textContent = String(i + 1);
3854
+ box.appendChild(badge);
3855
+ marksRoot.appendChild(box);
3856
+ }
3857
+ }
3858
+
3859
+ // The parent sends the ids still alive, in order: a removed chip removes
3860
+ // its mark, and the rest renumber (what the person writes, "1 is off",
3861
+ // refers to these numbers).
3862
+ function syncMarks(ids) {
3863
+ var byId = {};
3864
+ for (var i = 0; i < picked.length; i++) byId[picked[i].id] = picked[i];
3865
+ var next = [];
3866
+ for (var j = 0; j < ids.length; j++) if (byId[ids[j]]) next.push(byId[ids[j]]);
3867
+ picked = next;
3868
+ drawMarks();
3869
+ }
3870
+
3871
+ // The agent pointing (highlight_element): a cursor travels to the element,
3872
+ // centres it, and leaves a cyan Remix ring with its label. Independent of
3873
+ // pointing mode, anchored to the document like the marks.
3874
+ function ensureAgent() {
3875
+ if (agentHost && agentHost.parentNode) return;
3876
+ agentHost = makeHost('agent', 'absolute');
3877
+ agentRoot = shadowOf(agentHost);
3878
+ var keys = D.createElement('style');
3879
+ keys.textContent =
3880
+ '@keyframes pp-agent-pulse{0%{box-shadow:0 0 0 0 rgba(34,211,238,.55)}70%{box-shadow:0 0 0 10px rgba(34,211,238,0)}100%{box-shadow:0 0 0 5px rgba(34,211,238,.16)}}' +
3881
+ '@keyframes pp-agent-in{from{opacity:0;transform:scale(1.04)}to{opacity:1;transform:scale(1)}}';
3882
+ agentRoot.appendChild(keys);
3883
+ agentLayer = D.createElement('div');
3884
+ agentRoot.appendChild(agentLayer);
3885
+ D.documentElement.appendChild(agentHost);
3886
+ }
3887
+
3888
+ function ensureAgentCursor() {
3889
+ if (agentCursor && agentCursor.parentNode) return agentCursor;
3890
+ agentCursor = D.createElement('div');
3891
+ agentCursor.setAttribute('data-codeam', 'agent-cursor');
3892
+ agentCursor.style.cssText = 'position:fixed;pointer-events:none;opacity:0;' +
3893
+ 'left:' + Math.round(W.innerWidth / 2) + 'px;top:' + Math.round(W.innerHeight - 40) + 'px;' +
3894
+ 'transition:' + (reduced() ? 'none' : 'left .6s cubic-bezier(.22,1,.36,1),top .6s cubic-bezier(.22,1,.36,1),opacity .3s');
3895
+ agentCursor.innerHTML =
3896
+ '<svg width="22" height="22" viewBox="0 0 24 24" style="display:block;filter:drop-shadow(0 1px 2px rgba(0,0,0,.5))">' +
3897
+ '<path d="M4 2l15 8.5-6.6 1.6L9 18.5z" fill="' + AGENT_ACCENT + '" stroke="#141416" stroke-width="1.2"/></svg>' +
3898
+ '<div style="margin:2px 0 0 14px;display:inline-block;' + chipCss(AGENT_ACCENT) + ';padding:2px 8px;font-size:11px">Agent</div>';
3899
+ agentRoot.appendChild(agentCursor);
3900
+ return agentCursor;
3901
+ }
3902
+
3903
+ function drawAgentMarks() {
3904
+ if (!agentLayer) return;
3905
+ clear(agentLayer);
3906
+ var calm = reduced();
3907
+ var sx = W.scrollX || W.pageXOffset || 0, sy = W.scrollY || W.pageYOffset || 0;
3908
+ for (var i = 0; i < agentMarks.length; i++) {
3909
+ var m = agentMarks[i];
3910
+ if (m.el.isConnected === false) continue;
3911
+ var r = m.el.getBoundingClientRect();
3912
+ var box = D.createElement('div');
3913
+ box.setAttribute('data-codeam', 'agent-mark');
3914
+ box.style.cssText = 'position:absolute;pointer-events:none;' + ringCss(AGENT_ACCENT) +
3915
+ ';left:' + Math.round(r.left + sx - 4) + 'px;top:' + Math.round(r.top + sy - 4) + 'px' +
3916
+ ';width:' + Math.round(r.width + 8) + 'px;height:' + Math.round(r.height + 8) + 'px' +
3917
+ (calm ? '' : ';animation:pp-agent-in .25s ' + EASE + ',pp-agent-pulse 1.6s ease-out 3');
3918
+ if (m.label) {
3919
+ var tag = D.createElement('div');
3920
+ tag.setAttribute('data-codeam', 'agent-mark-label');
3921
+ // Above the element when it fits, else below \u2014 same rule as the chip.
3922
+ tag.style.cssText = 'position:absolute;left:0;' + (r.top >= 34 ? 'top:-30px' : 'top:' + Math.round(r.height + 12) + 'px') +
3923
+ ';pointer-events:none;' + chipCss(AGENT_ACCENT) + ';max-width:260px;overflow:hidden;text-overflow:ellipsis';
3924
+ tag.textContent = m.label;
3925
+ box.appendChild(tag);
3926
+ }
3927
+ agentLayer.appendChild(box);
3928
+ }
3929
+ }
3930
+
3931
+ function listenAgentLayout() {
3932
+ if (agentListening) return;
3933
+ agentListening = true;
3934
+ W.addEventListener('scroll', drawAgentMarks, true);
3935
+ W.addEventListener('resize', drawAgentMarks);
3936
+ }
3937
+
3938
+ function agentHighlight(selector, text) {
3939
+ var el = null;
3940
+ try { el = D.querySelector(String(selector)); } catch (e) { el = null; }
3941
+ if (!el || !el.getBoundingClientRect) return false;
3942
+ ensureAgent();
3943
+ listenAgentLayout();
3944
+ var calm = reduced();
3945
+ try { el.scrollIntoView({ block: 'center', inline: 'nearest', behavior: calm ? 'auto' : 'smooth' }); } catch (e) {}
3946
+ var pointer = ensureAgentCursor();
3947
+ var owner = agentHost;
3948
+ // After the smooth scroll the cursor travels to the element and, on
3949
+ // arrival, the ring appears: read as "the agent is pointing at this".
3950
+ W.setTimeout(function () {
3951
+ if (agentHost !== owner) return;
3952
+ var r = el.getBoundingClientRect();
3953
+ pointer.style.opacity = '1';
3954
+ pointer.style.left = Math.round(r.left + Math.min(r.width / 2, 40)) + 'px';
3955
+ pointer.style.top = Math.round(r.top + Math.min(r.height / 2, 24)) + 'px';
3956
+ W.setTimeout(function () {
3957
+ if (agentHost !== owner) return;
3958
+ agentMarks.push({ el: el, label: text ? String(text).slice(0, 80) : '' });
3959
+ drawAgentMarks();
3960
+ }, 620);
3961
+ }, 380);
3962
+ return true;
3963
+ }
3964
+
3965
+ function agentClear() {
3966
+ agentMarks = [];
3967
+ drop(agentHost);
3968
+ agentHost = agentRoot = agentLayer = agentCursor = null;
3969
+ if (agentListening) {
3970
+ W.removeEventListener('scroll', drawAgentMarks, true);
3971
+ W.removeEventListener('resize', drawAgentMarks);
3972
+ agentListening = false;
3973
+ }
3974
+ }
3975
+
3976
+ function enable(origin, a) {
3977
+ driver = origin;
3978
+ accent = typeof a === 'string' && ACCENT_RE.test(a) ? a : DEFAULT_ACCENT;
3979
+ if (!on) {
3980
+ on = true;
3981
+ build();
3982
+ for (var i = 0; i < HANDLERS.length; i++) {
3983
+ W.addEventListener(HANDLERS[i][0], HANDLERS[i][1], { capture: true, passive: false });
3984
+ bound.push(HANDLERS[i]);
3985
+ }
3986
+ W.addEventListener('scroll', onScroll, true);
3987
+ W.addEventListener('resize', onScroll, true);
3988
+ }
3989
+ send(MULTI ? 'ready' : 'on');
3990
+ }
3991
+
3992
+ function off(pinned) {
3993
+ if (!on) return;
3994
+ on = false;
3995
+ while (bound.length) {
3996
+ var h = bound.pop();
3997
+ W.removeEventListener(h[0], h[1], { capture: true });
3998
+ }
3999
+ W.removeEventListener('scroll', onScroll, true);
4000
+ W.removeEventListener('resize', onScroll, true);
4001
+ D.documentElement.removeAttribute('data-remix-pointing');
4002
+ if (style && style.parentNode) style.parentNode.removeChild(style);
4003
+ var node = host;
4004
+ if (pinned && node && !reduced()) {
4005
+ // A short "pinned" beat before the ring goes, so the tap feels taken.
4006
+ ring.style.transition = 'transform .32s ' + EASE + ', opacity .32s';
4007
+ ring.style.transform += ' scale(1.04)';
4008
+ ring.style.opacity = '0';
4009
+ veil.style.opacity = '0';
4010
+ chip.style.opacity = '0';
4011
+ leaving = node;
4012
+ W.setTimeout(function () {
4013
+ if (node.parentNode) node.parentNode.removeChild(node);
4014
+ if (leaving === node) leaving = null;
4015
+ }, 320);
4016
+ } else if (node && node.parentNode) {
4017
+ node.parentNode.removeChild(node);
4018
+ }
4019
+ host = veil = ring = chip = style = null;
4020
+ current = null;
4021
+ down = null;
4022
+ tapped = false;
4023
+ }
4024
+
4025
+ W.addEventListener('message', function (e) {
4026
+ if (ALLOWED.indexOf(e.origin) === -1 || e.source !== W.parent) return;
4027
+ var d = e.data;
4028
+ if (!d || typeof d !== 'object' || d[FIELD] !== CH) return;
4029
+ if (d.type === 'enable') enable(e.origin, d.accent);
4030
+ else if (d.type === 'disable' && e.origin === driver) off(false);
4031
+ else if (MULTI && d.type === 'marks' && d.ids && d.ids.length !== undefined) syncMarks(d.ids);
4032
+ else if (AGENT && d.type === 'agent-highlight' && d.selector) agentHighlight(d.selector, d.label);
4033
+ else if (AGENT && d.type === 'agent-clear') agentClear();
4034
+ });
4035
+
4036
+ if (!MULTI) {
4037
+ try {
4038
+ Object.defineProperty(W, '__remixPoint', {
4039
+ value: Object.freeze({ version: 1, active: function () { return on; } })
4040
+ });
4041
+ } catch (e) {}
4042
+ }
4043
+
4044
+ // The React Native door. Only where the bridge exists: on a normal page it
4045
+ // would be a global that turns the picker on with nobody's permission.
4046
+ if (GLOBAL && rnBridge()) {
4047
+ W[GLOBAL] = {
4048
+ enable: function () { enable('react-native'); },
4049
+ disable: function () { off(false); },
4050
+ marks: function (ids) { if (MULTI && ids && ids.length !== undefined) syncMarks(ids); },
4051
+ highlight: function (selector, text) { return AGENT ? agentHighlight(selector, text) : false; },
4052
+ clear: function () { if (AGENT) agentClear(); }
4053
+ };
4054
+ }
4055
+ } catch (e) {
4056
+ // Whatever happens, the person's app keeps working.
4057
+ }
4058
+ })();`;
4059
+ }
3339
4060
  // Annotate the CommonJS export names for ESM import in node:
3340
4061
  0 && (module.exports = {
3341
4062
  AGENT_REGISTRY,
@@ -3381,6 +4102,8 @@ var DEFAULT_FEATURE_GATES = {
3381
4102
  PACK_STATUS_COMMAND,
3382
4103
  PACK_WORKFLOW_ARTICLE,
3383
4104
  PLUGIN_AGENT_STATUSES,
4105
+ POINT_HTML_MAX,
4106
+ POINT_TEXT_MAX,
3384
4107
  PREVIEW_DETECT_PROMPT,
3385
4108
  PROTOCOL_VERSION,
3386
4109
  PUBLIC_TO_INTERNAL,
@@ -3436,6 +4159,7 @@ var DEFAULT_FEATURE_GATES = {
3436
4159
  normalizeGuardrailPolicy,
3437
4160
  orderDeployChoices,
3438
4161
  parsePackFindings,
4162
+ pointPickerScript,
3439
4163
  publicToInternal,
3440
4164
  recommendForDeps,
3441
4165
  renderToLines,