@lightworkai.official/debug-capture 0.8.1 → 0.8.2

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/README.md CHANGED
@@ -142,6 +142,11 @@ takes ten seconds. A tab opened after a countdown can be refused — the click's
142
142
  activation may have expired — so the dialog offers a real link in that case,
143
143
  which is a fresh gesture and cannot be blocked.
144
144
 
145
+ The tab is opened without the `noopener` window feature and its `opener` is
146
+ nulled afterwards instead. `window.open(…, "noopener")` returns `null` on
147
+ *success*, by specification, so asking for it destroys the only signal that says
148
+ whether the tab opened at all.
149
+
145
150
  **It announces itself first.** Leaving for another origin without warning,
146
151
  mid-task, reads as a bug — so a small dialog says where they are going, that
147
152
  they will be signed in when they arrive, and counts down with a way to go now or
package/dist/index.mjs CHANGED
@@ -171,15 +171,21 @@ p { margin: 0; font-size: 13px; line-height: 1.6; color: #475569; }
171
171
  .host { margin-top: 8px; font-size: 12px; color: #94a3b8; word-break: break-all; }
172
172
 
173
173
  .actions { display: flex; gap: 8px; margin-top: 20px; }
174
- button {
175
- flex: 1; padding: 9px 12px; border-radius: 9px; font: inherit; font-size: 14px;
176
- cursor: pointer; transition: background 120ms ease, border-color 120ms ease;
174
+ /* The anchor too: when a blocked tab turns the primary action into a link, it
175
+ has to be the same object it was a moment ago. Styling only the button left it
176
+ with the dark fill and none of the shape: an underlined black smear. */
177
+ .actions button, .actions a {
178
+ flex: 1; box-sizing: border-box;
179
+ display: inline-flex; align-items: center; justify-content: center;
180
+ padding: 9px 12px; border-radius: 9px; font: inherit; font-size: 14px;
181
+ text-decoration: none; cursor: pointer;
182
+ transition: background 120ms ease, border-color 120ms ease;
177
183
  }
178
184
  .go { border: none; background: #0f172a; color: #fff; font-weight: 600; }
179
185
  .go:hover { background: #1e293b; }
180
186
  .cancel { border: 1px solid #e2e8f0; background: #fff; color: #334155; }
181
187
  .cancel:hover { background: #f8fafc; }
182
- button:focus-visible { outline: 2px solid #0ea5e9; outline-offset: 2px; }
188
+ .actions button:focus-visible, .actions a:focus-visible { outline: 2px solid #0ea5e9; outline-offset: 2px; }
183
189
 
184
190
  .error h2 { color: #b91c1c; }
185
191
 
@@ -208,4 +214,4 @@ button:focus-visible { outline: 2px solid #0ea5e9; outline-offset: 2px; }
208
214
  <button type="button" class="cancel"></button>
209
215
  <button type="button" class="go"></button>
210
216
  </div>
211
- </div>`,z.append(K);let G=(D)=>K.querySelector(D),H=G(".card"),U=G(".bar"),C=G(".count"),w=G("h2"),P=G(".body"),O=G(".host"),N=G(".go"),S=G(".cancel");w.textContent=j.handoffTitle,P.textContent=j.handoffBody;try{O.textContent=new URL(R().host).host}catch{O.textContent=""}N.textContent=j.handoffGo,S.textContent=j.cancel,H.style.setProperty("--secs",`${J}s`),C.textContent=String(J),document.body.append(Q),requestAnimationFrame(()=>K.setAttribute("data-open","1"));let _=document.activeElement;return N.focus({preventScroll:!0}),new Promise((D)=>{let q=!1,g=null,e=!1,u=Date.now()+J*1000,E=window.setInterval(()=>{let B=Math.ceil((u-Date.now())/1000);if(C.textContent=String(Math.max(0,B)),Date.now()>=u)L()},100);function p(B){if(q)return;if(q=!0,window.clearInterval(E),document.removeEventListener("keydown",m,!0),K.removeAttribute("data-open"),window.setTimeout(()=>Q.remove(),200),!B)_?.focus?.({preventScroll:!0});D()}function H0(B){window.clearInterval(E),U.classList.remove("spinner"),U.style.animation="none",C.textContent="",w.textContent=j.handoffBlocked,P.textContent=j.handoffBody;let k=document.createElement("a");k.className="go",k.href=B,k.target="_blank",k.rel="noopener noreferrer",k.textContent=j.handoffOpen,k.addEventListener("click",()=>p(!0)),N.replaceWith(k),k.focus({preventScroll:!0})}function L(){if(q||e)return;if(g)return $.go(g)?p(!0):H0(g);window.clearInterval(E),C.textContent="",U.classList.add("spinner"),w.textContent=j.handoffPreparing}function m(B){if(B.key==="Escape")B.stopPropagation(),p(!1)}document.addEventListener("keydown",m,!0),N.addEventListener("click",L),S.addEventListener("click",()=>p(!1)),K.addEventListener("click",(B)=>{if(B.target===K)p(!1)}),$.prepare().then((B)=>{if(g=B,!q&&Date.now()>=u)L()},()=>{e=!0,window.clearInterval(E),H.classList.add("error"),U.classList.remove("spinner"),U.style.animation="none",C.textContent="",w.textContent=j.handoffFailed,P.textContent=j.handoffBody,N.remove(),S.textContent=j.close})})}var s4="/sso";function e1($,j,J){let Q=new URL(s4,$.endsWith("/")?$:`${$}/`);return Q.searchParams.set("realm",j),Q.searchParams.set("token",J),Q.toString()}class F0 extends Error{constructor($="No identity provider is configured, or nobody is signed in"){super($);this.name="NoIdentityError"}}async function a1(){let $=R();if(!$.identity)throw new F0;let j=await $.identity();if(!j)throw new F0;return e1($.host,$.realmKey,j)}function t1($,j){if(j==="_self")return window.location.assign($),!0;return window.open($,"_blank","noopener,noreferrer")!==null}async function n4($={}){if($.announce===!1){t1(await a1(),$.target);return}await n1({seconds:$.seconds,prepare:a1,go:(j)=>t1(j,$.target)})}export{g0 as trimToBudget,g1 as toCurl,e1 as supportPortalUrl,p0 as submitBundle,n as showToast,p4 as setErroredQueriesSource,d4 as requestPath,b as reporterStrings,o as redactUrl,c as redactLabel,i as redactHeaders,l as redactBody,n4 as openSupportPortal,R0 as onLaunch,_0 as onCrash,O0 as launch,i4 as isQuietStep,r1 as isFailedRequest,U0 as isConfigured,b4 as installDebugCapture,D4 as hasCause,A0 as getNetworkLog,S0 as getConsoleLog,R as getConfig,B0 as getActionTrail,o4 as formatDuration,x0 as errorSignature,L0 as emitCrash,u0 as defineReporterElement,y0 as createAnnotator,c4 as correlateActionTrail,Q2 as configureDebugCapture,E0 as captureScreen,P4 as bundleToMarkdown,w4 as bundleFileName,k0 as buildBundle,x as appInfo,D0 as actionCountSuffix,t as REPORTER_TAG,d as REDACTED,F0 as NoIdentityError,h0 as BUNDLE_BUDGET_BYTES};
217
+ </div>`,z.append(K);let G=(D)=>K.querySelector(D),H=G(".card"),U=G(".bar"),C=G(".count"),w=G("h2"),P=G(".body"),O=G(".host"),N=G(".go"),S=G(".cancel");w.textContent=j.handoffTitle,P.textContent=j.handoffBody;try{O.textContent=new URL(R().host).host}catch{O.textContent=""}N.textContent=j.handoffGo,S.textContent=j.cancel,H.style.setProperty("--secs",`${J}s`),C.textContent=String(J),document.body.append(Q),requestAnimationFrame(()=>K.setAttribute("data-open","1"));let _=document.activeElement;return N.focus({preventScroll:!0}),new Promise((D)=>{let q=!1,g=null,e=!1,u=Date.now()+J*1000,E=window.setInterval(()=>{let B=Math.ceil((u-Date.now())/1000);if(C.textContent=String(Math.max(0,B)),Date.now()>=u)L()},100);function p(B){if(q)return;if(q=!0,window.clearInterval(E),document.removeEventListener("keydown",m,!0),K.removeAttribute("data-open"),window.setTimeout(()=>Q.remove(),200),!B)_?.focus?.({preventScroll:!0});D()}function H0(B){window.clearInterval(E),U.classList.remove("spinner"),U.style.animation="none",C.textContent="",w.textContent=j.handoffBlocked,P.textContent=j.handoffBody;let k=document.createElement("a");k.className="go",k.href=B,k.target="_blank",k.rel="noopener noreferrer",k.textContent=j.handoffOpen,k.addEventListener("click",()=>p(!0)),N.replaceWith(k),k.focus({preventScroll:!0})}function L(){if(q||e)return;if(g)return $.go(g)?p(!0):H0(g);window.clearInterval(E),C.textContent="",U.classList.add("spinner"),w.textContent=j.handoffPreparing}function m(B){if(B.key==="Escape")B.stopPropagation(),p(!1)}document.addEventListener("keydown",m,!0),N.addEventListener("click",L),S.addEventListener("click",()=>p(!1)),K.addEventListener("click",(B)=>{if(B.target===K)p(!1)}),$.prepare().then((B)=>{if(g=B,!q&&Date.now()>=u)L()},()=>{e=!0,window.clearInterval(E),H.classList.add("error"),U.classList.remove("spinner"),U.style.animation="none",C.textContent="",w.textContent=j.handoffFailed,P.textContent=j.handoffBody,N.remove(),S.textContent=j.close})})}var s4="/sso";function e1($,j,J){let Q=new URL(s4,$.endsWith("/")?$:`${$}/`);return Q.searchParams.set("realm",j),Q.searchParams.set("token",J),Q.toString()}class F0 extends Error{constructor($="No identity provider is configured, or nobody is signed in"){super($);this.name="NoIdentityError"}}async function a1(){let $=R();if(!$.identity)throw new F0;let j=await $.identity();if(!j)throw new F0;return e1($.host,$.realmKey,j)}function t1($,j){if(j==="_self")return window.location.assign($),!0;let J=window.open($,"_blank");if(!J)return!1;try{J.opener=null}catch{}return!0}async function n4($={}){if($.announce===!1){t1(await a1(),$.target);return}await n1({seconds:$.seconds,prepare:a1,go:(j)=>t1(j,$.target)})}export{g0 as trimToBudget,g1 as toCurl,e1 as supportPortalUrl,p0 as submitBundle,n as showToast,p4 as setErroredQueriesSource,d4 as requestPath,b as reporterStrings,o as redactUrl,c as redactLabel,i as redactHeaders,l as redactBody,n4 as openSupportPortal,R0 as onLaunch,_0 as onCrash,O0 as launch,i4 as isQuietStep,r1 as isFailedRequest,U0 as isConfigured,b4 as installDebugCapture,D4 as hasCause,A0 as getNetworkLog,S0 as getConsoleLog,R as getConfig,B0 as getActionTrail,o4 as formatDuration,x0 as errorSignature,L0 as emitCrash,u0 as defineReporterElement,y0 as createAnnotator,c4 as correlateActionTrail,Q2 as configureDebugCapture,E0 as captureScreen,P4 as bundleToMarkdown,w4 as bundleFileName,k0 as buildBundle,x as appInfo,D0 as actionCountSuffix,t as REPORTER_TAG,d as REDACTED,F0 as NoIdentityError,h0 as BUNDLE_BUDGET_BYTES};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lightworkai.official/debug-capture",
3
- "version": "0.8.1",
3
+ "version": "0.8.2",
4
4
  "description": "Report-a-problem widget: rolling network/console/action capture, annotated screenshot, and submission to a Lightwork Support host",
5
5
  "type": "module",
6
6
  "module": "./dist/index.mjs",
package/src/portal.ts CHANGED
@@ -72,10 +72,29 @@ function navigate(url: string, target: OpenPortalOptions["target"]): boolean {
72
72
  window.location.assign(url);
73
73
  return true;
74
74
  }
75
- // noopener: the new tab gets no handle back to the host page. noreferrer:
76
- // and no Referer either, because the URL it is being sent to has a
77
- // credential in it.
78
- return window.open(url, "_blank", "noopener,noreferrer") !== null;
75
+
76
+ /*
77
+ * Opened WITHOUT the `noopener` feature, then severed by hand.
78
+ *
79
+ * `window.open(…, "noopener")` returns null on success — by specification,
80
+ * because handing back a reference is the very thing noopener exists to
81
+ * prevent. So "did it open?" and "give it no opener" cannot both be answered
82
+ * by the same call: asking for one destroys the answer to the other. Reading
83
+ * that null as "blocked" made every successful hand-off report a popup
84
+ * blocker while the tab it had just opened sat there.
85
+ *
86
+ * Nulling `opener` afterwards is the long-standing equivalent. In a try
87
+ * because a cross-origin window may refuse the assignment — and if it does,
88
+ * the tab is open, which is what the return value is about.
89
+ */
90
+ const opened = window.open(url, "_blank");
91
+ if (!opened) return false;
92
+ try {
93
+ opened.opener = null;
94
+ } catch {
95
+ /* the tab is open either way */
96
+ }
97
+ return true;
79
98
  }
80
99
 
81
100
  /**
package/src/ui/handoff.ts CHANGED
@@ -74,15 +74,21 @@ p { margin: 0; font-size: 13px; line-height: 1.6; color: #475569; }
74
74
  .host { margin-top: 8px; font-size: 12px; color: #94a3b8; word-break: break-all; }
75
75
 
76
76
  .actions { display: flex; gap: 8px; margin-top: 20px; }
77
- button {
78
- flex: 1; padding: 9px 12px; border-radius: 9px; font: inherit; font-size: 14px;
79
- cursor: pointer; transition: background 120ms ease, border-color 120ms ease;
77
+ /* The anchor too: when a blocked tab turns the primary action into a link, it
78
+ has to be the same object it was a moment ago. Styling only the button left it
79
+ with the dark fill and none of the shape: an underlined black smear. */
80
+ .actions button, .actions a {
81
+ flex: 1; box-sizing: border-box;
82
+ display: inline-flex; align-items: center; justify-content: center;
83
+ padding: 9px 12px; border-radius: 9px; font: inherit; font-size: 14px;
84
+ text-decoration: none; cursor: pointer;
85
+ transition: background 120ms ease, border-color 120ms ease;
80
86
  }
81
87
  .go { border: none; background: #0f172a; color: #fff; font-weight: 600; }
82
88
  .go:hover { background: #1e293b; }
83
89
  .cancel { border: 1px solid #e2e8f0; background: #fff; color: #334155; }
84
90
  .cancel:hover { background: #f8fafc; }
85
- button:focus-visible { outline: 2px solid #0ea5e9; outline-offset: 2px; }
91
+ .actions button:focus-visible, .actions a:focus-visible { outline: 2px solid #0ea5e9; outline-offset: 2px; }
86
92
 
87
93
  .error h2 { color: #b91c1c; }
88
94