@integrity-labs/agt-cli 0.28.972 → 0.28.974

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.
@@ -20,7 +20,7 @@ import {
20
20
  rotateDailySession,
21
21
  sessionFileExists,
22
22
  todayLocalIso
23
- } from "./chunk-O3VCRRUF.js";
23
+ } from "./chunk-HRHDATT5.js";
24
24
  import {
25
25
  classifyUnanswerablePane,
26
26
  findUsageLimitResetHint,
@@ -5575,4 +5575,4 @@ export {
5575
5575
  stopAllSessionsAndWait,
5576
5576
  getProjectDir
5577
5577
  };
5578
- //# sourceMappingURL=chunk-J7J3JWZ3.js.map
5578
+ //# sourceMappingURL=chunk-PHPOHMSK.js.map
@@ -236,7 +236,7 @@ async function spawnPairSession(session) {
236
236
  return { ok: true };
237
237
  } catch {
238
238
  }
239
- const { resolveClaudeBinary } = await import("./persistent-session-24ZIVLH3.js");
239
+ const { resolveClaudeBinary } = await import("./persistent-session-YJJXZSUE.js");
240
240
  const claudeBin = resolveClaudeBinary();
241
241
  const pairEnv = {
242
242
  ...process.env,
@@ -558,4 +558,4 @@ export {
558
558
  startClaudePair,
559
559
  submitClaudePairCode
560
560
  };
561
- //# sourceMappingURL=claude-pair-runtime-JT5WNC3G.js.map
561
+ //# sourceMappingURL=claude-pair-runtime-U6GBR7KM.js.map
@@ -63,7 +63,7 @@ import {
63
63
  safeWriteJsonAtomic,
64
64
  setConfigHash,
65
65
  tripClass
66
- } from "../chunk-TJXGP5XV.js";
66
+ } from "../chunk-3PYNSD4I.js";
67
67
  import {
68
68
  getProjectDir as getProjectDir2,
69
69
  getReadyTasks,
@@ -140,7 +140,7 @@ import {
140
140
  takeZombieDetection,
141
141
  toOpencodeModel,
142
142
  writeEgressAllowlist
143
- } from "../chunk-J7J3JWZ3.js";
143
+ } from "../chunk-PHPOHMSK.js";
144
144
  import {
145
145
  ACCOUNT_ENFORCEMENT_MARKER_FILENAME,
146
146
  AnchorSessionClient,
@@ -238,7 +238,7 @@ import {
238
238
  subagentActivityAgeSeconds,
239
239
  sumTranscriptUsageInWindow,
240
240
  transcriptActivityAgeSeconds
241
- } from "../chunk-O3VCRRUF.js";
241
+ } from "../chunk-HRHDATT5.js";
242
242
  import "../chunk-DHWNVVX4.js";
243
243
  import {
244
244
  reapOrphanChannelMcps
@@ -13182,7 +13182,7 @@ var pendingDayRolloverReset = /* @__PURE__ */ new Set();
13182
13182
  var dayRolloverInboundHold = /* @__PURE__ */ new Map();
13183
13183
  var INBOUND_HOLD_EPISODE_GAP_MS = 12e4;
13184
13184
  async function channelInboundActivityAgeSecondsFor(codeName) {
13185
- const { newestPendingInboundActivityMtimeMs } = await import("../responsiveness-probe-7KOUL2XK.js");
13185
+ const { newestPendingInboundActivityMtimeMs } = await import("../responsiveness-probe-SQZGSNVI.js");
13186
13186
  const newest = newestPendingInboundActivityMtimeMs(dirname11(paneLogPath(codeName)));
13187
13187
  if (newest === null) return null;
13188
13188
  return Math.max(0, Math.floor((Date.now() - newest) / 1e3));
@@ -13867,7 +13867,7 @@ var agentRestartTimezoneInputs = /* @__PURE__ */ new Map();
13867
13867
  var lastVersionCheckAt = 0;
13868
13868
  var VERSION_CHECK_INTERVAL_MS = 5 * 60 * 1e3;
13869
13869
  var lastResponsivenessProbeAt = 0;
13870
- var agtCliVersion = true ? "0.28.972" : "dev";
13870
+ var agtCliVersion = true ? "0.28.974" : "dev";
13871
13871
  function resolveBrewPath(execFileSync2) {
13872
13872
  try {
13873
13873
  const out = execFileSync2("which", ["brew"], { timeout: 5e3 }).toString().trim();
@@ -15469,7 +15469,7 @@ function flushRestartedAgentDiagnostics(hostId, codeNames) {
15469
15469
  if (codeNames.length === 0) return;
15470
15470
  void (async () => {
15471
15471
  try {
15472
- const { collectDiagnostics } = await import("../persistent-session-24ZIVLH3.js");
15472
+ const { collectDiagnostics } = await import("../persistent-session-YJJXZSUE.js");
15473
15473
  await api.post("/host/heartbeat", {
15474
15474
  host_id: hostId,
15475
15475
  agent_diagnostics: collectDiagnostics(codeNames, quarantineEntriesFor, claudeMdSizeFor, spawnOutcomeForDiagnostics, pidPressureFor, forwardedToolsFor)
@@ -15611,7 +15611,7 @@ async function pollCycleInner() {
15611
15611
  }
15612
15612
  try {
15613
15613
  const { detectHostSecurity } = await import("../host-security-6PDFG7F5.js");
15614
- const { collectDiagnostics } = await import("../persistent-session-24ZIVLH3.js");
15614
+ const { collectDiagnostics } = await import("../persistent-session-YJJXZSUE.js");
15615
15615
  const diagCodeNames = [...agentState.persistentSessionAgents];
15616
15616
  const agentDiagnostics = diagCodeNames.length > 0 ? collectDiagnostics(diagCodeNames, quarantineEntriesFor, claudeMdSizeFor, spawnOutcomeForDiagnostics, pidPressureFor, forwardedToolsFor) : void 0;
15617
15617
  let tailscaleHostname;
@@ -15787,7 +15787,7 @@ async function pollCycleInner() {
15787
15787
  collectPanelessActivityProbes,
15788
15788
  getResponsivenessIntervalMs,
15789
15789
  occupancyQualificationClassifications
15790
- } = await import("../responsiveness-probe-7KOUL2XK.js");
15790
+ } = await import("../responsiveness-probe-SQZGSNVI.js");
15791
15791
  const probeIntervalMs = getResponsivenessIntervalMs();
15792
15792
  if (now - lastResponsivenessProbeAt > probeIntervalMs) {
15793
15793
  const probeCodeNames = [...agentState.persistentSessionAgents];
@@ -15904,7 +15904,7 @@ async function pollCycleInner() {
15904
15904
  collectResponsivenessProbes,
15905
15905
  livePendingInboundOldestAgeSeconds,
15906
15906
  parkPendingInbound
15907
- } = await import("../responsiveness-probe-7KOUL2XK.js");
15907
+ } = await import("../responsiveness-probe-SQZGSNVI.js");
15908
15908
  const { getProjectDir: wedgeProjectDir } = await import("../scheduler-engine-NDP36U7O.js");
15909
15909
  const wedgeNow = /* @__PURE__ */ new Date();
15910
15910
  const liveAgents = agentState.persistentSessionAgents;
@@ -16060,7 +16060,7 @@ async function pollCycleInner() {
16060
16060
  }
16061
16061
  try {
16062
16062
  const { scrapeMcpFailedBannerCount } = await import("../pane-mcp-banner-scraper-JA437JIB.js");
16063
- const { probeSessionAuth } = await import("../session-auth-dead-BQVFVWZM.js");
16063
+ const { probeSessionAuth } = await import("../session-auth-dead-7OLNDHJC.js");
16064
16064
  const observations = [];
16065
16065
  const pendingCacheCommits = [];
16066
16066
  const modelApiErrorReportingOn = hostFlagStore().getBoolean("model-api-error-reporting");
@@ -20336,7 +20336,7 @@ async function handleRestartDoorbell(agentId, requestedAt, restartReason) {
20336
20336
  try {
20337
20337
  const freshId = rotateSessionForWedge(codeName);
20338
20338
  consecutiveWedgeCycles.delete(codeName);
20339
- const { parkPendingInbound: parkForRestart } = await import("../responsiveness-probe-7KOUL2XK.js");
20339
+ const { parkPendingInbound: parkForRestart } = await import("../responsiveness-probe-SQZGSNVI.js");
20340
20340
  const { parked, deadLettered } = parkForRestart(codeName, /* @__PURE__ */ new Date());
20341
20341
  const parkNote = parked > 0 ? `, ${parked} inbound parked` : "";
20342
20342
  const deadNote = deadLettered > 0 ? `, ${deadLettered} undeliverable dead-lettered` : "";
@@ -20379,7 +20379,7 @@ async function handleRestartDoorbell(agentId, requestedAt, restartReason) {
20379
20379
  void api.post("/host/restart-ack", { host_id: hostId, agent_id: agentId, restart_requested_at: requestedAt }).catch((err) => log(`[restart-lane] ack failed for '${codeName}': ${err.message}`));
20380
20380
  void (async () => {
20381
20381
  try {
20382
- const { collectDiagnostics } = await import("../persistent-session-24ZIVLH3.js");
20382
+ const { collectDiagnostics } = await import("../persistent-session-YJJXZSUE.js");
20383
20383
  await api.post("/host/heartbeat", {
20384
20384
  host_id: hostId,
20385
20385
  agent_diagnostics: collectDiagnostics([codeName], quarantineEntriesFor, claudeMdSizeFor, spawnOutcomeForDiagnostics, pidPressureFor, forwardedToolsFor)
@@ -20430,7 +20430,7 @@ async function respawnAgentAfterMcpStop(codeName, reason) {
20430
20430
  }
20431
20431
  try {
20432
20432
  const hostId = await getHostId();
20433
- const { collectDiagnostics } = await import("../persistent-session-24ZIVLH3.js");
20433
+ const { collectDiagnostics } = await import("../persistent-session-YJJXZSUE.js");
20434
20434
  await api.post("/host/heartbeat", {
20435
20435
  host_id: hostId,
20436
20436
  agent_diagnostics: collectDiagnostics([codeName], quarantineEntriesFor, claudeMdSizeFor, spawnOutcomeForDiagnostics, pidPressureFor, forwardedToolsFor)
@@ -21072,7 +21072,7 @@ async function processClaudePairSessions(agents) {
21072
21072
  killPairSession,
21073
21073
  pairTmuxSession,
21074
21074
  finalizeClaudePairOnboarding
21075
- } = await import("../claude-pair-runtime-JT5WNC3G.js");
21075
+ } = await import("../claude-pair-runtime-U6GBR7KM.js");
21076
21076
  for (const pairId of pendingResp.cancelled_pair_ids ?? []) {
21077
21077
  log(`[claude-pair] sweeping orphan tmux session for pair ${pairId.slice(0, 8)}`);
21078
21078
  const killed = await killPairSession(pairTmuxSession(pairId));
@@ -32068,21 +32068,80 @@ var MARKUP_BRIDGE_SCRIPT = `<script>(function(){
32068
32068
  // ancestors too - a wider style mutation than intended, for a probe whose
32069
32069
  // result the caller then rejects anyway.
32070
32070
  function textNodeAtPointThrough(x,y,target){
32071
+ return throughOverlays(x,y,target,function(){ return textNodeAtPoint(x,y); });
32072
+ }
32073
+ // Run probe() with everything stacked IN FRONT of target muted for pointer
32074
+ // hit-testing, then restore it. Shared by the text-node probe above and the
32075
+ // caret placement below (ENG-10632), so there is one overlay-muting path.
32076
+ // A target that is not in the hit stack (or no elementsFromPoint at all) runs
32077
+ // the probe unmuted, for the reason given on textNodeAtPointThrough.
32078
+ function throughOverlays(x,y,target,probe){
32071
32079
  var stack;
32072
- try{ stack=document.elementsFromPoint(x,y)||[]; }catch(e){ return textNodeAtPoint(x,y); }
32080
+ try{ stack=document.elementsFromPoint(x,y)||[]; }catch(e){ return probe(); }
32073
32081
  var cut=-1, i;
32074
- for(i=0;i<stack.length;i++){ if(stack[i]===target){ cut=i; break; } }
32075
- if(cut<0) return textNodeAtPoint(x,y);
32082
+ for(i=0;i<stack.length;i++){ if(stack[i]===target || (target && target.contains && stack[i] && stack[i].nodeType && target.contains(stack[i]))){ cut=i; break; } }
32083
+ if(cut<0) return probe();
32076
32084
  var muted=[];
32077
32085
  for(i=0;i<cut;i++){
32078
32086
  muted.push([stack[i], stack[i].style ? stack[i].style.pointerEvents : '']);
32079
32087
  try{ stack[i].style.pointerEvents='none'; }catch(e){}
32080
32088
  }
32081
- try{ return textNodeAtPoint(x,y); }
32089
+ try{ return probe(); }
32082
32090
  finally{
32083
32091
  for(i=0;i<muted.length;i++){ try{ muted[i][0].style.pointerEvents=muted[i][1]||''; }catch(e){} }
32084
32092
  }
32085
32093
  }
32094
+ // ENG-10632: is this pointer event aimed INSIDE the open editor?
32095
+ //
32096
+ // 'direct' - the event's own target is the editor or inside it (no overlay).
32097
+ // 'through' - the target is something laid over the editor (a deck click
32098
+ // zone), but the editor is in the hit stack under the pointer.
32099
+ // null - no edit is open, or the press is genuinely elsewhere.
32100
+ //
32101
+ // WHY NOT e.target ALONE. That is exactly the ENG-8477 blind spot: a pointer
32102
+ // event targets the HIT-TEST result, and a deck's full-viewport zones win the
32103
+ // hit test, so e.target is the zone for every press on slide text. ENG-8477
32104
+ // fixed the click that OPENS an editor this way and left the guards that
32105
+ // protect an OPEN editor keyed on e.target, so the second click - the one that
32106
+ // places the caret - was handed to the zone and paged the slide.
32107
+ //
32108
+ // A keydown is different: it targets the FOCUSED element, so the ENG-8500 key
32109
+ // guard's e.target test is correct and stays as it is.
32110
+ function pointInEditor(e){
32111
+ if(!editing || !e) return null;
32112
+ var t=e.target;
32113
+ if(t===editing || (editing.contains && t && t.nodeType && editing.contains(t))) return 'direct';
32114
+ if(typeof e.clientX!=='number' || typeof e.clientY!=='number') return null;
32115
+ var stack;
32116
+ try{ stack=document.elementsFromPoint(e.clientX, e.clientY)||[]; }catch(err){ return null; }
32117
+ for(var i=0;i<stack.length;i++){
32118
+ var n=stack[i];
32119
+ if(n===editing || (editing.contains && n && n.nodeType && editing.contains(n))) return 'through';
32120
+ }
32121
+ return null;
32122
+ }
32123
+ // ENG-10632: put the caret where the viewer pressed, as the browser would have
32124
+ // if nothing covered the editor. The browser's own caret placement goes to the
32125
+ // topmost element, which is the overlay, so it places nothing in the text.
32126
+ // Returns true when a caret was placed inside the editor.
32127
+ function placeCaretAtPointThrough(x,y,el){
32128
+ return throughOverlays(x,y,el,function(){
32129
+ try{
32130
+ var node=null, offset=0, r=null;
32131
+ if(document.caretRangeFromPoint){
32132
+ r=document.caretRangeFromPoint(x,y);
32133
+ if(r){ node=r.startContainer; offset=r.startOffset||0; }
32134
+ } else if(document.caretPositionFromPoint){
32135
+ var p=document.caretPositionFromPoint(x,y);
32136
+ if(p){ node=p.offsetNode; offset=p.offset||0; }
32137
+ }
32138
+ if(!node || !(node===el || (el.contains && el.contains(node.nodeType===3 ? node.parentNode||node.parentElement : node)))) return false;
32139
+ if(!r){ r=document.createRange(); r.setStart(node, offset); r.collapse(true); }
32140
+ var s=document.getSelection(); s.removeAllRanges(); s.addRange(r);
32141
+ return true;
32142
+ }catch(err){ return false; }
32143
+ });
32144
+ }
32086
32145
  // Index of a text node among its parent's child TEXT nodes, in order - the same
32087
32146
  // counting the server uses to re-locate it in source_html (ENG-6856).
32088
32147
  function textIndexOf(container, tn){
@@ -32090,6 +32149,13 @@ var MARKUP_BRIDGE_SCRIPT = `<script>(function(){
32090
32149
  for(var k=0;k<ns.length;k++){ if(ns[k].nodeType===3){ i++; if(ns[k]===tn) return i; } }
32091
32150
  return -1;
32092
32151
  }
32152
+ function keepCaretIn(el){
32153
+ try{
32154
+ var s=document.getSelection(), n=s && s.rangeCount ? s.anchorNode : null;
32155
+ if(n && (n===el || (el.contains && el.contains(n.nodeType===3 ? n.parentNode : n)))) return;
32156
+ }catch(err){}
32157
+ placeCaretEnd(el);
32158
+ }
32093
32159
  function placeCaretEnd(el){ try{ var r=document.createRange(); r.selectNodeContents(el); r.collapse(false); var s=document.getSelection(); s.removeAllRanges(); s.addRange(r); }catch(e){} }
32094
32160
  // The Save/Cancel toolbar anchored above el. Shared by whole-element edits
32095
32161
  // (startEdit) and per-text-node edits (startEditTextNode).
@@ -32299,9 +32365,35 @@ var MARKUP_BRIDGE_SCRIPT = `<script>(function(){
32299
32365
  // press that lands inside the editor. A press anywhere else is the viewer
32300
32366
  // leaving, and the artefact keeps it.
32301
32367
  window.addEventListener('pointerdown', function(e){
32302
- if(!editing) return;
32303
- var t=e.target;
32304
- if(t!==editing && !(editing.contains && t && t.nodeType && editing.contains(t))) return;
32368
+ // ENG-10632: "inside the editor" is decided through the hit stack, not from
32369
+ // e.target, which a deck's click zone always wins. See pointInEditor.
32370
+ var where=pointInEditor(e);
32371
+ if(!where) return;
32372
+ e.stopImmediatePropagation();
32373
+ if(where==='through'){
32374
+ // The overlay is the browser's hit target, so its default action would
32375
+ // move focus off the editor and place no caret in the text. Take both over.
32376
+ try{ e.preventDefault(); }catch(err){}
32377
+ try{ editing.focus(); }catch(err){}
32378
+ // The press is suppressed either way, so a probe that cannot resolve a
32379
+ // point inside the editor must not leave it without a caret: keep one the
32380
+ // editor already holds, otherwise put it at the end of the text.
32381
+ if(!placeCaretAtPointThrough(e.clientX, e.clientY, editing)) keepCaretIn(editing);
32382
+ }
32383
+ }, true);
32384
+ // ENG-10632: the same press also arrives as mousedown and click, and the deck
32385
+ // can page on either. Only a press that went THROUGH an overlay into the open
32386
+ // editor is swallowed; a direct press keeps its native behaviour (caret,
32387
+ // drag-select), and a press anywhere else is the viewer leaving and is left to
32388
+ // the artefact.
32389
+ window.addEventListener('mousedown', function(e){
32390
+ if(pointInEditor(e)!=='through') return;
32391
+ try{ e.preventDefault(); }catch(err){}
32392
+ e.stopImmediatePropagation();
32393
+ }, true);
32394
+ window.addEventListener('click', function(e){
32395
+ if(pointInEditor(e)!=='through') return;
32396
+ try{ e.preventDefault(); }catch(err){}
32305
32397
  e.stopImmediatePropagation();
32306
32398
  }, true);
32307
32399
  // ENG-8477: the hover affordance has the same blind spot as the click did -
package/dist/mcp/index.js CHANGED
@@ -24656,21 +24656,80 @@ var MARKUP_BRIDGE_SCRIPT = `<script>(function(){
24656
24656
  // ancestors too - a wider style mutation than intended, for a probe whose
24657
24657
  // result the caller then rejects anyway.
24658
24658
  function textNodeAtPointThrough(x,y,target){
24659
+ return throughOverlays(x,y,target,function(){ return textNodeAtPoint(x,y); });
24660
+ }
24661
+ // Run probe() with everything stacked IN FRONT of target muted for pointer
24662
+ // hit-testing, then restore it. Shared by the text-node probe above and the
24663
+ // caret placement below (ENG-10632), so there is one overlay-muting path.
24664
+ // A target that is not in the hit stack (or no elementsFromPoint at all) runs
24665
+ // the probe unmuted, for the reason given on textNodeAtPointThrough.
24666
+ function throughOverlays(x,y,target,probe){
24659
24667
  var stack;
24660
- try{ stack=document.elementsFromPoint(x,y)||[]; }catch(e){ return textNodeAtPoint(x,y); }
24668
+ try{ stack=document.elementsFromPoint(x,y)||[]; }catch(e){ return probe(); }
24661
24669
  var cut=-1, i;
24662
- for(i=0;i<stack.length;i++){ if(stack[i]===target){ cut=i; break; } }
24663
- if(cut<0) return textNodeAtPoint(x,y);
24670
+ for(i=0;i<stack.length;i++){ if(stack[i]===target || (target && target.contains && stack[i] && stack[i].nodeType && target.contains(stack[i]))){ cut=i; break; } }
24671
+ if(cut<0) return probe();
24664
24672
  var muted=[];
24665
24673
  for(i=0;i<cut;i++){
24666
24674
  muted.push([stack[i], stack[i].style ? stack[i].style.pointerEvents : '']);
24667
24675
  try{ stack[i].style.pointerEvents='none'; }catch(e){}
24668
24676
  }
24669
- try{ return textNodeAtPoint(x,y); }
24677
+ try{ return probe(); }
24670
24678
  finally{
24671
24679
  for(i=0;i<muted.length;i++){ try{ muted[i][0].style.pointerEvents=muted[i][1]||''; }catch(e){} }
24672
24680
  }
24673
24681
  }
24682
+ // ENG-10632: is this pointer event aimed INSIDE the open editor?
24683
+ //
24684
+ // 'direct' - the event's own target is the editor or inside it (no overlay).
24685
+ // 'through' - the target is something laid over the editor (a deck click
24686
+ // zone), but the editor is in the hit stack under the pointer.
24687
+ // null - no edit is open, or the press is genuinely elsewhere.
24688
+ //
24689
+ // WHY NOT e.target ALONE. That is exactly the ENG-8477 blind spot: a pointer
24690
+ // event targets the HIT-TEST result, and a deck's full-viewport zones win the
24691
+ // hit test, so e.target is the zone for every press on slide text. ENG-8477
24692
+ // fixed the click that OPENS an editor this way and left the guards that
24693
+ // protect an OPEN editor keyed on e.target, so the second click - the one that
24694
+ // places the caret - was handed to the zone and paged the slide.
24695
+ //
24696
+ // A keydown is different: it targets the FOCUSED element, so the ENG-8500 key
24697
+ // guard's e.target test is correct and stays as it is.
24698
+ function pointInEditor(e){
24699
+ if(!editing || !e) return null;
24700
+ var t=e.target;
24701
+ if(t===editing || (editing.contains && t && t.nodeType && editing.contains(t))) return 'direct';
24702
+ if(typeof e.clientX!=='number' || typeof e.clientY!=='number') return null;
24703
+ var stack;
24704
+ try{ stack=document.elementsFromPoint(e.clientX, e.clientY)||[]; }catch(err){ return null; }
24705
+ for(var i=0;i<stack.length;i++){
24706
+ var n=stack[i];
24707
+ if(n===editing || (editing.contains && n && n.nodeType && editing.contains(n))) return 'through';
24708
+ }
24709
+ return null;
24710
+ }
24711
+ // ENG-10632: put the caret where the viewer pressed, as the browser would have
24712
+ // if nothing covered the editor. The browser's own caret placement goes to the
24713
+ // topmost element, which is the overlay, so it places nothing in the text.
24714
+ // Returns true when a caret was placed inside the editor.
24715
+ function placeCaretAtPointThrough(x,y,el){
24716
+ return throughOverlays(x,y,el,function(){
24717
+ try{
24718
+ var node=null, offset=0, r=null;
24719
+ if(document.caretRangeFromPoint){
24720
+ r=document.caretRangeFromPoint(x,y);
24721
+ if(r){ node=r.startContainer; offset=r.startOffset||0; }
24722
+ } else if(document.caretPositionFromPoint){
24723
+ var p=document.caretPositionFromPoint(x,y);
24724
+ if(p){ node=p.offsetNode; offset=p.offset||0; }
24725
+ }
24726
+ if(!node || !(node===el || (el.contains && el.contains(node.nodeType===3 ? node.parentNode||node.parentElement : node)))) return false;
24727
+ if(!r){ r=document.createRange(); r.setStart(node, offset); r.collapse(true); }
24728
+ var s=document.getSelection(); s.removeAllRanges(); s.addRange(r);
24729
+ return true;
24730
+ }catch(err){ return false; }
24731
+ });
24732
+ }
24674
24733
  // Index of a text node among its parent's child TEXT nodes, in order - the same
24675
24734
  // counting the server uses to re-locate it in source_html (ENG-6856).
24676
24735
  function textIndexOf(container, tn){
@@ -24678,6 +24737,13 @@ var MARKUP_BRIDGE_SCRIPT = `<script>(function(){
24678
24737
  for(var k=0;k<ns.length;k++){ if(ns[k].nodeType===3){ i++; if(ns[k]===tn) return i; } }
24679
24738
  return -1;
24680
24739
  }
24740
+ function keepCaretIn(el){
24741
+ try{
24742
+ var s=document.getSelection(), n=s && s.rangeCount ? s.anchorNode : null;
24743
+ if(n && (n===el || (el.contains && el.contains(n.nodeType===3 ? n.parentNode : n)))) return;
24744
+ }catch(err){}
24745
+ placeCaretEnd(el);
24746
+ }
24681
24747
  function placeCaretEnd(el){ try{ var r=document.createRange(); r.selectNodeContents(el); r.collapse(false); var s=document.getSelection(); s.removeAllRanges(); s.addRange(r); }catch(e){} }
24682
24748
  // The Save/Cancel toolbar anchored above el. Shared by whole-element edits
24683
24749
  // (startEdit) and per-text-node edits (startEditTextNode).
@@ -24887,9 +24953,35 @@ var MARKUP_BRIDGE_SCRIPT = `<script>(function(){
24887
24953
  // press that lands inside the editor. A press anywhere else is the viewer
24888
24954
  // leaving, and the artefact keeps it.
24889
24955
  window.addEventListener('pointerdown', function(e){
24890
- if(!editing) return;
24891
- var t=e.target;
24892
- if(t!==editing && !(editing.contains && t && t.nodeType && editing.contains(t))) return;
24956
+ // ENG-10632: "inside the editor" is decided through the hit stack, not from
24957
+ // e.target, which a deck's click zone always wins. See pointInEditor.
24958
+ var where=pointInEditor(e);
24959
+ if(!where) return;
24960
+ e.stopImmediatePropagation();
24961
+ if(where==='through'){
24962
+ // The overlay is the browser's hit target, so its default action would
24963
+ // move focus off the editor and place no caret in the text. Take both over.
24964
+ try{ e.preventDefault(); }catch(err){}
24965
+ try{ editing.focus(); }catch(err){}
24966
+ // The press is suppressed either way, so a probe that cannot resolve a
24967
+ // point inside the editor must not leave it without a caret: keep one the
24968
+ // editor already holds, otherwise put it at the end of the text.
24969
+ if(!placeCaretAtPointThrough(e.clientX, e.clientY, editing)) keepCaretIn(editing);
24970
+ }
24971
+ }, true);
24972
+ // ENG-10632: the same press also arrives as mousedown and click, and the deck
24973
+ // can page on either. Only a press that went THROUGH an overlay into the open
24974
+ // editor is swallowed; a direct press keeps its native behaviour (caret,
24975
+ // drag-select), and a press anywhere else is the viewer leaving and is left to
24976
+ // the artefact.
24977
+ window.addEventListener('mousedown', function(e){
24978
+ if(pointInEditor(e)!=='through') return;
24979
+ try{ e.preventDefault(); }catch(err){}
24980
+ e.stopImmediatePropagation();
24981
+ }, true);
24982
+ window.addEventListener('click', function(e){
24983
+ if(pointInEditor(e)!=='through') return;
24984
+ try{ e.preventDefault(); }catch(err){}
24893
24985
  e.stopImmediatePropagation();
24894
24986
  }, true);
24895
24987
  // ENG-8477: the hover affordance has the same blind spot as the click did -
@@ -38807,21 +38807,80 @@ var MARKUP_BRIDGE_SCRIPT = `<script>(function(){
38807
38807
  // ancestors too - a wider style mutation than intended, for a probe whose
38808
38808
  // result the caller then rejects anyway.
38809
38809
  function textNodeAtPointThrough(x,y,target){
38810
+ return throughOverlays(x,y,target,function(){ return textNodeAtPoint(x,y); });
38811
+ }
38812
+ // Run probe() with everything stacked IN FRONT of target muted for pointer
38813
+ // hit-testing, then restore it. Shared by the text-node probe above and the
38814
+ // caret placement below (ENG-10632), so there is one overlay-muting path.
38815
+ // A target that is not in the hit stack (or no elementsFromPoint at all) runs
38816
+ // the probe unmuted, for the reason given on textNodeAtPointThrough.
38817
+ function throughOverlays(x,y,target,probe){
38810
38818
  var stack;
38811
- try{ stack=document.elementsFromPoint(x,y)||[]; }catch(e){ return textNodeAtPoint(x,y); }
38819
+ try{ stack=document.elementsFromPoint(x,y)||[]; }catch(e){ return probe(); }
38812
38820
  var cut=-1, i;
38813
- for(i=0;i<stack.length;i++){ if(stack[i]===target){ cut=i; break; } }
38814
- if(cut<0) return textNodeAtPoint(x,y);
38821
+ for(i=0;i<stack.length;i++){ if(stack[i]===target || (target && target.contains && stack[i] && stack[i].nodeType && target.contains(stack[i]))){ cut=i; break; } }
38822
+ if(cut<0) return probe();
38815
38823
  var muted=[];
38816
38824
  for(i=0;i<cut;i++){
38817
38825
  muted.push([stack[i], stack[i].style ? stack[i].style.pointerEvents : '']);
38818
38826
  try{ stack[i].style.pointerEvents='none'; }catch(e){}
38819
38827
  }
38820
- try{ return textNodeAtPoint(x,y); }
38828
+ try{ return probe(); }
38821
38829
  finally{
38822
38830
  for(i=0;i<muted.length;i++){ try{ muted[i][0].style.pointerEvents=muted[i][1]||''; }catch(e){} }
38823
38831
  }
38824
38832
  }
38833
+ // ENG-10632: is this pointer event aimed INSIDE the open editor?
38834
+ //
38835
+ // 'direct' - the event's own target is the editor or inside it (no overlay).
38836
+ // 'through' - the target is something laid over the editor (a deck click
38837
+ // zone), but the editor is in the hit stack under the pointer.
38838
+ // null - no edit is open, or the press is genuinely elsewhere.
38839
+ //
38840
+ // WHY NOT e.target ALONE. That is exactly the ENG-8477 blind spot: a pointer
38841
+ // event targets the HIT-TEST result, and a deck's full-viewport zones win the
38842
+ // hit test, so e.target is the zone for every press on slide text. ENG-8477
38843
+ // fixed the click that OPENS an editor this way and left the guards that
38844
+ // protect an OPEN editor keyed on e.target, so the second click - the one that
38845
+ // places the caret - was handed to the zone and paged the slide.
38846
+ //
38847
+ // A keydown is different: it targets the FOCUSED element, so the ENG-8500 key
38848
+ // guard's e.target test is correct and stays as it is.
38849
+ function pointInEditor(e){
38850
+ if(!editing || !e) return null;
38851
+ var t=e.target;
38852
+ if(t===editing || (editing.contains && t && t.nodeType && editing.contains(t))) return 'direct';
38853
+ if(typeof e.clientX!=='number' || typeof e.clientY!=='number') return null;
38854
+ var stack;
38855
+ try{ stack=document.elementsFromPoint(e.clientX, e.clientY)||[]; }catch(err){ return null; }
38856
+ for(var i=0;i<stack.length;i++){
38857
+ var n=stack[i];
38858
+ if(n===editing || (editing.contains && n && n.nodeType && editing.contains(n))) return 'through';
38859
+ }
38860
+ return null;
38861
+ }
38862
+ // ENG-10632: put the caret where the viewer pressed, as the browser would have
38863
+ // if nothing covered the editor. The browser's own caret placement goes to the
38864
+ // topmost element, which is the overlay, so it places nothing in the text.
38865
+ // Returns true when a caret was placed inside the editor.
38866
+ function placeCaretAtPointThrough(x,y,el){
38867
+ return throughOverlays(x,y,el,function(){
38868
+ try{
38869
+ var node=null, offset=0, r=null;
38870
+ if(document.caretRangeFromPoint){
38871
+ r=document.caretRangeFromPoint(x,y);
38872
+ if(r){ node=r.startContainer; offset=r.startOffset||0; }
38873
+ } else if(document.caretPositionFromPoint){
38874
+ var p=document.caretPositionFromPoint(x,y);
38875
+ if(p){ node=p.offsetNode; offset=p.offset||0; }
38876
+ }
38877
+ if(!node || !(node===el || (el.contains && el.contains(node.nodeType===3 ? node.parentNode||node.parentElement : node)))) return false;
38878
+ if(!r){ r=document.createRange(); r.setStart(node, offset); r.collapse(true); }
38879
+ var s=document.getSelection(); s.removeAllRanges(); s.addRange(r);
38880
+ return true;
38881
+ }catch(err){ return false; }
38882
+ });
38883
+ }
38825
38884
  // Index of a text node among its parent's child TEXT nodes, in order - the same
38826
38885
  // counting the server uses to re-locate it in source_html (ENG-6856).
38827
38886
  function textIndexOf(container, tn){
@@ -38829,6 +38888,13 @@ var MARKUP_BRIDGE_SCRIPT = `<script>(function(){
38829
38888
  for(var k=0;k<ns.length;k++){ if(ns[k].nodeType===3){ i++; if(ns[k]===tn) return i; } }
38830
38889
  return -1;
38831
38890
  }
38891
+ function keepCaretIn(el){
38892
+ try{
38893
+ var s=document.getSelection(), n=s && s.rangeCount ? s.anchorNode : null;
38894
+ if(n && (n===el || (el.contains && el.contains(n.nodeType===3 ? n.parentNode : n)))) return;
38895
+ }catch(err){}
38896
+ placeCaretEnd(el);
38897
+ }
38832
38898
  function placeCaretEnd(el){ try{ var r=document.createRange(); r.selectNodeContents(el); r.collapse(false); var s=document.getSelection(); s.removeAllRanges(); s.addRange(r); }catch(e){} }
38833
38899
  // The Save/Cancel toolbar anchored above el. Shared by whole-element edits
38834
38900
  // (startEdit) and per-text-node edits (startEditTextNode).
@@ -39038,9 +39104,35 @@ var MARKUP_BRIDGE_SCRIPT = `<script>(function(){
39038
39104
  // press that lands inside the editor. A press anywhere else is the viewer
39039
39105
  // leaving, and the artefact keeps it.
39040
39106
  window.addEventListener('pointerdown', function(e){
39041
- if(!editing) return;
39042
- var t=e.target;
39043
- if(t!==editing && !(editing.contains && t && t.nodeType && editing.contains(t))) return;
39107
+ // ENG-10632: "inside the editor" is decided through the hit stack, not from
39108
+ // e.target, which a deck's click zone always wins. See pointInEditor.
39109
+ var where=pointInEditor(e);
39110
+ if(!where) return;
39111
+ e.stopImmediatePropagation();
39112
+ if(where==='through'){
39113
+ // The overlay is the browser's hit target, so its default action would
39114
+ // move focus off the editor and place no caret in the text. Take both over.
39115
+ try{ e.preventDefault(); }catch(err){}
39116
+ try{ editing.focus(); }catch(err){}
39117
+ // The press is suppressed either way, so a probe that cannot resolve a
39118
+ // point inside the editor must not leave it without a caret: keep one the
39119
+ // editor already holds, otherwise put it at the end of the text.
39120
+ if(!placeCaretAtPointThrough(e.clientX, e.clientY, editing)) keepCaretIn(editing);
39121
+ }
39122
+ }, true);
39123
+ // ENG-10632: the same press also arrives as mousedown and click, and the deck
39124
+ // can page on either. Only a press that went THROUGH an overlay into the open
39125
+ // editor is swallowed; a direct press keeps its native behaviour (caret,
39126
+ // drag-select), and a press anywhere else is the viewer leaving and is left to
39127
+ // the artefact.
39128
+ window.addEventListener('mousedown', function(e){
39129
+ if(pointInEditor(e)!=='through') return;
39130
+ try{ e.preventDefault(); }catch(err){}
39131
+ e.stopImmediatePropagation();
39132
+ }, true);
39133
+ window.addEventListener('click', function(e){
39134
+ if(pointInEditor(e)!=='through') return;
39135
+ try{ e.preventDefault(); }catch(err){}
39044
39136
  e.stopImmediatePropagation();
39045
39137
  }, true);
39046
39138
  // ENG-8477: the hover affordance has the same blind spot as the click did -