@preventive/triage 1.0.0-alpha.46 → 1.0.0-alpha.48
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/client/dependency-paths.d.ts +2 -1
- package/client/dependency-paths.js +16 -4
- package/common/managed/agent-tasks.ts +12 -12
- package/common/managed/github-agents.ts +29 -10
- package/out/activity.js +4 -0
- package/out/chunk-6LNQTKAV.js +52 -0
- package/out/chunk-7BKKYVWR.js +7 -0
- package/out/chunk-7VAWINQF.js +6 -0
- package/out/chunk-C2AVODGZ.js +36 -0
- package/out/{chunk-I2MDGSQX.js → chunk-IO7JTZ6O.js} +1 -1
- package/out/chunk-MOACFRJ4.js +3 -0
- package/out/chunk-O6M574JI.js +1 -0
- package/out/chunk-PBZPYIG7.js +1 -0
- package/out/chunk-PENGGKX7.js +233 -0
- package/out/{chunk-OKRLLE5I.js → chunk-PWFUOLZK.js} +1 -1
- package/out/chunk-SZD5CW5R.js +3 -0
- package/out/{chunk-G24A42V6.js → chunk-TQJDYHVE.js} +1 -1
- package/out/{chunk-3PS4KWD2.js → chunk-WCPJDAYA.js} +1 -1
- package/out/{chunk-7XRR4OFU.js → chunk-WYLQWYLA.js} +1 -1
- package/out/chunk-X6PJAK3T.js +1 -0
- package/out/client-managed-import.js +1 -1
- package/out/client-managed.js +26 -26
- package/out/client-sync.js +2 -2
- package/out/compare.js +14 -14
- package/out/dialogs.js +40 -40
- package/out/graph.js +11 -11
- package/out/index.html +12 -12
- package/out/scan.js +2 -2
- package/out/terminal.js +215 -215
- package/out/view.css +1 -1
- package/out/view.js +15 -15
- package/package.json +9 -5
- package/risk/index.d.ts +4 -2
- package/risk/npm/assess.js +15 -4
- package/risk/npm/registry.js +6 -1
- package/risk/npm/score.js +1 -1
- package/server-managed/activity.ts +24 -11
- package/server-managed/agent-merges.ts +10 -8
- package/server-managed/app-agents.ts +8 -21
- package/server-managed/bundle-build-worker.js +7 -3
- package/server-managed/bundle-build.ts +5 -3
- package/server-managed/bundle-cache.ts +9 -3
- package/server-managed/bundle.ts +3 -4
- package/server-managed/cache-storage.ts +3 -2
- package/server-managed/commit-coauthors.ts +11 -10
- package/server-managed/commit-lockfiles.ts +22 -21
- package/server-managed/db-methods.ts +338 -187
- package/server-managed/db-neon.ts +31 -8
- package/server-managed/db.ts +2 -0
- package/server-managed/dependabot-access.ts +1 -1
- package/server-managed/dependency-review-comments.ts +7 -4
- package/server-managed/dependency-reviews.ts +71 -33
- package/server-managed/github-accounts.ts +28 -1
- package/server-managed/github-app.ts +27 -23
- package/server-managed/github-graphql.ts +39 -0
- package/server-managed/github-issue-links.ts +8 -1
- package/server-managed/github-metadata.ts +2 -2
- package/server-managed/http-response.ts +28 -10
- package/server-managed/http.ts +736 -247
- package/server-managed/index.ts +2 -2
- package/server-managed/link-reports.ts +5 -1
- package/server-managed/managed-issues.ts +3 -0
- package/server-managed/npm-delta.ts +16 -0
- package/server-managed/npm-kept.ts +51 -10
- package/server-managed/npm-loads.ts +48 -28
- package/server-managed/npm-manifest.ts +1 -1
- package/server-managed/npm-packages.ts +31 -15
- package/server-managed/npm-risk.ts +64 -17
- package/server-managed/object-storage.ts +4 -3
- package/server-managed/pretty-print.ts +45 -12
- package/server-managed/public-workspace.ts +3 -3
- package/server-managed/pull-dependencies.ts +84 -36
- package/server-managed/pull-dependency-reviews.ts +13 -10
- package/server-managed/rename-diff-worker.js +1 -1
- package/server-managed/report-store.ts +16 -11
- package/server-managed/repository-agents.ts +9 -6
- package/server-managed/repository-browser.ts +17 -5
- package/server-managed/repository-code.ts +180 -17
- package/server-managed/repository-dependency-reviews.ts +7 -0
- package/server-managed/repository-history.ts +85 -152
- package/server-managed/repository-issues.ts +27 -13
- package/server-managed/repository-pulse.ts +9 -3
- package/server-managed/repository-squashes.ts +145 -0
- package/server-managed/request-timing.ts +63 -0
- package/server-managed/reviewed-dependencies.ts +3 -2
- package/server-managed/session.ts +34 -12
- package/server-managed/solidity-entry-points.ts +5 -3
- package/server-managed/sql-postgres.ts +3 -0
- package/server-managed/sql.ts +17 -8
- package/server-managed/storage-db.ts +11 -3
- package/server-managed/storage-encryption.ts +23 -12
- package/server-managed/storage-maintenance.ts +5 -2
- package/server-managed/storage-payload.ts +18 -0
- package/server-managed/storage-stores.ts +3 -2
- package/server-managed/storage.ts +4 -2
- package/server-managed/team-feed.ts +19 -12
- package/server-managed/team-npm-scopes.ts +29 -0
- package/server-managed/team-reports.ts +74 -17
- package/server-managed/uploads.ts +29 -9
- package/out/chunk-4UFAMJAX.js +0 -1
- package/out/chunk-BOIXMUU5.js +0 -3
- package/out/chunk-CC4OM6TZ.js +0 -3
- package/out/chunk-JKGQ3O7O.js +0 -6
- package/out/chunk-LE5ZWLG4.js +0 -235
- package/out/chunk-MSION3G4.js +0 -36
- package/out/chunk-S6GIXWQW.js +0 -52
- package/out/chunk-V3KWNNMY.js +0 -1
- package/out/chunk-WQ74ZBZF.js +0 -7
package/out/dialogs.js
CHANGED
|
@@ -1,51 +1,51 @@
|
|
|
1
|
-
import{a as e,c as t,d as n,h as r,i}from"./chunk-
|
|
2
|
-
`,
|
|
3
|
-
`,
|
|
4
|
-
`,
|
|
5
|
-
`,B=class extends b{static styles=[...b.styles,I(z)];firstUpdated(){if(this.signal?.aborted){this._onCancel();return}this.signal?.addEventListener(`abort`,this._onCancel,{once:!0}),super.firstUpdated()}disconnectedCallback(){this.signal?.removeEventListener(`abort`,this._onCancel),super.disconnectedCallback()}focusInitial(){this.renderRoot.querySelector(`button[data-role="cancel"]`)?.focus()}confirmationResult(e){return{confirmed:e&&!this.signal?.aborted}}_finish(e){this._settled||super._finish(this.confirmationResult(!!e))}_onClose=()=>this._finish(!1);_onCancel=()=>this._finish(!1);_onConfirm=()=>this._finish(!0)},ln=class extends B{static properties={bundleName:{type:String},inRemote:{type:Boolean}};constructor(){super(),this.bundleName=``,this.inRemote=!1}_remoteNotice(){return this.inRemote?L`<p class="lwd-note">This bundle is also stored in the workspace's <strong>remote</strong> inventory and will be removed from there too. Newly synced workspace members won't see it; members who already downloaded it keep their local copy (and could re-upload).</p>`:R}render(){return L`<dialog @close="${this._onClose}"><header><h3>Delete bundle</h3></header><p class="lwd-body">Delete <strong>"${this.bundleName}"</strong>?</p>${this._remoteNotice()}<footer class="nwd-actions"><span class="nwd-spacer"></span> <button type="button" data-role="cancel" @click="${this._onCancel}">Cancel</button> <button type="button" class="danger" @click="${this._onConfirm}">Delete</button></footer></dialog>`}};customElements.define(`delete-bundle-dialog`,ln);function un({name:e,inRemote:t}={}){return x(`delete-bundle-dialog`,{bundleName:e??``,inRemote:!!t})}var dn=class extends B{static properties={reportName:{type:String},kindLabel:{type:String},orphanedTriage:{type:Number},sharedTriage:{type:Number},inRemote:{type:Boolean},_triage:{state:!0}};constructor(){super(),this.reportName=``,this.kindLabel=`report`,this.orphanedTriage=0,this.sharedTriage=0,this.inRemote=!1,this._triage=`keep`}confirmationResult(e){return{confirmed:e,triage:this.orphanedTriage>0?this._triage:`keep`}}_onTriageChange=e=>{this._triage=e.target.value};_remoteNotice(){return this.inRemote?L`<p class="lwd-note">This report is also stored in the workspace's <strong>remote</strong> inventory and will be removed from there too. Newly synced workspace members won't see it; members who already downloaded it keep their local copy (and could re-upload).</p>`:R}_triageSection(){let e=this.orphanedTriage,t=this.sharedTriage;return e===0&&t===0?L`<p class="lwd-note">No local triage is attached to this ${this.kindLabel}.</p>`:e===0?L`<p class="lwd-note">Local triage on this report is also attached to ${t===1?`a report`:`reports`} you're keeping. Nothing will be orphaned.</p>`:L`<fieldset class="lwd-choice"><legend><strong>${e}</strong> ${e===1?`triage entry won't`:`triage entries won't`} be reachable from any remaining report. What should happen ${e===1?`to it`:`to them`}?</legend><label class="lwd-option"><input type="radio" name="drd-triage" value="keep" ?checked="${this._triage===`keep`}" @change="${this._onTriageChange}"> <span class="lwd-option-text"><span class="lwd-option-title">Keep</span> <span class="lwd-option-hint">Reapplied automatically if a matching report is imported later.</span></span></label> <label class="lwd-option"><input type="radio" name="drd-triage" value="wipe" ?checked="${this._triage===`wipe`}" @change="${this._onTriageChange}"> <span class="lwd-option-text"><span class="lwd-option-title">Wipe</span> <span class="lwd-option-hint">Drop the orphaned triage from this browser.</span></span></label></fieldset>`}render(){return L`<dialog @close="${this._onClose}"><header><h3>Delete ${this.kindLabel}</h3></header><p class="lwd-body">Delete <strong>"${this.reportName}"</strong>?</p>${this._remoteNotice()} ${this._triageSection()}<footer class="nwd-actions"><span class="nwd-spacer"></span> <button type="button" data-role="cancel" @click="${this._onCancel}">Cancel</button> <button type="button" class="danger" @click="${this._onConfirm}">Delete</button></footer></dialog>`}};customElements.define(`delete-report-dialog`,dn);function fn({name:e,kindLabel:t,triageImpact:n,inRemote:r}={}){return x(`delete-report-dialog`,{reportName:e??``,kindLabel:t||`report`,orphanedTriage:n?.orphanedCount??0,sharedTriage:n?.sharedCount??0,inRemote:!!r})}var pn=class extends B{static properties={bundleName:{type:String},workspaceName:{type:String}};constructor(){super(),this.bundleName=``,this.workspaceName=``}render(){return L`<dialog @close="${this._onClose}"><header><h3>Detach bundle</h3></header><p class="lwd-body">Detach <strong>"${this.bundleName}"</strong> from workspace <strong>"${this.workspaceName}"</strong>?</p><p class="lwd-note">This bundle is also stored in the workspace's <strong>remote</strong> inventory and will be removed from there too. Newly synced workspace members won't see it; members who already downloaded it keep their local copy (and could re-upload). The bundle bytes stay on this device — drop it onto another workspace to attach it there.</p><footer class="nwd-actions"><span class="nwd-spacer"></span> <button type="button" data-role="cancel" @click="${this._onCancel}">Cancel</button> <button type="button" class="danger" @click="${this._onConfirm}">Detach</button></footer></dialog>`}};customElements.define(`detach-bundle-dialog`,pn);function mn({name:e,workspaceName:t}={}){return x(`detach-bundle-dialog`,{bundleName:e??``,workspaceName:t??``})}var hn=class extends B{static properties={reportName:{type:String},workspaceName:{type:String}};constructor(){super(),this.reportName=``,this.workspaceName=``}render(){return L`<dialog @close="${this._onClose}"><header><h3>Detach report</h3></header><p class="lwd-body">Detach <strong>"${this.reportName}"</strong> from workspace <strong>"${this.workspaceName}"</strong>?</p><p class="lwd-note">This report is also stored in the workspace's <strong>remote</strong> inventory and will be removed from there too. Newly synced workspace members won't see it; members who already downloaded it keep their local copy (and could re-upload). The report bytes stay on this device — drop it onto another workspace to attach it there.</p><footer class="nwd-actions"><span class="nwd-spacer"></span> <button type="button" data-role="cancel" @click="${this._onCancel}">Cancel</button> <button type="button" class="danger" @click="${this._onConfirm}">Detach</button></footer></dialog>`}};customElements.define(`detach-report-dialog`,hn);function gn({name:e,workspaceName:t}={}){return x(`detach-report-dialog`,{reportName:e??``,workspaceName:t??``})}var _n=class extends B{static properties={reviewTitle:{type:String}};render(){return L`<dialog aria-labelledby="discard-review-title" @close="${this._onClose}"><header><h3 id="discard-review-title">Discard unpublished review?</h3></header><p class="lwd-body"><strong>${this.reviewTitle}</strong></p><p class="lwd-note">This permanently deletes this review and all its file verdicts and notes. This cannot be undone.</p><footer class="nwd-actions"><span class="nwd-spacer"></span> <button type="button" data-role="cancel" @click="${this._onCancel}">Cancel</button> <button type="button" class="danger" @click="${this._onConfirm}">Discard review</button></footer></dialog>`}};customElements.define(`discard-review-dialog`,_n);function vn(e){return x(`discard-review-dialog`,e)}var yn=class extends b{static styles=[...b.styles,I(z)];static properties={bundleName:{type:String},encrypted:{type:Boolean}};constructor(){super(),this.bundleName=``,this.encrypted=!1}focusInitial(){this.renderRoot.querySelector(`button[data-role="cancel"]`)?.focus()}_finish(e){this._settled||super._finish({confirmed:!!e})}_onClose=()=>this._finish(!1);_onCancel=()=>this._finish(!1);_onConfirm=()=>this._finish(!0);_securityNote(){return this.encrypted?L`<p class="lwd-note">The bundle is saved <strong>unencrypted</strong> — even though your vault keeps its stored copy encrypted — and the file contains <strong>all of the bundle's related sources</strong>. Anyone with access to the downloaded file can read every bundled source, so store or share it with care.</p>`:L`<p class="lwd-note">The downloaded file is <strong>unencrypted</strong> and contains <strong>all of the bundle's related sources</strong>. Anyone with access to it can read every bundled source, so store or share it with care.</p>`}render(){return L`<dialog @close="${this._onClose}"><header><h3>Download bundle</h3></header><p class="lwd-body">Download <strong>"${this.bundleName}"</strong> to disk?</p>${this._securityNote()}<footer class="nwd-actions"><span class="nwd-spacer"></span> <button type="button" data-role="cancel" @click="${this._onCancel}">Cancel</button> <button type="button" class="primary" @click="${this._onConfirm}">Download</button></footer></dialog>`}};customElements.define(`download-bundle-dialog`,yn);function bn({name:e,encrypted:t}={}){return x(`download-bundle-dialog`,{bundleName:e??``,encrypted:!!t})}var xn=`dialog{max-width:515px;width:min(94vw,515px)}.ecd-count{margin:0;font-size:1.02rem;color:var(--text);& strong{color:var(--accent);font-weight:700}}.ecd-excluded{margin:.25rem 0 0;color:var(--muted);font-size:.82rem}.ecd-filters{margin:.9rem 0 0;display:grid;grid-template-columns:auto minmax(0,1fr);gap:.3rem .7rem;font-size:.82rem}.ecd-filters dt{color:var(--muted);font-weight:600;white-space:nowrap}.ecd-filters dd{display:flex;align-items:baseline;gap:.5rem;color:var(--text);overflow-wrap:anywhere}.ecd-filter-value{min-width:0}.ecd-filter-drop{margin-left:auto;flex:none;padding:0 .2rem;border:0;border-radius:var(--ui-radius, 4px);background:none;color:var(--muted);font:inherit;font-size:1rem;line-height:1;&:hover{color:var(--critical, #f87171)}&:focus-visible{outline:2px solid var(--accent);outline-offset:1px}}.ecd-filters-label{margin:.9rem 0 0;font-size:.76rem;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--muted)}.ecd-filters-label+.ecd-filters{margin-top:.35rem}.ecd-samples{display:flex;align-items:flex-start;gap:.5rem;margin-top:1rem;font-size:.82rem;color:var(--text)}.ecd-samples input{margin-top:.15rem;accent-color:var(--accent)}.ecd-samples-hint{display:block;margin-top:.2rem;color:var(--muted);font-size:.76rem}
|
|
6
|
-
`,
|
|
7
|
-
`,
|
|
8
|
-
`),{target:n=
|
|
9
|
-
`,
|
|
10
|
-
</code>`}return i}var
|
|
11
|
-
`),n=t.length>1&&t.at(-1)===``?t.length-1:t.length;this._lines=t.slice(0,n),this._kb=(new TextEncoder().encode(this.markdown).length/1024).toFixed(1),this._chunks=
|
|
1
|
+
import{a as e,c as t,d as n,h as r,i}from"./chunk-MOACFRJ4.js";import{b as a,c as o,d as s,e as c,g as l}from"./chunk-X6PJAK3T.js";import{A as u,d,e as f,z as p}from"./chunk-SZD5CW5R.js";import"./chunk-QB4CBOX2.js";import{$e as m,A as h,Ae as ee,Be as te,Ca as ne,Cb as re,Ce as ie,Da as ae,Db as oe,De as se,E as ce,Eb as le,Ec as ue,F as de,Fb as fe,Fc as g,Gb as pe,Gc as me,Hb as he,Hc as ge,Ic as _e,J as ve,Ja as ye,Jc as be,Je as xe,Kc as Se,Ke as Ce,L as _,Lc as we,Le as Te,M as v,Mc as Ee,Nc as De,Ne as y,O as Oe,Oc as ke,Oe as Ae,Pe as je,Re as Me,Se as Ne,Te as b,U as Pe,Ue as Fe,Vc as Ie,W as Le,Wb as Re,Ye as ze,Ze as Be,aa as Ve,ae as x,af as He,b as S,ba as Ue,bc as We,ca as Ge,cb as Ke,cf as qe,d as Je,da as Ye,db as Xe,de as Ze,df as Qe,ea as $e,ee as et,ef as tt,f as nt,fa as rt,fb as it,g as at,ga as ot,h as st,ha as ct,j as lt,jb as ut,jf as dt,k as ft,kb as pt,lb as C,mb as w,n as mt,nb as T,ob as E,pb as ht,qb as D,qe as gt,rb as O,sb as _t,tb as vt,ua as k,ub as yt,uc as bt,vc as xt,w as A,wa as St,x as Ct,y as wt,ye as Tt,z as Et,zc as Dt,ze as Ot}from"./chunk-PENGGKX7.js";import"./chunk-JCIIBLKR.js";import"./chunk-VH4XMZIW.js";import{h as kt,i as At}from"./chunk-O6M574JI.js";import{j as jt,k as Mt}from"./chunk-6LNQTKAV.js";import{c as Nt}from"./chunk-7VAWINQF.js";import"./chunk-NRPC74AM.js";import"./chunk-7BKKYVWR.js";import"./chunk-7C7DWL7I.js";import"./chunk-PNHBZGDD.js";import{a as j}from"./chunk-R5O6FAW4.js";import{O as Pt,S as M,T as Ft,aa as It,b as Lt,sa as Rt}from"./chunk-C2AVODGZ.js";import"./chunk-IO7JTZ6O.js";import"./chunk-WCPJDAYA.js";import{e as zt}from"./chunk-IYPY47MC.js";import{a as N,d as Bt}from"./chunk-PWFUOLZK.js";import{k as P}from"./chunk-BC2UCLFJ.js";import{B as Vt,J as Ht,L as Ut,b as Wt,k as Gt,m as Kt,p as qt,t as Jt,v as F,y as Yt,z as Xt}from"./chunk-SXV2T5C4.js";import{B as Zt,C as Qt,d as I}from"./chunk-WFYLMF6Z.js";import"./chunk-C4Z6ZBXV.js";import{a as L,c as R,d as $t,g as z}from"./chunk-2XUEZHIP.js";import"./chunk-TUII34K2.js";import"./chunk-OOJM4CTU.js";var en=`.inline-quote{background:rgb(from var(--accent) r g b / .1);padding:1px;border-radius:3px;color:var(--text)}.inline-code{background:var(--surface-active);border-radius:3px;border:1px solid var(--border);padding:0 .25em;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.9em;color:var(--text)}.para+.para{margin-top:.5lh}.md-list{margin:0;padding-inline-start:1.2em}.md-list li+li{margin-top:.25lh}.para+.md-list,.md-list+.para,.md-list+.md-list{margin-top:.5lh}.md-list li::marker{color:rgb(from currentcolor r g b / .45)}.md-list li.md-task{list-style:none;position:relative}.md-task>input{position:absolute;inset-inline-start:-1.2em;top:calc((1lh - .9em)/2);width:.9em;height:.9em;margin:0;font:inherit}.md-heading{font-weight:600}.md-heading:is([aria-level="1"],[aria-level="2"]){font-size:1.15em}.md-heading:not(:first-child){margin-top:.75lh}.md-heading+*{margin-top:.25lh}.md-rule{margin:.75lh 0;border:0;border-top:1px solid var(--border)}.code-block:not(.token){white-space:normal;margin:.4rem 0;border:1px solid var(--border);border-radius:var(--ui-radius, 4px);background:rgb(from var(--text) r g b / .05);overflow:hidden;width:fit-content;max-width:100%;.code-block-lang{padding:.15rem .5rem;border-bottom:1px solid var(--border);color:var(--muted);font-size:.62rem;font-weight:600;text-transform:uppercase;letter-spacing:.06em}& pre{margin:0;padding:.4rem .55rem;overflow-x:auto;white-space:pre;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.95em;line-height:1.5;color:var(--text)}& code{font:inherit}}
|
|
2
|
+
`,tn=`dialog{width:min(94vw,80rem);max-width:none;max-height:90dvh;margin:auto;padding:0;overflow:hidden}dialog[open]{display:flex;flex-direction:column}header{display:flex;align-items:start;gap:1rem;flex-shrink:0;padding:.8rem 1rem;border-bottom:1px solid var(--border)}header h3{display:flex;align-items:baseline;gap:.6rem;flex:1;min-width:0;margin:0;overflow-wrap:anywhere}header h3>span:last-child{min-width:0}header button{flex-shrink:0;border:0;background:transparent;color:var(--muted);font-family:inherit;font-size:1.4rem;line-height:1;padding:0 .25rem}header button:hover{color:var(--text)}.advisory-description{min-height:0;padding:1rem;overflow:auto;overscroll-behavior:contain;white-space:pre-wrap;overflow-wrap:anywhere;font-size:.85rem;line-height:1.6}a{color:var(--accent)}button:focus-visible,.advisory-description:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}
|
|
3
|
+
`,nn=class extends C{static styles=[...C.styles,L(en),L(e),L(E),L(tn)];static properties={heading:{attribute:!1},severity:{attribute:!1},_body:{state:!0}};connectedCallback(){super.connectedCallback(),this._dispose=S(()=>{if(!this.isCurrent()){this._body=z,queueMicrotask(this._onClose);return}this._body=Ie(Pt(this.markdown))})}disconnectedCallback(){this._dispose?.(),super.disconnectedCallback()}render(){return R`<dialog aria-labelledby="advisory-title" @close="${this._onClose}"><header><h3 id="advisory-title">${D(this.severity===`unknown`?`unrated`:this.severity)}<span>${this.heading}</span></h3><button type="button" aria-label="Close advisory details" @click="${this._onClose}">×</button></header><div class="advisory-description" tabindex="0">${this._body}</div></dialog>`}};customElements.define(`advisory-details-dialog`,nn);function rn(e){return w(`advisory-details-dialog`,e)}var an=`dialog{max-width:640px;width:min(94vw,640px)}header h3{margin:0 0 .4rem}.finding{display:flex;align-items:center;gap:.5rem;flex-wrap:wrap;margin-bottom:.35rem}.loc{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.76rem;color:var(--text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0;flex:1}.desc{font-size:.78rem;color:var(--muted);margin:0 0 .7rem;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}textarea{display:block;width:100%;box-sizing:border-box;min-height:9rem;max-height:50vh;resize:vertical;padding:.55rem .65rem;border:1px solid var(--border);border-radius:var(--ui-radius, 6px);background:var(--bg, var(--surface));color:var(--text);font:inherit;font-size:.88rem;line-height:1.4;&:focus{outline:none;border-color:var(--accent);box-shadow:0 0 0 3px rgb(from var(--accent) r g b / .18)}&::placeholder{color:var(--muted)}}
|
|
4
|
+
`,on=class extends C{static styles=[...C.styles,L(E),L(an)];static properties={initial:{attribute:!1},finding:{attribute:!1},_value:{state:!0}};constructor(){super(),this.initial=``,this.finding=null,this._value=``}beforeOpen(){this._value=this.initial??``}focusInitial(){let e=this.renderRoot.querySelector(`textarea`);if(!e)return;e.focus();let t=e.value.length;try{e.setSelectionRange(t,t)}catch{}}_onInput=e=>{this._value=e.target.value};_onSave=()=>{let e=(this._value??``).trim();if(e===(this.initial??``).trim()){this._finish(null);return}this._finish(e)};_onClear=()=>this._finish(``);_onCancel=()=>this._finish(null);_onKeydown=e=>{e.key===`Enter`&&(e.ctrlKey||e.metaKey)&&(e.preventDefault(),this._onSave())};render(){let e=this.finding??{},t=e.file?e.line?`${e.file}:${e.line}`:e.file:``,n=(this.initial??``).length>0;return R`<dialog @close="${this._onClose}"><header><h3>${n?`Edit comment`:`Add comment`}</h3><div class="finding">${D(Kt(e,A.severityMode))} ${t?R`<span class="loc" data-tooltip-truncated data-tooltip="${t}">${t}</span>`:z}</div>${e.description?R`<div class="desc" data-tooltip-truncated data-tooltip="${e.description}">${e.description}</div>`:z}</header><textarea rows="6" placeholder="Write your notes here. Markdown is not rendered. Ctrl/⌘+Enter to save, Esc to cancel." .value="${this._value}" @input="${this._onInput}" @keydown="${this._onKeydown}"></textarea><footer class="nwd-actions">${n?R`<button type="button" class="danger" @click="${this._onClear}">Clear</button>`:z} <span class="nwd-spacer"></span> <button type="button" @click="${this._onCancel}">Cancel</button> <button type="button" class="primary" @click="${this._onSave}">Save</button></footer></dialog>`}};customElements.define(`comment-dialog`,on);function sn({initial:e=``,finding:t=null}={}){return w(`comment-dialog`,{initial:e,finding:t})}var B=`.lwd-body{margin:.2rem 0 .55rem;color:var(--text);font-size:.86rem;line-height:1.5}.lwd-empty{margin:.2rem 0 .55rem;color:var(--muted);font-size:.78rem}.lwd-note{margin:.2rem 0 .65rem;padding:.55rem .7rem;border:1px solid var(--border);border-radius:var(--ui-radius, 6px);background:rgb(from var(--text) r g b / .03);color:var(--muted);font-size:.78rem;line-height:1.45}.lwd-choice{margin:.2rem 0 .65rem;padding:.55rem .7rem .6rem;border:1px solid var(--border);border-radius:var(--ui-radius, 6px);background:rgb(from var(--text) r g b / .03);& legend{padding:0 .35rem;color:var(--text);font-size:.78rem;font-weight:500;& strong{font-weight:600}}}.lwd-option{display:flex;align-items:flex-start;gap:.5rem;padding:.3rem .15rem;& input[type=radio]{margin:.15rem 0 0;accent-color:var(--accent)}.lwd-option-text{display:flex;flex-direction:column;gap:.1rem;min-width:0}.lwd-option-title{color:var(--text);font-size:.82rem;line-height:1.35}.lwd-option-hint{color:var(--muted);font-size:.72rem;line-height:1.4}}.lwd-list{margin:0;padding-left:1.1rem;color:var(--muted);font-size:.76rem;line-height:1.5;& li+li{margin-top:.2rem}& strong{color:var(--text);font-weight:600}}.lwd-kind-tag{display:inline-block;vertical-align:middle;margin-left:.3rem;padding:0 .35rem;border:1px solid var(--border);border-radius:3px;background:rgb(from var(--text) r g b / .04);color:var(--muted);font-size:.62rem;font-weight:600;text-transform:uppercase;letter-spacing:.06em}.lwd-rename-input{margin-top:.2rem}.lwd-rename-error{margin:.25rem 0 0;color:var(--critical, #f87171);font-size:.72rem;line-height:1.35}
|
|
5
|
+
`,V=class extends C{static styles=[...C.styles,L(B)];firstUpdated(){if(this.signal?.aborted){this._onCancel();return}this.signal?.addEventListener(`abort`,this._onCancel,{once:!0}),super.firstUpdated()}disconnectedCallback(){this.signal?.removeEventListener(`abort`,this._onCancel),super.disconnectedCallback()}focusInitial(){this.renderRoot.querySelector(`button[data-role="cancel"]`)?.focus()}confirmationResult(e){return{confirmed:e&&!this.signal?.aborted}}_finish(e){this._settled||super._finish(this.confirmationResult(!!e))}_onClose=()=>this._finish(!1);_onCancel=()=>this._finish(!1);_onConfirm=()=>this._finish(!0)},cn=class extends V{static properties={bundleName:{type:String},inRemote:{type:Boolean}};constructor(){super(),this.bundleName=``,this.inRemote=!1}_remoteNotice(){return this.inRemote?R`<p class="lwd-note">This bundle is also stored in the workspace's <strong>remote</strong> inventory and will be removed from there too. Newly synced workspace members won't see it; members who already downloaded it keep their local copy (and could re-upload).</p>`:z}render(){return R`<dialog @close="${this._onClose}"><header><h3>Delete bundle</h3></header><p class="lwd-body">Delete <strong>"${this.bundleName}"</strong>?</p>${this._remoteNotice()}<footer class="nwd-actions"><span class="nwd-spacer"></span> <button type="button" data-role="cancel" @click="${this._onCancel}">Cancel</button> <button type="button" class="danger" @click="${this._onConfirm}">Delete</button></footer></dialog>`}};customElements.define(`delete-bundle-dialog`,cn);function ln({name:e,inRemote:t}={}){return w(`delete-bundle-dialog`,{bundleName:e??``,inRemote:!!t})}var un=class extends V{static properties={reportName:{type:String},kindLabel:{type:String},orphanedTriage:{type:Number},sharedTriage:{type:Number},inRemote:{type:Boolean},_triage:{state:!0}};constructor(){super(),this.reportName=``,this.kindLabel=`report`,this.orphanedTriage=0,this.sharedTriage=0,this.inRemote=!1,this._triage=`keep`}confirmationResult(e){return{confirmed:e,triage:this.orphanedTriage>0?this._triage:`keep`}}_onTriageChange=e=>{this._triage=e.target.value};_remoteNotice(){return this.inRemote?R`<p class="lwd-note">This report is also stored in the workspace's <strong>remote</strong> inventory and will be removed from there too. Newly synced workspace members won't see it; members who already downloaded it keep their local copy (and could re-upload).</p>`:z}_triageSection(){let e=this.orphanedTriage,t=this.sharedTriage;return e===0&&t===0?R`<p class="lwd-note">No local triage is attached to this ${this.kindLabel}.</p>`:e===0?R`<p class="lwd-note">Local triage on this report is also attached to ${t===1?`a report`:`reports`} you're keeping. Nothing will be orphaned.</p>`:R`<fieldset class="lwd-choice"><legend><strong>${e}</strong> ${e===1?`triage entry won't`:`triage entries won't`} be reachable from any remaining report. What should happen ${e===1?`to it`:`to them`}?</legend><label class="lwd-option"><input type="radio" name="drd-triage" value="keep" ?checked="${this._triage===`keep`}" @change="${this._onTriageChange}"> <span class="lwd-option-text"><span class="lwd-option-title">Keep</span> <span class="lwd-option-hint">Reapplied automatically if a matching report is imported later.</span></span></label> <label class="lwd-option"><input type="radio" name="drd-triage" value="wipe" ?checked="${this._triage===`wipe`}" @change="${this._onTriageChange}"> <span class="lwd-option-text"><span class="lwd-option-title">Wipe</span> <span class="lwd-option-hint">Drop the orphaned triage from this browser.</span></span></label></fieldset>`}render(){return R`<dialog @close="${this._onClose}"><header><h3>Delete ${this.kindLabel}</h3></header><p class="lwd-body">Delete <strong>"${this.reportName}"</strong>?</p>${this._remoteNotice()} ${this._triageSection()}<footer class="nwd-actions"><span class="nwd-spacer"></span> <button type="button" data-role="cancel" @click="${this._onCancel}">Cancel</button> <button type="button" class="danger" @click="${this._onConfirm}">Delete</button></footer></dialog>`}};customElements.define(`delete-report-dialog`,un);function dn({name:e,kindLabel:t,triageImpact:n,inRemote:r}={}){return w(`delete-report-dialog`,{reportName:e??``,kindLabel:t||`report`,orphanedTriage:n?.orphanedCount??0,sharedTriage:n?.sharedCount??0,inRemote:!!r})}var fn=class extends V{static properties={bundleName:{type:String},workspaceName:{type:String}};constructor(){super(),this.bundleName=``,this.workspaceName=``}render(){return R`<dialog @close="${this._onClose}"><header><h3>Detach bundle</h3></header><p class="lwd-body">Detach <strong>"${this.bundleName}"</strong> from workspace <strong>"${this.workspaceName}"</strong>?</p><p class="lwd-note">This bundle is also stored in the workspace's <strong>remote</strong> inventory and will be removed from there too. Newly synced workspace members won't see it; members who already downloaded it keep their local copy (and could re-upload). The bundle bytes stay on this device — drop it onto another workspace to attach it there.</p><footer class="nwd-actions"><span class="nwd-spacer"></span> <button type="button" data-role="cancel" @click="${this._onCancel}">Cancel</button> <button type="button" class="danger" @click="${this._onConfirm}">Detach</button></footer></dialog>`}};customElements.define(`detach-bundle-dialog`,fn);function pn({name:e,workspaceName:t}={}){return w(`detach-bundle-dialog`,{bundleName:e??``,workspaceName:t??``})}var mn=class extends V{static properties={reportName:{type:String},workspaceName:{type:String}};constructor(){super(),this.reportName=``,this.workspaceName=``}render(){return R`<dialog @close="${this._onClose}"><header><h3>Detach report</h3></header><p class="lwd-body">Detach <strong>"${this.reportName}"</strong> from workspace <strong>"${this.workspaceName}"</strong>?</p><p class="lwd-note">This report is also stored in the workspace's <strong>remote</strong> inventory and will be removed from there too. Newly synced workspace members won't see it; members who already downloaded it keep their local copy (and could re-upload). The report bytes stay on this device — drop it onto another workspace to attach it there.</p><footer class="nwd-actions"><span class="nwd-spacer"></span> <button type="button" data-role="cancel" @click="${this._onCancel}">Cancel</button> <button type="button" class="danger" @click="${this._onConfirm}">Detach</button></footer></dialog>`}};customElements.define(`detach-report-dialog`,mn);function hn({name:e,workspaceName:t}={}){return w(`detach-report-dialog`,{reportName:e??``,workspaceName:t??``})}var gn=class extends V{static properties={reviewTitle:{type:String}};render(){return R`<dialog aria-labelledby="discard-review-title" @close="${this._onClose}"><header><h3 id="discard-review-title">Discard unpublished review?</h3></header><p class="lwd-body"><strong>${this.reviewTitle}</strong></p><p class="lwd-note">This permanently deletes this review and all its file verdicts and notes. This cannot be undone.</p><footer class="nwd-actions"><span class="nwd-spacer"></span> <button type="button" data-role="cancel" @click="${this._onCancel}">Cancel</button> <button type="button" class="danger" @click="${this._onConfirm}">Discard review</button></footer></dialog>`}};customElements.define(`discard-review-dialog`,gn);function _n(e){return w(`discard-review-dialog`,e)}var vn=class extends C{static styles=[...C.styles,L(B)];static properties={bundleName:{type:String},encrypted:{type:Boolean}};constructor(){super(),this.bundleName=``,this.encrypted=!1}focusInitial(){this.renderRoot.querySelector(`button[data-role="cancel"]`)?.focus()}_finish(e){this._settled||super._finish({confirmed:!!e})}_onClose=()=>this._finish(!1);_onCancel=()=>this._finish(!1);_onConfirm=()=>this._finish(!0);_securityNote(){return this.encrypted?R`<p class="lwd-note">The bundle is saved <strong>unencrypted</strong> — even though your vault keeps its stored copy encrypted — and the file contains <strong>all of the bundle's related sources</strong>. Anyone with access to the downloaded file can read every bundled source, so store or share it with care.</p>`:R`<p class="lwd-note">The downloaded file is <strong>unencrypted</strong> and contains <strong>all of the bundle's related sources</strong>. Anyone with access to it can read every bundled source, so store or share it with care.</p>`}render(){return R`<dialog @close="${this._onClose}"><header><h3>Download bundle</h3></header><p class="lwd-body">Download <strong>"${this.bundleName}"</strong> to disk?</p>${this._securityNote()}<footer class="nwd-actions"><span class="nwd-spacer"></span> <button type="button" data-role="cancel" @click="${this._onCancel}">Cancel</button> <button type="button" class="primary" @click="${this._onConfirm}">Download</button></footer></dialog>`}};customElements.define(`download-bundle-dialog`,vn);function yn({name:e,encrypted:t}={}){return w(`download-bundle-dialog`,{bundleName:e??``,encrypted:!!t})}var bn=`dialog{max-width:515px;width:min(94vw,515px)}.ecd-count{margin:0;font-size:1.02rem;color:var(--text);& strong{color:var(--accent);font-weight:700}}.ecd-excluded{margin:.25rem 0 0;color:var(--muted);font-size:.82rem}.ecd-filters{margin:.9rem 0 0;display:grid;grid-template-columns:auto minmax(0,1fr);gap:.3rem .7rem;font-size:.82rem}.ecd-filters dt{color:var(--muted);font-weight:600;white-space:nowrap}.ecd-filters dd{display:flex;align-items:baseline;gap:.5rem;color:var(--text);overflow-wrap:anywhere}.ecd-filter-value{min-width:0}.ecd-filter-drop{margin-left:auto;flex:none;padding:0 .2rem;border:0;border-radius:var(--ui-radius, 4px);background:none;color:var(--muted);font:inherit;font-size:1rem;line-height:1;&:hover{color:var(--critical, #f87171)}&:focus-visible{outline:2px solid var(--accent);outline-offset:1px}}.ecd-filters-label{margin:.9rem 0 0;font-size:.76rem;font-weight:600;text-transform:uppercase;letter-spacing:.05em;color:var(--muted)}.ecd-filters-label+.ecd-filters{margin-top:.35rem}.ecd-samples{display:flex;align-items:flex-start;gap:.5rem;margin-top:1rem;font-size:.82rem;color:var(--text)}.ecd-samples input{margin-top:.15rem;accent-color:var(--accent)}.ecd-samples-hint{display:block;margin-top:.2rem;color:var(--muted);font-size:.76rem}
|
|
6
|
+
`,xn=`.dlg-tabs{display:flex;gap:.1rem;margin:0 0 .55rem;border-bottom:1px solid var(--border)}.dlg-tab{display:inline-flex;align-items:center;gap:.35rem;background:transparent;border:none;color:var(--muted);padding:.1rem .6rem .4rem;font:inherit;font-size:.85rem;font-weight:600;border-bottom:2px solid transparent;margin-bottom:-1px;transition:color .12s,border-color .12s;&:first-child{padding-left:0}&:hover:not(:disabled){color:var(--text)}&[aria-selected=true]{color:var(--accent);border-bottom-color:var(--accent)}&:disabled{opacity:.5}& svg{display:block}}
|
|
7
|
+
`,Sn=[{id:`download`,label:`Markdown`,icon:t},{id:`print`,label:`Print`,icon:n}],Cn=class extends C{static styles=[...C.styles,L(bn),L(xn)];static properties={mode:{type:String},included:{type:Number},excluded:{type:Number},total:{type:Number},filters:{attribute:!1},fields:{attribute:!1},bucketLabel:{type:String},focusedOnly:{type:Boolean},oneSamplePerGroup:{type:Boolean}};constructor(){super(),this.mode=`download`,this.included=0,this.excluded=0,this.total=0,this.filters=[],this.fields=null,this._relaxed=!1,this.bucketLabel=null,this.focusedOnly=!1,this.oneSamplePerGroup=!1}get _focusedOnly(){return this.mode===`print`&&this.focusedOnly}_selectTab(e){this.mode!==e&&(this.mode=e)}focusInitial(){let e=this.included===0?`button[data-role="cancel"]`:`button.primary`;this.renderRoot.querySelector(e)?.focus()}_finish(e){this._settled||super._finish({confirmed:e===`confirm`,view:e===`view`,fields:this._relaxed?this.fields:null,mode:this.mode,oneSamplePerGroup:this.oneSamplePerGroup})}_onClose=()=>this._finish(`cancel`);_onCancel=()=>this._finish(`cancel`);_onView=()=>this._finish(`view`);_onConfirm=()=>this._finish(`confirm`);async _onDropFilter(e){let t=this.filters.findIndex(t=>t.key===e);if(t===-1)return;let n=r(this.mode,{...this.fields,...this.filters[t].clear});this._relaxed=!0,this.fields=n.fields,this.filters=n.filters,this.included=n.included,this.excluded=n.excluded,this.total=n.total,await this.updateComplete;let i=[...this.renderRoot.querySelectorAll(`button[data-drop]`)];(i[Math.min(t,i.length-1)]??this.renderRoot.querySelector(`button.primary`)??this.renderRoot.querySelector(`button[data-role="cancel"]`))?.focus()}_countSection(){if(this.total===0)return R`<p class="ecd-count">No findings to ${this.mode===`print`?`print`:`download`}.</p>`;if(this._focusedOnly&&this.included>0)return R`<p class="ecd-count">Only the <strong>focused</strong> finding will be printed.</p><p class="ecd-excluded">Focus mode prints just the finding you're viewing. Switch to the list or grouped view to print all ${this.included} matching ${this.included===1?`finding`:`findings`}.</p>`;let e=this.excluded===0&&this.filters.length===0?R`All <strong>${this.included}</strong> ${this.included===1?`finding`:`findings`} included.`:R`<strong>${this.included}</strong> of ${this.total} ${this.total===1?`finding`:`findings`} included.`;return R`<p class="ecd-count">${e}</p>${this.excluded>0?R`<p class="ecd-excluded">${this.excluded} filtered out and ${this.mode===`print`?`won't be printed`:`won't be downloaded`}.</p>`:z}`}_filtersSection(){return this.filters.length===0?R`<p class="ecd-filters-label">No filters active — the full ${this.bucketLabel?`${this.bucketLabel.toLowerCase()} `:``}set is included.</p>`:R`<p class="ecd-filters-label">Active filters</p><dl class="ecd-filters">${this.filters.map(e=>R`<dt>${e.label}</dt><dd><span class="ecd-filter-value">${e.value}</span> <button type="button" class="ecd-filter-drop" data-drop="${e.key}" aria-label="${`Drop the ${e.label.toLowerCase()} filter (${e.value}) from this export`}" @click="${()=>this._onDropFilter(e.key)}">×</button></dd>`)}</dl>`}render(){let e=this.mode===`print`,t=this._focusedOnly?`You're in focus mode — printing outputs just the finding you're focused on.`:e?`Prints only the findings matching your current filters — the same set shown on screen. Use the toolbar to change the selection first.`:`Downloads a Markdown file of the findings matching your current filters — the same set shown on screen. Use the toolbar to change the selection first.`;return R`<dialog @close="${this._onClose}"><header>${ht({tabs:Sn,current:this.mode,prefix:`ecd`,select:e=>this._selectTab(e)})}</header><div role="tabpanel" id="${`ecd-panel-${this.mode}`}" aria-labelledby="${`ecd-tab-${this.mode}`}"><p class="nwd-intro">${t}</p>${this._countSection()} ${this.bucketLabel?R`<p class="nwd-note">Scoped to the <strong>${this.bucketLabel}</strong> list — live findings are not included.</p>`:z} ${this._filtersSection()} <label class="ecd-samples"><input type="checkbox" .checked="${this.oneSamplePerGroup}" @change="${e=>{this.oneSamplePerGroup=e.target.checked}}"> <span>One sample per group<span class="ecd-samples-hint">Include only the currently selected sample from each group.</span></span></label></div><footer class="nwd-actions"><span class="nwd-spacer"></span> <button type="button" data-role="cancel" @click="${this._onCancel}">Cancel</button> ${e?z:R`<button type="button" data-role="view" ?disabled="${this.included===0}" @click="${this._onView}">View</button>`} <button type="button" class="primary" ?disabled="${this.included===0}" @click="${this._onConfirm}">${e?`Print`:`Download`}</button></footer></dialog>`}};customElements.define(`export-confirm-dialog`,Cn);function wn(e=`download`){let t=r(e),n=e===`print`?t.focusedOnly:r(`print`).focusedOnly;return new Promise(r=>{let i=document.createElement(`export-confirm-dialog`);Object.assign(i,{mode:e,included:t.included,excluded:t.excluded,total:t.total,filters:t.filters,fields:t.fields,bucketLabel:t.bucketLabel,focusedOnly:n});let a=e=>{i.remove(),r(e)};i.addEventListener(`resolve`,t=>a({confirmed:!!t.detail?.confirmed,view:!!t.detail?.view,fields:t.detail?.fields??null,mode:t.detail?.mode??e,oneSamplePerGroup:!!t.detail?.oneSamplePerGroup})),i.addEventListener(`modal-conflict`,()=>a({confirmed:!1,view:!1,fields:null,mode:e})),document.body.append(i)})}var Tn=160,En=320,Dn=/^ {0,3}#{1,6}(?: |$)/u,On=/^ {0,3}(?:=+|-+) *$/u,H=/^\|?[^|\n]+(?:\|[^|\n]+)+\|?$/u,kn=/^\|?[ \t]*:?-{3,}:?[ \t]*(?:\|[ \t]*:?-{3,}:?[ \t]*)+\|?$/u;function An(e,t=e.join(`
|
|
8
|
+
`),{target:n=Tn,max:r=En}={}){let i=e.length;if(i===0)return[];let a=Wt(t),o=0,s=0,c=()=>{for(;o<a.length&&a[o][1]<=s-1;)o++;return o<a.length&&s-1>=a[o][0]},l=t=>e[t].trim()===``,u=t=>H.test(e[t]),d=t=>!c()&&!(kn.test(e[t])&&u(t-1))&&!(On.test(e[t])&&!l(t-1)),f=t=>l(t-1)||Dn.test(e[t]),p=[],m=0;for(let t=1;t<i;t++){s+=e[t-1].length+1;let i=t-m;i<n||!d(t)||(i>=r||f(t))&&(p.push([m,t]),m=t)}return p.push([m,i]),p}function jn(e,t){if(t<2||!H.test(e[t])||!H.test(e[t-1]))return null;let n=t-1;for(;n>0&&H.test(e[n-1]);)n--;for(let r=n+1;r<t;r++)if(kn.test(e[r]))return[r-1,r+1];return null}function Mn(e){return e.replaceAll(`&`,`&`).replaceAll(`<`,`<`).replaceAll(`>`,`>`)}var Nn=`dialog{max-width:none;max-height:none;width:calc(100vw - 4rem);height:calc(100vh - 4rem);margin:2rem auto;position:relative;padding:12px}dialog[open]{display:flex;flex-direction:column}dialog::backdrop{backdrop-filter:none}header{display:flex;align-items:center;gap:.45rem;margin-bottom:.6rem;padding-right:3rem;user-select:none}header h3{margin:0}.evd-close{position:absolute;top:6px;right:6px;z-index:1;width:22px;height:22px;display:grid;place-items:center;background:transparent;border:0;border-radius:var(--ui-radius, 4px);color:var(--muted);font:500 16px/1 ui-monospace,monospace;cursor:default;&:hover{color:var(--accent);background:var(--surface-active)}&:focus-visible{outline:2px solid var(--accent);outline-offset:1px}}.evd-code{flex:1 1 auto;min-height:0;margin:0;overflow:auto;border:1px solid var(--border);border-radius:var(--ui-radius, 6px);background:var(--bg, var(--surface));color:var(--text);font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.78rem;line-height:1.55;scrollbar-width:thin;scrollbar-color:var(--border) transparent;&::-webkit-scrollbar{width:6px;height:6px}&::-webkit-scrollbar-thumb{background:var(--border);border-radius:3px}}.evd-lines{--evd-gutter: calc(var(--evd-lineno-width, 3ch) + 1.4rem);padding:.5rem 0;background-image:linear-gradient(to right,rgb(from var(--text) r g b / .03) 0 calc(var(--evd-gutter) - 1px),var(--border) calc(var(--evd-gutter) - 1px) var(--evd-gutter),transparent var(--evd-gutter))}.evd-chunk{display:grid;grid-template-columns:var(--evd-gutter) minmax(0,1fr);align-items:start;content-visibility:auto;contain-intrinsic-size:auto calc(var(--evd-chunk-lines, 1) * 1.55em)}.evd-chunk.evd-pending{display:block;padding:0 .75rem 0 calc(var(--evd-gutter) + .6rem);white-space:pre-wrap;overflow-wrap:anywhere}.evd-lineno{padding:0 .55rem 0 .4rem;color:var(--muted);text-align:right;font-variant-numeric:tabular-nums;user-select:none}.evd-line{padding:0 .75rem 0 .6rem;font:inherit;background:none;white-space:pre-wrap;overflow-wrap:anywhere}.evd-meta{color:var(--muted);font-size:.74rem;font-variant-numeric:tabular-nums}.evd-copy{margin-left:auto;flex-shrink:0;height:1.75rem;padding:0 .55rem;gap:.35rem;display:inline-flex;align-items:center;background:transparent;color:var(--muted);border:1px solid var(--border);border-radius:var(--ui-radius, 3px);font:inherit;font-size:.72rem;font-weight:500;letter-spacing:.02em;cursor:default;&:hover{color:var(--accent);background:var(--surface-active)}&:focus-visible{outline:2px solid var(--accent);outline-offset:1px}&.evd-done{color:var(--green, #4ade80);background:color-mix(in srgb,var(--green, #4ade80) 14%,transparent)}&.evd-failed{color:var(--critical, #f87171);background:color-mix(in srgb,var(--critical, #f87171) 14%,transparent)}}
|
|
9
|
+
`,Pn=R`<svg viewBox="0 0 16 16" width="12" height="12" aria-hidden="true"><rect x="3" y="2.5" width="8" height="10" rx="1" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linejoin="round"/><rect x="5.5" y="5" width="8" height="9" rx="1" fill="none" stroke="currentColor" stroke-width="1.2" stroke-linejoin="round"/></svg>`;function Fn(e,t,n,r=null){let i=``;for(let a=t;a<n;a++){let n=r===null?Mn(e[a]):r[a-t];i+=`<span class="evd-lineno" aria-hidden="true">${a+1}</span><code class="evd-line">${n}
|
|
10
|
+
</code>`}return i}var In=class extends C{static styles=[...C.styles,L(e),L(Nn)];static properties={markdown:{attribute:!1},_copied:{state:!0}};_lines=[];_kb=`0.0`;_chunks=[];_copiedTimer=null;constructor(){super(),this.markdown=``,this._copied=``}focusInitial(){this.renderRoot.querySelector(`button[data-role="cancel"]`)?.focus()}willUpdate(e){if(!e.has(`markdown`))return;let t=this.markdown.split(`
|
|
11
|
+
`),n=t.length>1&&t.at(-1)===``?t.length-1:t.length;this._lines=t.slice(0,n),this._kb=(new TextEncoder().encode(this.markdown).length/1024).toFixed(1),this._chunks=An(this._lines,this.markdown).map(([e,t])=>({start:e,end:t,asked:!1}))}render(){let e=this._lines.length,t=this._copied===`done`?`Copied`:this._copied===`failed`?`Copy failed`:`Copy`,n=this._copied===`failed`?`Select the text and copy it by hand`:z;return R`<dialog @close="${this._onClose}"><button type="button" class="evd-close" data-role="cancel" aria-label="Close" @click="${this._onClose}">×</button><header><h3>Report markdown</h3><span class="evd-meta">· ${e} ${e===1?`line`:`lines`} · ${this._kb} KB</span> <button type="button" class="evd-copy ${this._copied===`done`?`evd-done`:this._copied===`failed`?`evd-failed`:``}" data-role="copy" data-tooltip="${n}" @click="${this._onCopy}">${Pn}${t}</button></header><div class="evd-code" tabindex="0"><div class="evd-lines" style="${N({"--evd-lineno-width":`${String(e).length}ch`})}">${this._chunks.map((e,t)=>R`<div class="evd-chunk evd-pending" data-chunk="${t}" style="${N({"--evd-chunk-lines":`${e.end-e.start}`})}"></div>`)}</div></div></dialog>`}updated(){if(this.isConnected)for(let e of this.renderRoot.querySelectorAll(`.evd-chunk:not([data-watched])`)){e.dataset.watched=``;let t=Number(e.dataset.chunk),n=this._chunks[t];e.textContent=`${this._lines.slice(n.start,n.end).join(`
|
|
12
12
|
`)}
|
|
13
|
-
`,
|
|
14
|
-
`),a=await
|
|
15
|
-
`,
|
|
16
|
-
`,
|
|
17
|
-
`).map((t,n)=>
|
|
18
|
-
`,Xn=class extends b{static styles=[...b.styles,I(C),I(Yn)];static properties={initial:{attribute:!1},finding:{attribute:!1},canApplyToGroup:{attribute:!1},_value:{state:!0}};constructor(){super(),this.initial=``,this.finding=null,this.canApplyToGroup=!1,this._value=``}beforeOpen(){this._value=this.initial??``}focusInitial(){let e=this.renderRoot.querySelector(`input[type="url"]`);if(e){e.focus();try{e.select()}catch{}}}_onInput=e=>{this._value=e.target.value};get _trimmed(){return(this._value??``).trim()}_onSave=()=>{let e=(this.initial??``).trim();if(this._trimmed===e){this._finish(null);return}this._finish({value:this._trimmed,scope:`finding`})};_onApplyToGroup=()=>{this._trimmed&&this._finish({value:this._trimmed,scope:`group`})};_onClear=()=>this._finish({value:``,scope:`finding`});_onCancel=()=>this._finish(null);_onKeydown=e=>{e.key===`Enter`&&(e.preventDefault(),this._onSave())};render(){let e=this.finding??{},t=e.file?e.line?`${e.file}:${e.line}`:e.file:``,n=(this.initial??``).length>0,r=this._trimmed,i=Ht(r);return L`<dialog @close="${this._onClose}"><header><h3>${n?`Edit fix link`:`Add fix link`}</h3><div class="finding">${w(qt(e,k.severityMode))} ${t?L`<span class="loc" data-tooltip-truncated data-tooltip="${t}">${t}</span>`:R}</div>${e.description?L`<div class="desc" data-tooltip-truncated data-tooltip="${e.description}">${e.description}</div>`:R}</header><div class="input-row"><input type="url" inputmode="url" autocomplete="off" spellcheck="false" placeholder="https://github.com/owner/repo/pull/123" .value="${this._value}" @input="${this._onInput}" @keydown="${this._onKeydown}"> ${i?L`<a class="open" href="${r}" target="_blank" rel="noopener noreferrer">Open ↗</a>`:R}</div><p class="hint">PR URL, issue link, commit, or any free-form reference. Enter to save, Esc to cancel.</p><footer class="nwd-actions">${n?L`<button type="button" class="danger" @click="${this._onClear}">Clear</button>`:R} <span class="nwd-spacer"></span> ${this.canApplyToGroup?L`<button type="button" class="quiet" ?disabled="${!r}" @click="${this._onApplyToGroup}">Apply to whole group</button> <span class="nwd-spacer"></span>`:R} <button type="button" @click="${this._onCancel}">Cancel</button> <button type="button" class="primary" @click="${this._onSave}">Save</button></footer></dialog>`}};customElements.define(`fix-link-dialog`,Xn);function Zn({initial:e=``,finding:t=null,canApplyToGroup:n=!1}={}){return x(`fix-link-dialog`,{initial:e,finding:t,canApplyToGroup:n})}var Qn=class extends b{static styles=[...b.styles,I(z)];static properties={reportName:{type:String},workspaceNames:{type:Array},existingNames:{type:Object},_choice:{state:!0},_newName:{state:!0}};constructor(){super(),this.reportName=``,this.workspaceNames=[],this.existingNames=new Set,this._choice=`replace`,this._newName=``}focusInitial(){this.renderRoot.querySelector(`button[data-role="cancel"]`)?.focus()}_onClose=()=>this._finish({action:`cancel`});_onCancel=()=>this._finish({action:`cancel`});_onChoiceChange=e=>{this._choice=e.target.value};_onNewNameInput=e=>{this._newName=e.target.value};_onNewNameKeydown=e=>{e.key===`Enter`&&(e.preventDefault(),this._onConfirm())};_onConfirm=()=>{if(this._choice===`rename`){let e=(this._newName??``).trim();if(this._renameError(e)!==null)return;this._finish({action:`rename`,newName:e});return}this._finish({action:`replace`})};_replaceHint(){let e=this.workspaceNames.length;if(e===0)return L`<span class="lwd-option-hint">The local file will be overwritten.</span>`;let t=e===1?L`workspace <strong>"${this.workspaceNames[0]}"</strong>`:L`<strong>${e}</strong> workspaces (${this.workspaceNames.join(`, `)})`;return L`<span class="lwd-option-hint">The local file will be overwritten. When sync is online, the new bytes are also uploaded to ${t} so the cloud copy matches.</span>`}_renameError(e){return e?e===this.reportName?`Pick a name different from the original.`:e.includes(`\0`)?`The new name cannot contain a NUL byte.`:e.includes(`/`)||e.includes(`\\`)?`The new name cannot contain "/" or "\\".`:this.existingNames.has(e)?`A file named "${e}" already exists \u2014 pick something else.`:null:`Enter a new name.`}render(){let e=this._choice===`rename`,t=(this._newName??``).trim(),n=e?this._renameError(t):null,r=n!==null,i=e?`Rename`:`Replace`,a=e?`primary`:`danger`,o=this.workspaceNames.length>0?L`<span class="lwd-option-hint">Saved as an unfiled report — the renamed copy isn't added to any workspace.</span>`:R;return L`<dialog @close="${this._onClose}"><header><h3>Import conflict</h3></header><p class="lwd-body"><strong>"${this.reportName}"</strong> already exists with different content.</p><fieldset class="lwd-choice"><legend>What should happen?</legend><label class="lwd-option"><input type="radio" name="icd-choice" value="replace" ?checked="${this._choice===`replace`}" @change="${this._onChoiceChange}"> <span class="lwd-option-text"><span class="lwd-option-title">Replace</span> ${this._replaceHint()}</span></label> <label class="lwd-option"><input type="radio" name="icd-choice" value="rename" ?checked="${this._choice===`rename`}" @change="${this._onChoiceChange}"> <span class="lwd-option-text"><span class="lwd-option-title">Rename to</span> <input type="text" class="nwd-input lwd-rename-input" maxlength="200" .value="${this._newName}" ?disabled="${!e}" @input="${this._onNewNameInput}" @keydown="${this._onNewNameKeydown}" @focus="${()=>{this._choice=`rename`}}"> ${o} ${n===null?R:L`<p class="lwd-rename-error">${n}</p>`}</span></label></fieldset><footer class="nwd-actions"><span class="nwd-spacer"></span> <button type="button" data-role="cancel" @click="${this._onCancel}">Cancel</button> <button type="button" class="${a}" ?disabled="${r}" @click="${this._onConfirm}">${i}</button></footer></dialog>`}};customElements.define(`import-conflict-dialog`,Qn);function $n({name:e,workspaceNames:t,existingNames:n}={}){let r=n instanceof Set?n:new Set(n??[]);return new Promise(n=>{let i=document.createElement(`import-conflict-dialog`);Object.assign(i,{reportName:e??``,workspaceNames:Array.isArray(t)?t:[],existingNames:r,_newName:er(e??``,r)}),i.addEventListener(`resolve`,e=>{i.remove(),n(e.detail)}),i.addEventListener(`modal-conflict`,()=>{i.remove(),n({action:`cancel`})}),document.body.append(i)})}function er(e,t){if(typeof e!=`string`||e.length===0)return``;let n=e.lastIndexOf(`.`),r=n>0?e.slice(0,n):e,i=n>0?e.slice(n):``,a=r.replace(/-\d+$/u,``);for(let e=2;e<1e3;e++){let n=`${a}-${e}${i}`;if(!t.has(n))return n}return``}var tr=class extends b{static styles=[...b.styles,I(z)];static properties={workspaceName:{type:String},reportCount:{type:Number},bundleCount:{type:Number},orphanedTriage:{type:Number},sharedTriage:{type:Number},_mode:{state:!0},_triage:{state:!0},_settled:{state:!0}};constructor(){super(),this.workspaceName=``,this.reportCount=0,this.bundleCount=0,this.orphanedTriage=0,this.sharedTriage=0,this._mode=`detach`,this._triage=`keep`,this._settled=!1}focusInitial(){this.renderRoot.querySelector(`button[data-role="cancel"]`)?.focus()}_finish(e){if(this._settled)return;let t=this.reportCount>0?this._mode:`detach`,n=t===`delete`&&this.orphanedTriage>0?this._triage:`keep`;super._finish({confirmed:!!e,mode:t,triage:n})}_onClose=()=>this._finish(!1);_onCancel=()=>this._finish(!1);_onConfirm=()=>this._finish(!0);_onModeChange=e=>{this._mode=e.target.value};_onTriageChange=e=>{this._triage=e.target.value};_triageSection(){if(this._mode!==`delete`||this.reportCount===0)return``;let e=this.orphanedTriage,t=this.sharedTriage;return e===0&&t===0?L`<p class="lwd-note">No local triage is attached to ${this.reportCount===1?`this report`:`these reports`}.</p>`:e===0?L`<p class="lwd-note">Local triage on ${this.reportCount===1?`this report`:`these reports`} is also attached to ${t===1?`a report`:`reports`} you're keeping. Nothing will be orphaned.</p>`:L`<fieldset class="lwd-choice"><legend><strong>${e}</strong> ${e===1?`triage entry won't`:`triage entries won't`} be reachable from any remaining report. What should happen ${e===1?`to it`:`to them`}?</legend><label class="lwd-option"><input type="radio" name="lwd-triage" value="keep" ?checked="${this._triage===`keep`}" @change="${this._onTriageChange}"> <span class="lwd-option-text"><span class="lwd-option-title">Keep</span> <span class="lwd-option-hint">Reapplied automatically if a matching report is imported later.</span></span></label> <label class="lwd-option"><input type="radio" name="lwd-triage" value="wipe" ?checked="${this._triage===`wipe`}" @change="${this._onTriageChange}"> <span class="lwd-option-text"><span class="lwd-option-title">Wipe</span> <span class="lwd-option-hint">Drop the orphaned triage from this browser.</span></span></label></fieldset>`}render(){let e=this.reportCount,t=this.bundleCount,n=e>0,r=e===1?`report`:`reports`,i=e===1?`it`:`them`,a=e===1?`stays`:`stay`,o=e===1?`is removed`:`are removed`,s=t>0?L`<p class="lwd-note">${t===1?`One bundle is`:`${t} bundles are`} also attached. ${t===1?`It`:`They`} will become unfiled — the bundle bytes stay on this device either way (delete bundles manually from the Bundles list if you want them gone).</p>`:``,c=n?L`<fieldset class="lwd-choice"><legend>What should happen to the <strong>${e}</strong> attached ${r}?</legend><label class="lwd-option"><input type="radio" name="lwd-mode" value="detach" ?checked="${this._mode===`detach`}" @change="${this._onModeChange}"> <span class="lwd-option-text"><span class="lwd-option-title">Move ${i} out of the workspace</span> <span class="lwd-option-hint">The ${r} ${a} in this browser as unattached ${r}.</span></span></label> <label class="lwd-option"><input type="radio" name="lwd-mode" value="delete" ?checked="${this._mode===`delete`}" @change="${this._onModeChange}"> <span class="lwd-option-text"><span class="lwd-option-title">Delete ${i} along with the workspace</span> <span class="lwd-option-hint">The ${r} ${o} from this browser.</span></span></label></fieldset>`:t>0?L`<p class="lwd-empty">No reports are attached to this workspace (only bundles, see below).</p>`:L`<p class="lwd-empty">No reports are attached to this workspace.</p>`;return L`<dialog @close="${this._onClose}"><header><h3>Leave workspace</h3></header><p class="lwd-body">Leave workspace <strong>"${this.workspaceName}"</strong> on this device?</p>${c} ${s} ${this._triageSection()}<ul class="lwd-list"><li>Workspace synchronization data stays on the sync server — peers (and your other devices) keep their copy.</li></ul><footer class="nwd-actions"><span class="nwd-spacer"></span> <button type="button" data-role="cancel" @click="${this._onCancel}">Cancel</button> <button type="button" class="danger" @click="${this._onConfirm}">Leave</button></footer></dialog>`}};customElements.define(`leave-workspace-dialog`,tr);function nr({name:e,reportCount:t,bundleCount:n,triageImpact:r}={}){return x(`leave-workspace-dialog`,{workspaceName:e??``,reportCount:t??0,bundleCount:n??0,orphanedTriage:r?.orphanedCount??0,sharedTriage:r?.sharedCount??0})}var rr=`dialog{width:min(42rem,90vw)}.content-import-groups{max-height:55vh;overflow:auto}fieldset{margin:.7rem 0;padding:.5rem .75rem;border:1px solid var(--border);border-radius:var(--ui-radius, 6px)}legend{padding:0 .35rem;font-size:.85rem;font-weight:600}.content-import-item{display:flex;gap:.65rem;align-items:flex-start;padding:.65rem 0}.content-import-item+.content-import-item{border-top:1px solid var(--border)}.content-import-item input{margin:.15rem 0 0;accent-color:var(--accent)}.content-import-item.unavailable{opacity:.55;cursor:default}.content-import-label{display:grid;gap:.25rem;min-width:0;overflow-wrap:anywhere;font-size:.82rem}.content-import-status{display:flex;flex-wrap:wrap;gap:.4rem;color:var(--muted);font-size:.74rem}.content-import-present{border:1px solid var(--border);border-radius:3px;padding:0 .3rem}input:focus-visible{outline:2px solid var(--accent);outline-offset:2px}button:disabled{opacity:.5}
|
|
19
|
-
`,
|
|
20
|
-
`,
|
|
13
|
+
`,Ke(e,n=>{n&&(Xe(e),this._paint(e,t))})}}disconnectedCallback(){for(let e of this.renderRoot.querySelectorAll(`.evd-chunk[data-watched]`))Xe(e);clearTimeout(this._copiedTimer),super.disconnectedCallback()}_onCopy=async()=>{let e=`done`;try{await navigator.clipboard.writeText(this.markdown)}catch{e=`failed`}this.isConnected&&(this._copied=e,clearTimeout(this._copiedTimer),this._copiedTimer=setTimeout(()=>{this._copied=``,this._copiedTimer=null},1500))};async _paint(e,t){let n=this._chunks[t];if(!n||n.asked)return;n.asked=!0;let r=jn(this._lines,n.start),i=[...r?this._lines.slice(r[0],r[1]):[],...this._lines.slice(n.start,n.end)].join(`
|
|
14
|
+
`),a=await bt(i,`markdown`);if(!this.isConnected)return;let o=a===null?null:xt(a).slice(r?r[1]-r[0]:0);e.innerHTML=Lt(Fn(this._lines,n.start,n.end,o?.length===n.end-n.start?o:null)),e.classList.remove(`evd-pending`)}};customElements.define(`export-view-dialog`,In);function Ln(e){return new Promise(t=>{let n=document.createElement(`export-view-dialog`);n.markdown=e;let r=()=>{n.remove(),t()};n.addEventListener(`resolve`,r),n.addEventListener(`modal-conflict`,r),document.body.append(n)})}var Rn={triage:`Status`,color:`Label`,flagged:`Flagged`,fix:`Fix`,comment:`Comment`},zn={inprogress:`In progress`,fixed:`Fixed`,invalid:`Invalid`,deleted:`Deleted`,ignored:`Ignored`};function Bn(e,t){return t===`triage`?zn[e?.triage]??`Untriaged`:t===`flagged`?e?.flagged?`Yes`:`No`:e?.[t]||`None`}function Vn(e,t){return Object.entries(Rn).flatMap(([n,r])=>{let i=Bn(e,n);if(t===void 0)return e?.[n]===void 0?[]:[{label:r,after:i}];let a=Bn(t,n);return a===i?[]:[{label:r,before:a,after:i}]})}var Hn=`dialog{width:min(94vw,720px);max-width:720px;max-height:calc(100dvh - 4rem);margin-block:2rem;padding:0;overflow:hidden;background:rgb(from var(--surface) r g b / 1)}dialog[open]{display:flex;flex-direction:column}header{display:flex;align-items:start;justify-content:space-between;gap:1rem;padding:.75rem 1rem;border-bottom:1px solid var(--border);flex:none}.history-heading{min-width:0}header h3{display:flex;align-items:center;gap:.55rem;margin:0;line-height:1.5}header svg{flex:none;color:var(--muted)}header .detail-action{flex:none;width:1.75rem;height:1.75rem}.location{margin-top:.4rem;color:var(--muted);font:.76rem/1.5 ui-monospace,monospace;overflow-wrap:anywhere}.history-summary{display:flex;justify-content:space-between;gap:.5rem;flex:none;padding:.5rem 1rem .25rem;color:var(--muted);font-size:.72rem}.history-body{min-height:0;overflow:auto;overscroll-behavior:contain;padding:.75rem 1rem 1rem;font-size:.82rem;line-height:1.5}.timeline{list-style:none}.event{position:relative;display:grid;grid-template-columns:2rem minmax(0,1fr);gap:.75rem;padding-bottom:1rem}.event:last-child{padding-bottom:0}.event:not(:last-child):before{content:"";position:absolute;top:2.4rem;bottom:.4rem;left:calc(1rem - .5px);width:1px;background:var(--border)}.managed-comment-avatar{width:2rem;height:2rem;font-size:.8rem;background:var(--surface-active)}.event-content{min-width:0}.event-meta{display:flex;flex-wrap:wrap;align-items:baseline;justify-content:space-between;gap:.15rem .75rem}.event-user{display:flex;flex-wrap:wrap;align-items:baseline;gap:.1rem .45rem;min-width:0;overflow-wrap:anywhere}.event-user strong{font-weight:600}.username{color:var(--muted);font-size:.75rem}time{color:var(--muted);font-size:.7rem;white-space:nowrap}.event-action{margin-top:.1rem;color:var(--muted);font-size:.75rem}.changes{margin-top:.65rem;border:1px solid var(--border);border-radius:var(--ui-radius, 7px);overflow:hidden}.change{display:grid;grid-template-columns:4.5rem minmax(0,1fr);align-items:baseline;gap:.65rem;padding:.4rem .6rem}.change+.change{border-top:1px solid var(--border)}dt{color:var(--muted);font-size:.75rem}dd{display:grid;grid-template-columns:minmax(0,1fr) auto minmax(0,1fr);align-items:baseline;gap:.65rem}dd.snapshot{grid-template-columns:minmax(0,1fr)}.before,.after{white-space:pre-wrap;overflow-wrap:anywhere}.before,.change-arrow{color:var(--muted)}.after{font-weight:500}.history-state{padding:1.5rem .5rem;text-align:center;color:var(--muted)}.history-state strong{display:block;margin-bottom:.35rem;color:var(--text);font-weight:500}.sr-only{position:absolute;width:1px;height:1px;padding:0;overflow:hidden;clip-path:inset(50%);white-space:nowrap}@media(width<=540px){dialog{margin-block:1rem;max-height:calc(100dvh - 2rem)}header{padding:.65rem}.history-summary{padding-inline:.65rem}.history-body{padding:.65rem}.event{gap:.6rem}.event-meta{flex-direction:column}.change{grid-template-columns:minmax(0,1fr);gap:.3rem;padding:.35rem .5rem}dd{gap:.45rem}}
|
|
15
|
+
`,Un=class extends C{static styles=[...C.styles,L(j),L(i),L(Hn)];static properties={finding:{attribute:!1},teamId:{attribute:!1},_events:{state:!0},_loading:{state:!0},_error:{state:!0}};constructor(){super(),this._events=[],this._loading=!0,this._error=!1}beforeOpen(){this._load()}async _load(){if(!this.isCurrent()){this._finish(null);return}this._loading=!0,this._error=!1;let{_managedReportId:e,id:t}=this.finding;this._key=`finding-history:${this.teamId??``}:${e}:${t}`,f.invalidate([this._key]);let n=f.load(this._key,`issue history`,async n=>{let r=new URLSearchParams({finding:t});this.teamId&&r.set(`team`,this.teamId);let i=await d(`/api/reports/${encodeURIComponent(e)}/triage/history?${r}`,{credentials:`same-origin`,signal:n});if(!i.ok)throw Error(`History request failed (${i.status})`);let a=await i.json();if(a.finding!==t||!Array.isArray(a.events))throw Error(`Invalid history response`);return a.events});this._owner=f.resources.get(this._key),this._signal=this._owner.controller.signal,this._signal.addEventListener(`abort`,this._onClose,{once:!0});try{let e=await n;if(this._settled||!this.isConnected)return;if(!this.isCurrent()){this._finish(null);return}this._events=e}catch(e){if(this._settled||!this.isConnected)return;e.name===`AbortError`||!this.isCurrent()?this._finish(null):this._error=!0}finally{this._loading=!1}}disconnectedCallback(){this._signal?.removeEventListener(`abort`,this._onClose),this._owner&&f.resources.get(this._key)===this._owner&&f.invalidate([this._key]),this._events=[],super.disconnectedCallback()}_onKeydown=e=>{e.key===`Escape`&&e.stopPropagation()};_renderEvent(e,t){let n=this._events[t+1],r=Vn(e.entry,n?.entry),i=new Date(e.at),a=e.actorLogin?`@${e.actorLogin}`:``;return R`<li class="event">${e.actorId?Ee(e.actorId,e.actorName||e.actorLogin):R`<span class="managed-comment-avatar" aria-hidden="true">${(e.actorLogin?.[0]||`?`).toUpperCase()}</span>`}<div class="event-content"><div class="event-meta"><div class="event-user"><strong>${e.actorName||a||`Unknown user`}</strong> ${e.actorName&&a?R`<span class="username">${a}</span>`:z}</div><time datetime="${i.toISOString()}" data-tooltip="${i.toLocaleString()}">${i.toLocaleString(void 0,{dateStyle:`medium`,timeStyle:`short`})}</time></div><p class="event-action">${e.entry===null?`Cleared triage`:n?`Updated triage`:`Recorded triage state`}</p>${r.length>0?R`<dl class="changes">${r.map(e=>R`<div class="change"><dt>${e.label}</dt><dd class="${e.before===void 0?`snapshot`:``}">${e.before===void 0?z:R`<span class="before"><span class="sr-only">Previously: </span>${e.before}</span> <span class="change-arrow" aria-hidden="true">→</span>`} <span class="after"><span class="sr-only">${e.before===void 0?`Recorded: `:`Changed to: `}</span>${e.after}</span></dd></div>`)}</dl>`:z}</div></li>`}render(){let e=this.finding??{};return R`<dialog aria-labelledby="history-title" @close="${this._onClose}" @keydown="${this._onKeydown}"><header><div class="history-heading"><h3 id="history-title"><svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 11a9 9 0 1 1 2.5 7M3 5v6h6M12 7v5l3 2"/></svg>Issue history</h3><p class="location">${e.file}${e.line?`:${e.line}`:``}</p></div><button type="button" class="detail-action" aria-label="Close issue history" @click="${this._onClose}">×</button></header>${!this._loading&&!this._error&&this._events.length>0?R`<div class="history-summary"><span>${this._events.length} ${this._events.length===1?`record`:`records`}</span><span>Newest first</span></div>`:z}<div class="history-body" aria-busy="${String(this._loading)}">${this._loading?R`<p class="history-state" role="status">Loading history…</p>`:this._error?R`<div class="history-state" role="alert"><strong>Couldn’t load issue history</strong><p>Close and reopen to try again.</p></div>`:this._events.length===0?R`<div class="history-state" role="status"><strong>No triage changes yet</strong><p>Changes to status, labels, flags, and fixes will appear here.</p></div>`:R`<ol class="timeline">${this._events.map((e,t)=>this._renderEvent(e,t))}</ol>`}</div></dialog>`}};customElements.define(`finding-history-dialog`,Un);function Wn(e){return e.isCurrent()?w(`finding-history-dialog`,e):Promise.resolve(null)}var Gn=`dialog{width:min(96vw,1400px);max-width:1400px;height:min(88dvh,1000px);max-height:92dvh;margin:auto;padding:0;overflow:hidden}dialog[open]{display:flex;flex-direction:column}header{display:flex;align-items:center;gap:1rem;padding:.7rem 1rem;border-bottom:1px solid var(--border);flex:0 0 auto;& h3{margin:0;min-width:0;flex:1;overflow-wrap:anywhere;font:.85rem ui-monospace,monospace}& button{border:0;background:transparent;color:var(--muted);font-size:1.4rem;padding:0 .3rem;&:hover{color:var(--text)}}}.source-scroll{flex:1;min-height:0;overflow:auto;overscroll-behavior:contain}.source-status{padding:1rem;color:var(--muted)}.source-lines{display:flex;min-width:100%;width:max-content;font:.8rem/1.6 ui-monospace,monospace;& aside{position:sticky;left:0;align-self:stretch;flex:0 0 auto;padding:.75rem 0;background:var(--surface);color:var(--muted);border-right:1px solid var(--border);text-align:right;user-select:none;& div{padding:0 .75rem}.cited{background:rgb(from var(--accent) r g b / .18);color:var(--accent);font-weight:700}}& pre{padding:.75rem 1rem;margin:0;tab-size:4}& pre,code{font:inherit;white-space:pre}}
|
|
16
|
+
`,Kn=class extends C{static styles=[...C.styles,L(Gn),L(e)];static properties={reportId:{attribute:!1},file:{attribute:!1},line:{attribute:!1},_path:{state:!0},_content:{state:!0},_highlighted:{state:!0},_loading:{state:!0}};constructor(){super(),this.reportId=``,this.file=``,this.line=``,this._path=``,this._content=null,this._highlighted=null,this._loading=!0}beforeOpen(){this._load()}async _load(){try{let{data:e,error:t}=await ae(this.reportId).then(e=>({data:e}),e=>({error:e}));if(this._settled||!this.isConnected)return;let n=ne(this.reportId),r=t?n?.error===t:n?.data===e||e===null&&!!n?.error;if(!n||!r||t?.name===`AbortError`||n.controller?.signal.aborted){this._finish(null);return}if(this._sourceSignal=n.controller?.signal,this._sourceSignal?.addEventListener(`abort`,this._onClose,{once:!0}),this._path=e?.paths.get(this.file)??this.file,this._content=e?.sources.get(this._path)??null,this._loading=!1,await this.updateComplete,this._settled||!this.isConnected)return;Dt(this.renderRoot.querySelector(`.source-scroll`),this.renderRoot.querySelectorAll(`.cited`));let i=Nt(this._path,e?.formats?.get(this._path),this._content);if(i&&this._content!==null){let e=await bt(this._content,i);!this._settled&&this.isConnected&&(this._highlighted=e)}}catch(e){e.name===`AbortError`?this._finish(null):this._loading=!1}}disconnectedCallback(){this._sourceSignal?.removeEventListener(`abort`,this._onClose),this._content=null,this._highlighted=null,super.disconnectedCallback()}_onKeydown=e=>{e.key===`Escape`&&e.stopPropagation()};render(){let e=M(this.line);return R`<dialog aria-labelledby="source-title" @close="${this._onClose}" @keydown="${this._onKeydown}" @click="${this._onBackdrop}"><header><h3 id="source-title" class="mono">${this._path||this.file}</h3><button type="button" aria-label="Close source viewer" @click="${this._onClose}">×</button></header><div class="source-scroll" tabindex="0" aria-label="Full source code" aria-busy="${String(this._loading)}">${this._content===null?R`<p class="source-status" role="status">${this._loading?`Loading source…`:`Source unavailable`}</p>`:R`<div class="source-lines"><aside aria-hidden="true">${this._content.split(`
|
|
17
|
+
`).map((t,n)=>R`<div class="${e&&n+1>=e.start&&n+1<=e.end?`cited`:``}">${n+1}</div>`)}</aside><pre><code>${typeof this._highlighted==`string`?I(this._highlighted):this._content}</code></pre></div>`}</div></dialog>`}};customElements.define(`finding-source-dialog`,Kn);function qn({reportId:e,file:t,line:n}){return w(`finding-source-dialog`,{reportId:e,file:t,line:n})}var Jn=`dialog{max-width:560px;width:min(94vw,560px)}header h3{margin:0 0 .4rem}.finding{display:flex;align-items:center;gap:.5rem;flex-wrap:wrap;margin-bottom:.35rem}.loc{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.76rem;color:var(--text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0;flex:1}.desc{font-size:.78rem;color:var(--muted);margin:0 0 .7rem;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.input-row{display:flex;align-items:stretch;gap:.4rem}input{flex:1;min-width:0;padding:.5rem .65rem;border:1px solid var(--border);border-radius:var(--ui-radius, 6px);background:var(--bg, var(--surface));color:var(--text);font:inherit;font-size:.88rem;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;&:focus{outline:none;border-color:var(--accent);box-shadow:0 0 0 3px rgb(from var(--accent) r g b / .18)}&::placeholder{color:var(--muted);font-family:inherit}}.open{display:inline-flex;align-items:center;padding:0 .7rem;border:1px solid var(--accent);border-radius:var(--ui-radius, 6px);color:var(--accent);text-decoration:none;font-size:.8rem;font-weight:600;white-space:nowrap;&:hover{background:rgb(from var(--accent) r g b / .1)}}.hint{margin:.45rem 0 0;color:var(--muted);font-size:.74rem}
|
|
18
|
+
`,Yn=class extends C{static styles=[...C.styles,L(E),L(Jn)];static properties={initial:{attribute:!1},finding:{attribute:!1},canApplyToGroup:{attribute:!1},_value:{state:!0}};constructor(){super(),this.initial=``,this.finding=null,this.canApplyToGroup=!1,this._value=``}beforeOpen(){this._value=this.initial??``}focusInitial(){let e=this.renderRoot.querySelector(`input[type="url"]`);if(e){e.focus();try{e.select()}catch{}}}_onInput=e=>{this._value=e.target.value};get _trimmed(){return(this._value??``).trim()}_onSave=()=>{let e=(this.initial??``).trim();if(this._trimmed===e){this._finish(null);return}this._finish({value:this._trimmed,scope:`finding`})};_onApplyToGroup=()=>{this._trimmed&&this._finish({value:this._trimmed,scope:`group`})};_onClear=()=>this._finish({value:``,scope:`finding`});_onCancel=()=>this._finish(null);_onKeydown=e=>{e.key===`Enter`&&(e.preventDefault(),this._onSave())};render(){let e=this.finding??{},t=e.file?e.line?`${e.file}:${e.line}`:e.file:``,n=(this.initial??``).length>0,r=this._trimmed,i=Vt(r);return R`<dialog @close="${this._onClose}"><header><h3>${n?`Edit fix link`:`Add fix link`}</h3><div class="finding">${D(Kt(e,A.severityMode))} ${t?R`<span class="loc" data-tooltip-truncated data-tooltip="${t}">${t}</span>`:z}</div>${e.description?R`<div class="desc" data-tooltip-truncated data-tooltip="${e.description}">${e.description}</div>`:z}</header><div class="input-row"><input type="url" inputmode="url" autocomplete="off" spellcheck="false" placeholder="https://github.com/owner/repo/pull/123" .value="${this._value}" @input="${this._onInput}" @keydown="${this._onKeydown}"> ${i?R`<a class="open" href="${r}" target="_blank" rel="noopener noreferrer">Open ↗</a>`:z}</div><p class="hint">PR URL, issue link, commit, or any free-form reference. Enter to save, Esc to cancel.</p><footer class="nwd-actions">${n?R`<button type="button" class="danger" @click="${this._onClear}">Clear</button>`:z} <span class="nwd-spacer"></span> ${this.canApplyToGroup?R`<button type="button" class="quiet" ?disabled="${!r}" @click="${this._onApplyToGroup}">Apply to whole group</button> <span class="nwd-spacer"></span>`:z} <button type="button" @click="${this._onCancel}">Cancel</button> <button type="button" class="primary" @click="${this._onSave}">Save</button></footer></dialog>`}};customElements.define(`fix-link-dialog`,Yn);function Xn({initial:e=``,finding:t=null,canApplyToGroup:n=!1}={}){return w(`fix-link-dialog`,{initial:e,finding:t,canApplyToGroup:n})}var Zn=class extends C{static styles=[...C.styles,L(B)];static properties={reportName:{type:String},workspaceNames:{type:Array},existingNames:{type:Object},_choice:{state:!0},_newName:{state:!0}};constructor(){super(),this.reportName=``,this.workspaceNames=[],this.existingNames=new Set,this._choice=`replace`,this._newName=``}focusInitial(){this.renderRoot.querySelector(`button[data-role="cancel"]`)?.focus()}_onClose=()=>this._finish({action:`cancel`});_onCancel=()=>this._finish({action:`cancel`});_onChoiceChange=e=>{this._choice=e.target.value};_onNewNameInput=e=>{this._newName=e.target.value};_onNewNameKeydown=e=>{e.key===`Enter`&&(e.preventDefault(),this._onConfirm())};_onConfirm=()=>{if(this._choice===`rename`){let e=(this._newName??``).trim();if(this._renameError(e)!==null)return;this._finish({action:`rename`,newName:e});return}this._finish({action:`replace`})};_replaceHint(){let e=this.workspaceNames.length;if(e===0)return R`<span class="lwd-option-hint">The local file will be overwritten.</span>`;let t=e===1?R`workspace <strong>"${this.workspaceNames[0]}"</strong>`:R`<strong>${e}</strong> workspaces (${this.workspaceNames.join(`, `)})`;return R`<span class="lwd-option-hint">The local file will be overwritten. When sync is online, the new bytes are also uploaded to ${t} so the cloud copy matches.</span>`}_renameError(e){return e?e===this.reportName?`Pick a name different from the original.`:e.includes(`\0`)?`The new name cannot contain a NUL byte.`:e.includes(`/`)||e.includes(`\\`)?`The new name cannot contain "/" or "\\".`:this.existingNames.has(e)?`A file named "${e}" already exists \u2014 pick something else.`:null:`Enter a new name.`}render(){let e=this._choice===`rename`,t=(this._newName??``).trim(),n=e?this._renameError(t):null,r=n!==null,i=e?`Rename`:`Replace`,a=e?`primary`:`danger`,o=this.workspaceNames.length>0?R`<span class="lwd-option-hint">Saved as an unfiled report — the renamed copy isn't added to any workspace.</span>`:z;return R`<dialog @close="${this._onClose}"><header><h3>Import conflict</h3></header><p class="lwd-body"><strong>"${this.reportName}"</strong> already exists with different content.</p><fieldset class="lwd-choice"><legend>What should happen?</legend><label class="lwd-option"><input type="radio" name="icd-choice" value="replace" ?checked="${this._choice===`replace`}" @change="${this._onChoiceChange}"> <span class="lwd-option-text"><span class="lwd-option-title">Replace</span> ${this._replaceHint()}</span></label> <label class="lwd-option"><input type="radio" name="icd-choice" value="rename" ?checked="${this._choice===`rename`}" @change="${this._onChoiceChange}"> <span class="lwd-option-text"><span class="lwd-option-title">Rename to</span> <input type="text" class="nwd-input lwd-rename-input" maxlength="200" .value="${this._newName}" ?disabled="${!e}" @input="${this._onNewNameInput}" @keydown="${this._onNewNameKeydown}" @focus="${()=>{this._choice=`rename`}}"> ${o} ${n===null?z:R`<p class="lwd-rename-error">${n}</p>`}</span></label></fieldset><footer class="nwd-actions"><span class="nwd-spacer"></span> <button type="button" data-role="cancel" @click="${this._onCancel}">Cancel</button> <button type="button" class="${a}" ?disabled="${r}" @click="${this._onConfirm}">${i}</button></footer></dialog>`}};customElements.define(`import-conflict-dialog`,Zn);function Qn({name:e,workspaceNames:t,existingNames:n}={}){let r=n instanceof Set?n:new Set(n??[]);return new Promise(n=>{let i=document.createElement(`import-conflict-dialog`);Object.assign(i,{reportName:e??``,workspaceNames:Array.isArray(t)?t:[],existingNames:r,_newName:$n(e??``,r)}),i.addEventListener(`resolve`,e=>{i.remove(),n(e.detail)}),i.addEventListener(`modal-conflict`,()=>{i.remove(),n({action:`cancel`})}),document.body.append(i)})}function $n(e,t){if(typeof e!=`string`||e.length===0)return``;let n=e.lastIndexOf(`.`),r=n>0?e.slice(0,n):e,i=n>0?e.slice(n):``,a=r.replace(/-\d+$/u,``);for(let e=2;e<1e3;e++){let n=`${a}-${e}${i}`;if(!t.has(n))return n}return``}var er=class extends C{static styles=[...C.styles,L(B)];static properties={workspaceName:{type:String},reportCount:{type:Number},bundleCount:{type:Number},orphanedTriage:{type:Number},sharedTriage:{type:Number},_mode:{state:!0},_triage:{state:!0},_settled:{state:!0}};constructor(){super(),this.workspaceName=``,this.reportCount=0,this.bundleCount=0,this.orphanedTriage=0,this.sharedTriage=0,this._mode=`detach`,this._triage=`keep`,this._settled=!1}focusInitial(){this.renderRoot.querySelector(`button[data-role="cancel"]`)?.focus()}_finish(e){if(this._settled)return;let t=this.reportCount>0?this._mode:`detach`,n=t===`delete`&&this.orphanedTriage>0?this._triage:`keep`;super._finish({confirmed:!!e,mode:t,triage:n})}_onClose=()=>this._finish(!1);_onCancel=()=>this._finish(!1);_onConfirm=()=>this._finish(!0);_onModeChange=e=>{this._mode=e.target.value};_onTriageChange=e=>{this._triage=e.target.value};_triageSection(){if(this._mode!==`delete`||this.reportCount===0)return``;let e=this.orphanedTriage,t=this.sharedTriage;return e===0&&t===0?R`<p class="lwd-note">No local triage is attached to ${this.reportCount===1?`this report`:`these reports`}.</p>`:e===0?R`<p class="lwd-note">Local triage on ${this.reportCount===1?`this report`:`these reports`} is also attached to ${t===1?`a report`:`reports`} you're keeping. Nothing will be orphaned.</p>`:R`<fieldset class="lwd-choice"><legend><strong>${e}</strong> ${e===1?`triage entry won't`:`triage entries won't`} be reachable from any remaining report. What should happen ${e===1?`to it`:`to them`}?</legend><label class="lwd-option"><input type="radio" name="lwd-triage" value="keep" ?checked="${this._triage===`keep`}" @change="${this._onTriageChange}"> <span class="lwd-option-text"><span class="lwd-option-title">Keep</span> <span class="lwd-option-hint">Reapplied automatically if a matching report is imported later.</span></span></label> <label class="lwd-option"><input type="radio" name="lwd-triage" value="wipe" ?checked="${this._triage===`wipe`}" @change="${this._onTriageChange}"> <span class="lwd-option-text"><span class="lwd-option-title">Wipe</span> <span class="lwd-option-hint">Drop the orphaned triage from this browser.</span></span></label></fieldset>`}render(){let e=this.reportCount,t=this.bundleCount,n=e>0,r=e===1?`report`:`reports`,i=e===1?`it`:`them`,a=e===1?`stays`:`stay`,o=e===1?`is removed`:`are removed`,s=t>0?R`<p class="lwd-note">${t===1?`One bundle is`:`${t} bundles are`} also attached. ${t===1?`It`:`They`} will become unfiled — the bundle bytes stay on this device either way (delete bundles manually from the Bundles list if you want them gone).</p>`:``,c=n?R`<fieldset class="lwd-choice"><legend>What should happen to the <strong>${e}</strong> attached ${r}?</legend><label class="lwd-option"><input type="radio" name="lwd-mode" value="detach" ?checked="${this._mode===`detach`}" @change="${this._onModeChange}"> <span class="lwd-option-text"><span class="lwd-option-title">Move ${i} out of the workspace</span> <span class="lwd-option-hint">The ${r} ${a} in this browser as unattached ${r}.</span></span></label> <label class="lwd-option"><input type="radio" name="lwd-mode" value="delete" ?checked="${this._mode===`delete`}" @change="${this._onModeChange}"> <span class="lwd-option-text"><span class="lwd-option-title">Delete ${i} along with the workspace</span> <span class="lwd-option-hint">The ${r} ${o} from this browser.</span></span></label></fieldset>`:t>0?R`<p class="lwd-empty">No reports are attached to this workspace (only bundles, see below).</p>`:R`<p class="lwd-empty">No reports are attached to this workspace.</p>`;return R`<dialog @close="${this._onClose}"><header><h3>Leave workspace</h3></header><p class="lwd-body">Leave workspace <strong>"${this.workspaceName}"</strong> on this device?</p>${c} ${s} ${this._triageSection()}<ul class="lwd-list"><li>Workspace synchronization data stays on the sync server — peers (and your other devices) keep their copy.</li></ul><footer class="nwd-actions"><span class="nwd-spacer"></span> <button type="button" data-role="cancel" @click="${this._onCancel}">Cancel</button> <button type="button" class="danger" @click="${this._onConfirm}">Leave</button></footer></dialog>`}};customElements.define(`leave-workspace-dialog`,er);function tr({name:e,reportCount:t,bundleCount:n,triageImpact:r}={}){return w(`leave-workspace-dialog`,{workspaceName:e??``,reportCount:t??0,bundleCount:n??0,orphanedTriage:r?.orphanedCount??0,sharedTriage:r?.sharedCount??0})}var nr=`dialog{width:min(42rem,90vw)}.content-import-groups{max-height:55vh;overflow:auto}fieldset{margin:.7rem 0;padding:.5rem .75rem;border:1px solid var(--border);border-radius:var(--ui-radius, 6px)}legend{padding:0 .35rem;font-size:.85rem;font-weight:600}.content-import-item{display:flex;gap:.65rem;align-items:flex-start;padding:.65rem 0}.content-import-item+.content-import-item{border-top:1px solid var(--border)}.content-import-item input{margin:.15rem 0 0;accent-color:var(--accent)}.content-import-item.unavailable{opacity:.55;cursor:default}.content-import-label{display:grid;gap:.25rem;min-width:0;overflow-wrap:anywhere;font-size:.82rem}.content-import-status{display:flex;flex-wrap:wrap;gap:.4rem;color:var(--muted);font-size:.74rem}.content-import-present{border:1px solid var(--border);border-radius:3px;padding:0 .3rem}input:focus-visible{outline:2px solid var(--accent);outline-offset:2px}button:disabled{opacity:.5}
|
|
19
|
+
`,rr=class extends V{static styles=[...V.styles,L(nr)];static properties={plan:{attribute:!1},selected:{state:!0}};constructor(){super(),this.plan={kind:`report`,items:[],groups:[]},this.selected=new Set}beforeOpen(){this.selected=new Set(this.plan.items.filter(e=>e.synced&&!e.present&&!e.error).map(e=>e.value))}toggle(e,t){if(e.present||e.error)return;let n=new Set(this.selected);t?n.add(e.value):n.delete(e.value),this.selected=n}confirmationResult(e){let t=this.plan.items.filter(e=>this.selected.has(e.value)&&!e.present&&!e.error).map(e=>e.value),n=e&&t.length>0&&!this.signal?.aborted;return{confirmed:n,selected:n?t:[]}}render(){let e=this.plan.kind===`bundle`?`bundles`:`reports`;return R`<dialog aria-labelledby="content-import-title" @close="${this._onClose}"><header><h3 id="content-import-title">Import ${e}</h3></header><p class="lwd-body">Choose files to upload. Cloud-synced files are selected by default.</p><p class="lwd-note">Workspaces are shown for organization only. Teams and triage are not imported. Repository locations follow each file’s embedded metadata; files without it stay unattached.</p><div class="content-import-groups">${this.plan.groups.map(e=>R`<fieldset><legend>${e.name}</legend>${e.items.map(({item:e,synced:t,cached:n})=>R`<label class="${`content-import-item${e.present||e.error?` unavailable`:``}`}"><input type="checkbox" .checked="${this.selected.has(e.value)}" ?disabled="${e.present||!!e.error}" @change="${t=>this.toggle(e,t.target.checked)}"> <span class="content-import-label"><strong>${e.name}</strong> <span class="content-import-status">${t?`Cloud synced`:`Not synced`}${n?` · last known status`:``} ${e.present?R`<span class="content-import-present">Already present</span>`:z}</span> ${e.error?R`<span>${e.error}</span>`:z}</span></label>`)}</fieldset>`)} ${this.plan.items.length===0?R`<p class="lwd-empty">No local ${e} available.</p>`:z}</div><footer class="nwd-actions"><span class="lwd-empty" role="status">${this.selected.size} selected</span><span class="nwd-spacer"></span> <button type="button" data-role="cancel" @click="${this._onCancel}">Cancel</button> <button type="button" class="primary" ?disabled="${this.selected.size===0}" @click="${this._onConfirm}">Import ${e}</button></footer></dialog>`}};customElements.define(`local-content-import-dialog`,rr);function ir(e){return w(`local-content-import-dialog`,e)}var ar=class extends V{static properties={matched:{type:Number},available:{type:Number}};constructor(){super(),this.matched=0,this.available=0}confirmationResult(e){return{confirmed:e&&this.matched>0&&!this.signal?.aborted}}render(){return R`<dialog aria-labelledby="import-title" @close="${this._onClose}"><header><h3 id="import-title">Import local triage</h3></header><p class="lwd-body"><strong>${this.matched}</strong> of <strong>${this.available}</strong> local triage ${this.available===1?`entry`:`entries`} can be imported.</p><p class="lwd-note">${this.matched>0?`Import saved triage and comments for these findings? Conflicting values will prompt for resolution.`:this.available>0?`None match findings in managed reports.`:`There is no saved local triage to import.`}</p>${this.available>this.matched?R`<p class="lwd-empty">Unmatched entries stay in this browser.</p>`:z}<footer class="nwd-actions"><span class="nwd-spacer"></span> <button type="button" data-role="cancel" @click="${this._onCancel}">${this.matched>0?`Cancel`:`Close`}</button> ${this.matched>0?R`<button type="button" class="primary" @click="${this._onConfirm}">Import triage</button>`:z}</footer></dialog>`}};customElements.define(`local-triage-import-dialog`,ar);function or(e){return w(`local-triage-import-dialog`,e)}var sr=`dialog{width:min(94vw,800px);max-width:800px;max-height:calc(100dvh - 4rem);margin-block:2rem;padding:0}dialog[open]{display:flex;flex-direction:column}.discussion-header{display:flex;align-items:start;gap:1rem;padding:1rem 1.25rem;border-bottom:1px solid var(--border)}.discussion-heading{flex:1;min-width:0}header h3{display:flex;align-items:center;gap:.5rem;margin:0;line-height:1.5}.comment-count{padding:.05rem .4rem;border-radius:1rem;background:rgb(from var(--muted) r g b / .12);color:var(--muted);font-size:.72rem;font-weight:400}.discussion-finding{margin-top:.4rem;font-size:.85rem;overflow-wrap:anywhere}.loc{margin-top:.25rem;color:var(--muted)}.header-actions{display:flex;flex:none;align-items:center;gap:.7rem}.header-actions button{display:inline-flex;flex:none;align-items:center;justify-content:center;width:1.5rem;height:1.5rem;padding:0;border:0;background:transparent;color:var(--muted);line-height:0;& svg{display:block;width:1rem;height:1rem}&:hover:not(:disabled){color:var(--text)}&:disabled{opacity:.5}}.comment-action{display:inline-flex;align-items:center;justify-content:center;width:1.5rem;height:1.5rem;padding:.2rem;border:1px solid transparent;border-radius:var(--ui-radius, 4px);background:transparent;color:var(--muted);cursor:default;& svg{width:.9rem;height:.9rem}&:hover:not(:disabled),&[aria-pressed=true]{border-color:var(--border);background:rgb(from var(--muted) r g b / .08);color:var(--text)}&.danger:hover:not(:disabled),&[aria-expanded=true]{color:var(--critical, #f87171)}&:disabled{opacity:.5}}button:focus-visible{outline:2px solid var(--accent);outline-offset:3px;border-radius:var(--ui-radius, 3px)}.discussion-log{min-height:0;max-height:50dvh;overflow-y:auto;overscroll-behavior:contain;padding:.5rem;scrollbar-gutter:stable}.comments{display:flex;flex-direction:column;gap:.15rem;list-style:none}.comment{padding:.6rem .75rem;border-radius:var(--ui-radius, 6px);font-size:.84rem}.comment:hover{background:rgb(from var(--muted) r g b / .035)}.comment.editing{background:rgb(from var(--accent) r g b / .08);box-shadow:inset 0 0 0 1px rgb(from var(--accent) r g b / .2)}.delete-confirm{display:flex;align-items:center;justify-content:end;flex-wrap:wrap;gap:.5rem;margin-top:.6rem;color:var(--muted);font-size:.75rem;& button{padding:.25rem .55rem;border:1px solid var(--border);border-radius:var(--ui-radius, 4px);background:transparent;color:var(--text);font:inherit;&:hover:not(:disabled){background:rgb(from var(--muted) r g b / .08)}&.danger{color:var(--critical, #f87171)}&:disabled{opacity:.5}}}.empty-discussion{padding:2.5rem 0;color:var(--muted);text-align:center;font-size:.85rem}.composer{padding:1rem 1.25rem;border-top:1px solid var(--border)}.composer-label{display:flex;align-items:center;gap:.45rem;margin-bottom:.6rem;font-size:.78rem}.composer-label strong{font-weight:600}textarea{min-height:4.5rem;max-height:25dvh;font-size:.84rem}.nwd-actions{border:0;padding-top:0;margin-top:.65rem}.shortcut{color:var(--muted);font-size:.7rem}.error{padding:.65rem 1.25rem;color:var(--danger, #c44);font-size:.8rem}.read-only{padding:1rem 1.25rem;border-top:1px solid var(--border);color:var(--muted);font-size:.8rem}@media(width<=480px){.discussion-header,.composer{padding:.8rem}.discussion-log{padding-inline:.3rem}.comment{padding-inline:.5rem}.shortcut{max-width:8rem}}
|
|
20
|
+
`,cr=class extends C{static styles=[...C.styles,L(an),L(i),L(sr)];static properties={finding:{attribute:!1},changed:{attribute:!1},_comments:{state:!0},_value:{state:!0},_editing:{state:!0},_busy:{state:!0},_error:{state:!0},_deleting:{state:!0}};constructor(){super(),this.finding=null,this.changed=null,this._comments=[],this._value=``,this._editing=null,this._busy=!1,this._error=``,this._deleting=null}connectedCallback(){super.connectedCallback(),document.addEventListener(`managed-comments-reset`,this._close)}disconnectedCallback(){this._unsubscribeComments?.(),this._unsubscribeComments=null,document.removeEventListener(`managed-comments-reset`,this._close),super.disconnectedCallback()}beforeOpen(){this._current=_e(this.finding?._managedReportId),this._comments=g(this.finding),this._unsubscribeComments?.(),this._unsubscribeComments=ue(this.finding.id,this._commentsChanged),this._refresh({scroll:!0})}_commentsChanged=()=>{if(!this.isConnected)return;if(!this._current()){this._close();return}let e=this.renderRoot.querySelector(`.discussion-log`),t=e&&e.scrollHeight-e.scrollTop-e.clientHeight<32;this._comments=g(this.finding),t&&this._scrollToLatest()};_close=()=>this._finish(null);_scrollToLatest=async()=>{await this.updateComplete;let e=this.renderRoot.querySelector(`.discussion-log`);e&&(e.scrollTop=e.scrollHeight)};_refresh=async({scroll:e=!1}={})=>{if(!this._current()){this._close();return}let t=this.renderRoot.querySelector(`.discussion-log`),n=t&&t.scrollHeight-t.scrollTop-t.clientHeight<32;this._busy=!0,this._error=``;let r=await be(this.finding._managedReportId);if(this.isConnected){if(!this._current()){this._close();return}if(this._busy=!1,!r){this._error=`Could not load comments. Please retry.`;return}this._comments=g(this.finding),this.changed?.(),(e||n)&&this._scrollToLatest()}};_edit(e){this._deleting=null,this._editing=e,this._value=e.body,this._error=``,this.updateComplete.then(()=>this.renderRoot.querySelector(`textarea`)?.focus())}_cancelEdit=()=>{this._editing=null,this._value=``,this._error=``};_delete=async e=>{if(this._busy||!this._current())return;this._busy=!0,this._error=``;let t=await we(this.finding,e);if(!this.isConnected){t===204&&this._current()&&this.changed?.();return}if(!this._current()){this._close();return}if(this._busy=!1,this._deleting=null,t===409){await this._refresh(),this.isConnected&&(this._error=`This comment changed elsewhere. Review the latest version before deleting it.`);return}if(t!==204){this._error=t===403||t===404?`This comment is unavailable or you no longer have permission to delete it.`:`Could not delete the comment. Please retry.`;return}this._comments=g(this.finding),this._editing?.id===e.id&&this._cancelEdit(),this.changed?.()};_save=async()=>{let e=this._value.trim();if(this._busy||!e||e.length>1e4||!this._current())return;this._busy=!0,this._error=``;let t=await Se(this.finding,e,this._editing);if(!this.isConnected)return;if(!this._current()){this._close();return}if(this._busy=!1,t.status===409){if(await this._refresh(),!this.isConnected)return;this._editing=this._comments.find(e=>e.id===this._editing?.id)??this._editing,this._error=`This comment changed elsewhere. Your draft is kept. Review the latest version above before saving again.`;return}if(!t.comment){this._error=t.status===403||t.status===404?`You no longer have permission to save this comment.`:`Could not save the comment. Your draft is kept; please retry.`;return}let n=this._editing!=null;this._comments=g(this.finding),this._cancelEdit(),this.changed?.(),n||await this._scrollToLatest(),await this.updateComplete,this.renderRoot.querySelector(`textarea`)?.focus()};_keydown=e=>{e.key===`Enter`&&(e.ctrlKey||e.metaKey)&&(e.preventDefault(),this._save())};_commentActions(e,t){let n=t&&e.authorId===A.managedSession?.id,r=ge(e);return!n&&!r?z:R`${n?R`<button type="button" class="comment-action" aria-label="Edit comment" aria-pressed="${this._editing?.id===e.id}" ?disabled="${this._busy}" @click="${()=>this._edit(e)}">${I(Zt)}</button>`:z} ${r?R`<button type="button" class="comment-action danger" aria-label="Delete comment" aria-expanded="${this._deleting===e.id}" ?disabled="${this._busy}" @click="${()=>{this._deleting=this._deleting===e.id?null:e.id}}">${I(Qt)}</button>`:z}`}_deleteConfirmation(e){return this._deleting!==e.id||!ge(e)?z:R`<div class="delete-confirm" role="group" aria-label="Confirm comment deletion"><span>Delete this comment?</span> <button type="button" ?disabled="${this._busy}" @click="${()=>{this._deleting=null}}">Cancel</button> <button type="button" class="danger" ?disabled="${this._busy}" @click="${()=>this._delete(e)}">Delete</button></div>`}render(){let e=me();return R`<dialog aria-labelledby="comments-title" @close="${this._onClose}"><header class="discussion-header"><div class="discussion-heading"><h3 id="comments-title">Discussion <span class="comment-count">${this._comments.length}</span></h3>${this.finding?.title?R`<p class="discussion-finding">${this.finding.title}</p>`:z}<p class="loc">${this.finding?.file??``}</p></div><div class="header-actions"><button type="button" class="refresh" aria-label="Refresh discussion" ?disabled="${this._busy}" @click="${()=>this._refresh()}"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 12a9 9 0 1 1-9-9c2.52 0 4.93 1 6.74 2.74L21 8M21 3v5h-5"/></svg></button> <button type="button" class="close" aria-label="Close discussion" @click="${this._close}"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="m6 6 12 12M6 18 18 6"/></svg></button></div></header><section class="discussion-log" role="log" aria-label="Discussion" aria-relevant="additions text" aria-busy="${this._busy}"><ol class="comments">${this._comments.map(t=>R`<li class="${`comment${this._editing?.id===t.id?` editing`:``}`}">${De(t,ke(t.body),this._commentActions(t,e))} ${this._deleteConfirmation(t)}</li>`)}</ol>${this._comments.length===0?R`<p class="empty-discussion">${this._busy?`Loading discussion…`:e?`No comments yet. Start the discussion below.`:`No comments yet.`}</p>`:z}</section>${this._error?R`<p class="error" role="alert">${this._error}</p>`:z} ${e?R`<form class="composer" @submit="${e=>{e.preventDefault(),this._save()}}"><label class="composer-label" for="comment-body">${Ee(A.managedSession.id,A.managedSession.login)} ${this._editing?`Edit your comment`:R`Reply as <strong>${A.managedSession.login}</strong>`}</label> <textarea id="comment-body" rows="3" maxlength="${1e4}" .value="${this._value}" placeholder="Write a comment…" aria-describedby="comment-shortcut" ?disabled="${this._busy}" @input="${e=>{this._value=e.target.value}}" @keydown="${this._keydown}"></textarea><footer class="nwd-actions"><span id="comment-shortcut" class="shortcut">Ctrl / ⌘ + Enter to send</span><span class="nwd-spacer"></span> ${this._editing?R`<button type="button" class="quiet" ?disabled="${this._busy}" @click="${this._cancelEdit}">Cancel edit</button>`:z} <button type="submit" class="primary" ?disabled="${this._busy||!this._value.trim()}">${this._editing?`Save changes`:`Comment`}</button></footer></form>`:R`<p class="read-only">You have read-only access to this discussion.</p>`}</dialog>`}};customElements.define(`managed-comments-dialog`,cr);function lr(e,t){return w(`managed-comments-dialog`,{finding:e,changed:t})}var ur=new Set([`github-unavailable`,`unavailable`,`shutting-down`,`github-create-failed`]);async function dr(e,t,n,r=null){let i=`/api/teams/${encodeURIComponent(e)}/issues`,a,o;try{a=await d(r?i:`${i}?${new URLSearchParams(t)}`,{method:r?`POST`:`GET`,credentials:`same-origin`,headers:{accept:`application/json`,...r?{"content-type":`application/json`,"x-csrf-token":n}:{}},...r?{body:JSON.stringify({...t,...r})}:{}}),o=await a.json()}catch{throw Error(r?`github-create-uncertain`:`github-unavailable`)}if(!a.ok){let e=typeof o?.error==`string`&&o.error?o.error:null;throw Error(r&&a.status>=500&&!ur.has(e)?`github-create-uncertain`:e??(r?`github-create-uncertain`:`github-unavailable`))}return o}var U=[[`impact`,`Impact`],[`evidence`,`Evidence`],[`reproduction`,`Reproduction`],[`recommendation`,`Recommendation`],[`confidenceReason`,`Confidence reason`],[`revalidateVerdict`,`Revalidation verdict`],[`revalidateRecommendation`,`Revalidation recommendation`]],fr=e=>e.toLowerCase().replaceAll(/[^\p{L}\p{N}]/gu,``),pr=new Map([...U.map(([e,t])=>[fr(t),e]),[`reproductionsteps`,`reproduction`],[`recommendedfix`,`recommendation`]]);function mr(e,{sourceUrl:t,evidence:n=``,showRevalidation:r=!0}={}){let i=[],a=new Map,o=(e,t,n)=>{if(!n?.trim())return;let r=a.get(e)??{id:e,label:t,parts:[]};r.parts.includes(n.trim())||r.parts.push(n.trim()),a.set(e,r)},s=Xt(e);e.file&&i.push(`File: ${t?`[${s}](${t})`:s}`);let c=null;for(let t of Yt(Jt(e.description,e))){if(t.label!==null){let e=fr(t.label);c=[`description`,`details`,`summary`].includes(e)?null:{id:pr.get(e)??e,label:t.label}}c?o(c.id,c.label,t.body):i.push(t.label?`**${t.label}:** ${t.body}`:t.body)}e.confidence!=null&&i.push(`Confidence: ${e.confidence}/10`);for(let[t,i]of U){if(t.startsWith(`revalidate`)&&!r){a.delete(t);continue}o(t,i,t===`evidence`?n.replace(/^\*\*Evidence:\*\*\n/u,``):Jt(e[t],e))}let l=[...U.map(([e])=>a.get(e)).filter(Boolean),...[...a.values()].filter(e=>!U.some(([t])=>t===e.id))],u={body:i.join(`
|
|
21
21
|
|
|
22
22
|
`),sections:l.map(({id:e,label:t,parts:n})=>({id:e,label:t,text:`**${t}:**
|
|
23
23
|
${n.join(`
|
|
24
24
|
|
|
25
|
-
`)}`,selected:!1,start:0,end:0}))};return a.has(`impact`)&&(u=
|
|
25
|
+
`)}`,selected:!1,start:0,end:0}))};return a.has(`impact`)&&(u=gr(u,`impact`,!0)),u}function hr(e,t){let n=e.body;if(t===n)return e;let r=n.length,i=t.length,a=0;for(;a<r&&a<i&&n[a]===t[a];)a++;for(;r>a&&i>a&&n[r-1]===t[i-1];)r--,i--;let o=t.length-n.length;return{body:t,sections:e.sections.map(e=>e.selected?a>=e.start&&r<=e.end?{...e,end:e.end+o}:r<=e.start?{...e,start:e.start+o,end:e.end+o}:a>=e.end?e:{...e,text:n.slice(e.start,e.end),selected:!1,start:0,end:0}:e)}}function gr(e,t,n){let r=e.sections.find(e=>e.id===t);if(!r||r.selected===n)return e;if(n){let t=!e.body||e.body.endsWith(`
|
|
26
26
|
|
|
27
27
|
`)?``:e.body.endsWith(`
|
|
28
28
|
`)?`
|
|
29
29
|
`:`
|
|
30
30
|
|
|
31
|
-
`,i=e.body+t+r.text,a=e.body.length+t.length;return{body:i,sections:e.sections.map(e=>e===r?{...e,selected:n,separator:t,start:a,end:i.length}:e)}}let i=e.body.slice(r.start,r.end),a=r.separator??``,o=r.start-a.length,s=e.body.slice(o,r.start)===a&&!e.sections.some(e=>e!==r&&e.selected&&e.start<r.start&&e.end>o)?o:r.start,c=e.body.slice(0,s)+e.body.slice(r.end),l=s-r.end;return{body:c,sections:e.sections.map(e=>e===r?{...e,text:i,selected:n,start:0,end:0}:e.selected&&e.start>=r.end?{...e,start:e.start+l,end:e.end+l}:e)}}var
|
|
32
|
-
`,
|
|
33
|
-
`,
|
|
34
|
-
`,
|
|
35
|
-
`,
|
|
36
|
-
`,
|
|
37
|
-
${e.detail}`:t}_heldLocally(e){return e.identifier?e.kind===`bundle`?this.localBundles.includes(e.identifier):this.localFileNames.includes(e.identifier):!1}_choice(e){if(e.status!==`differs`)return R;let t=this._running||this._bulk||this._resolving.has(e.resourceTag);return L`<span class="rec-choice"><button type="button" data-role="keep-cloud" data-tooltip="Replace your copy with the cloud copy" @click="${()=>this._onResolve(e,`cloud`)}" ?disabled="${t}">Use cloud copy</button> <button type="button" data-role="keep-local" data-tooltip="Upload your copy, replacing the cloud copy" @click="${()=>this._onResolve(e,`local`)}" ?disabled="${t}">Upload mine</button></span>`}_rowsSection(){return this._rows.length===0?this._running?L`<p class="lwd-body">Fetching remote listing…</p>`:R:L`<ul class="rec-list">${this._rows.map(e=>L`<li class="rec-row"><span class="rec-label">${e.label}${e.kind===`bundle`?L`<span class="lwd-kind-tag">bundle</span>`:R}</span> ${this._choice(e)} <span class="${`rec-status rec-status-${e.status}`}" data-tooltip="${this._statusHint(e)}">${Nr[e.status]?.label??e.status}</span></li>`)}</ul>`}_summarySection(){if(!this._ran||this._rows.length===0)return R;let e=this._counts(),t=[];return e.good&&t.push(`${e.good} available`),e.updated&&t.push(`${e.updated} local ${Fr(e.updated)} updated from the cloud`),e.uploaded&&t.push(`${e.uploaded} cloud ${Fr(e.uploaded)} updated from yours`),e.differs&&t.push(`${e.differs} ${e.differs===1?`differs`:`differ`} \u2014 choose which copy to keep`),e.reuploaded&&t.push(`${e.reuploaded} re-uploaded`),e.failed&&t.push(`${e.failed} failed`),e[`check-failed`]&&t.push(`${e[`check-failed`]} check failed`),e.missing&&t.push(`${e.missing} missing`),t.length>0?L`<p class="rec-summary" role="status" aria-live="polite">${t.join(` · `)}</p>`:R}render(){let e=this.cloudCount===1?``:`s`,t=this.cloudCount>0?L`the <strong>${this.cloudCount}</strong> remote`:`remote`,n=this._ran?R:L`<p class="lwd-body">Re-check ${t} object${e} for this workspace. Each is re-fetched from the relay and compared with your copy. When one copy is provably newer, the other is updated to match; when it isn't clear, you choose which to keep. Any whose bytes are missing are re-uploaded from a matching local copy.</p>`,r=this._ran&&this._rows.length===0&&!this._error?L`<p class="lwd-empty">No remote objects to check.</p>`:R,i=this._ran?this._downloadableItems().length:0,a=this._differingRows().length>1||this._bulk;return L`<dialog @close="${this._onClose}"><button type="button" class="detail-action rec-close" data-role="close" aria-label="Close" @click="${this._onCancel}">×</button><header><h3>Re-check cloud storage</h3></header>${n} ${this._rowsSection()} ${r} ${this._summarySection()} ${this._error?L`<p class="rec-error" role="alert">${this._error}</p>`:R}<footer class="nwd-actions"><span class="nwd-spacer"></span> ${a?L`<button type="button" data-role="use-cloud-all" data-tooltip="Replace your copy of every report that differs with its cloud copy" @click="${this._onUseCloudForAll}" ?disabled="${this._running||this._bulk}">${this._bulk?`Using cloud copies…`:`Use cloud for all ${this._differingRows().length} conflicts`}</button>`:R} ${!a&&i>0?L`<button type="button" data-role="download" @click="${this._onDownload}" ?disabled="${this._running}">Download ${i} not stored locally</button>`:R} ${a?R:L`<button type="button" data-role="cancel" @click="${this._onCancel}" ?disabled="${this._running}">${this._ran?`Close`:`Cancel`}</button>`} <button type="button" data-role="recheck" @click="${this._onRecheck}" ?disabled="${this._running||this._bulk||this._resolving.size>0}">${this._running?`Re-checking…`:this._ran?`Re-check again`:`Re-check`}</button></footer></dialog>`}};customElements.define(`objstore-recovery-dialog`,Ir);function Lr({workspaceId:e,cloudCount:t,localFileNames:n,localBundles:r,autoRun:i}={}){return x(`objstore-recovery-dialog`,{workspaceId:e??``,cloudCount:typeof t==`number`?t:0,localFileNames:Array.isArray(n)?[...n]:[],localBundles:Array.isArray(r)?[...r]:[],autoRun:i===!0})}var Rr=class extends b{static styles=[...b.styles,I(E)];static properties={_userName:{state:!0},_busy:{state:!0},_error:{state:!0},_phase:{state:!0},_success:{state:!0},_orphan:{state:!0},_orphanAck:{state:!0},_canCancel:{state:!0}};constructor(){super(),this._userName=``,this._busy=!1,this._error=``,this._phase=``,this._success=!1,this._orphan=!1,this._orphanAck=!1,this._canCancel=!0,this._abortController=null}updated(e){if(e.has(`_success`)&&this._success){let e=this.renderRoot.querySelector(`footer button.primary`);e&&e.focus()}}beforeOpen(){typeof location<`u`&&(this._userName=location.hostname||`DeepView`),this._orphan=Ke(),this._vaultStateUnsub=Je(()=>{if(this._settled)return;let e=Ke();this._orphan&&!e&&Re()?(this._error=`Encryption was just enabled in another tab. Close this dialog and reload to unlock.`,this._busy=!1,this._canCancel=!0):this._orphan!==e&&(this._orphan=e)})}async focusInitial(){await this.updateComplete;let e=this._orphan?`input[data-role="orphan-ack"]`:`input[data-role="name"]`;this.renderRoot.querySelector(e)?.focus()}disconnectedCallback(){if(super.disconnectedCallback(),this._vaultStateUnsub){try{this._vaultStateUnsub()}catch{}this._vaultStateUnsub=null}}_finish(e){if(!this._settled){if(this._abortController)try{this._abortController.abort()}catch{}super._finish(!!e)}}_onClose=()=>this._finish(this._success);_onCancel=()=>{this._busy&&!this._canCancel||this._finish(!1)};_onDialogCancel=e=>{if(this._busy&&!this._canCancel){e.preventDefault();return}this._finish(!1)};_onNameInput=e=>{this._userName=e.target.value};_onOrphanAckInput=e=>{this._orphanAck=e.target.checked};_onWipeOrphan=async()=>{if(!this._busy&&this._orphanAck){if(Re()){this._error=`Encryption was just enabled in another tab. Reload this page to unlock.`;return}this._busy=!0,this._canCancel=!1,this._error=``,this._phase=`Clearing previous vault state…`;try{await $e({refuseIfEnabled:!0}),location.reload()}catch(e){this._error=`Could not clear previous vault state: ${e?.message??e}`,this._phase=``}finally{this._busy=!1,this._canCancel=!0}}};_onEnable=async()=>{if(!this._busy){if(!We()){this._error=`Passkeys are not supported in this browser.`;return}this._busy=!0,this._canCancel=!0,this._error=``,this._phase=`Registering passkey…`,this._abortController=new AbortController;try{await Ze({userName:this._userName.trim()||`DeepView user`,rpName:`DeepView`,signal:this._abortController.signal,migrate:async({seal:e})=>{this._canCancel=!1,this._phase=`Encrypting workspaces and metadata…`,await rt({seal:e}),this._phase=`Encrypting triage data…`,await De({seal:e}),this._phase=`Encrypting report files…`,await h({seal:e}),this._phase=`Encrypting bundles…`,await ne({seal:e})}})&&(this._success=!0,this._phase=``)}catch(e){this._error=e?.name===`AbortError`?``:e?.message??String(e),this._phase=``}finally{this._busy=!1,this._canCancel=!0,this._abortController=null}}};_body(){if(this._orphan)return L`<p class="wsl-error">DeepView found an abandoned passkey setup on this device. The original passkey can no longer be accessed, so any local data still encrypted under it is <strong>unreadable</strong> and cannot be recovered.</p><p class="nwd-note ui-hint">To enable encryption, DeepView will <strong>permanently delete ALL local DeepView data on this device</strong> — including any plaintext workspaces or reports you may have added after the previous passkey was lost — and then register a fresh passkey. This is irreversible. There is no backup.</p><label class="wsl-ack"><input type="checkbox" data-role="orphan-ack" .checked="${this._orphanAck}" ?disabled="${this._busy}" @change="${this._onOrphanAckInput}"> <span>I understand all local DeepView data on this device will be deleted.</span></label> ${this._phase?L`<p class="nwd-note">${this._phase}</p>`:R} ${this._error?L`<p class="wsl-error">${this._error}</p>`:R}<footer class="nwd-actions"><span class="nwd-spacer"></span> <button type="button" ?disabled="${this._busy&&!this._canCancel}" @click="${this._onCancel}">Cancel</button> <button type="button" class="primary" ?disabled="${this._busy||!this._orphanAck}" @click="${this._onWipeOrphan}">${this._busy?`Working…`:`Wipe and continue`}</button></footer>`;if(this._success)return L`<p class="nwd-note ui-hint"><strong>Encryption is on.</strong> Your triage data and report files are now sealed under your passkey. You'll be asked to unlock with the passkey on every fresh load.</p><footer class="nwd-actions"><span class="nwd-spacer"></span> <button type="button" class="primary" @click="${()=>this._finish(!0)}">Close</button></footer>`;let e=We();return L`<p class="nwd-note ui-hint">Passkey encryption protects your local triage and report data with a hardware-backed key (TouchID, Windows Hello, security key). Without the passkey, the data on disk is unreadable — even to scripts running on this origin.</p><p class="wsl-error"><strong>If you lose access to this passkey, your data CANNOT be recovered.</strong> DeepView has no backup key, no recovery code, and no escrow. Make sure your passkey is backed up (iCloud Keychain / Google Password Manager sync, or a second registered device) before relying on encryption for important data.</p><p class="nwd-note ui-hint"><strong>Opt-in.</strong> You can disable encryption later from the lock icon in the sidebar header — the data is rewritten as plaintext and DeepView signals your authenticator to remove the passkey (where supported).</p>${e?R:L`<p class="wsl-error">Your browser doesn't expose the WebAuthn passkey API. Try Chrome, Safari, Edge, or Firefox in a recent version on a device with a platform authenticator.</p>`} <label class="wsl-field"><span>Passkey label</span> <input type="text" class="nwd-input" data-role="name" placeholder="DeepView" maxlength="200" .value="${this._userName}" @input="${this._onNameInput}"></label> ${this._phase?L`<p class="nwd-note">${this._phase}</p>`:R} ${this._error?L`<p class="wsl-error">${this._error}</p>`:R}<footer class="nwd-actions"><span class="nwd-spacer"></span> <button type="button" ?disabled="${this._busy&&!this._canCancel}" @click="${this._onCancel}">Cancel</button> <button type="button" class="primary" ?disabled="${!e||this._busy}" @click="${this._onEnable}">${this._busy?`Working…`:`Enable encryption`}</button></footer>`}render(){return L`<dialog @close="${this._onClose}" @cancel="${this._onDialogCancel}"><header><h3>${this._success?`Encryption enabled`:this._orphan?`Previous passkey setup detected`:`Enable passkey encryption`}</h3></header>${this._body()}</dialog>`}};customElements.define(`passkey-setup-dialog`,Rr);function zr(){return x(`passkey-setup-dialog`)}var Br=class extends b{_onConfirm=()=>this._finish(!0);render(){return L`<dialog @close="${this._onClose}"><header><h3>Changes might not be saved</h3></header><p class="nwd-intro">Triage works in this tab, but this browser <strong>can't save its sync state right now</strong> — so changes that haven't reached the server could be lost if you reload the page.</p><p class="nwd-intro">This usually means the browser's storage is full, or another tab is running a newer version of the app (to avoid overwriting that newer version's data, this tab has paused saving).</p><p class="nwd-note ui-hint">Free up space if storage is full, and close or reload other tabs of this app to the same version. This notice clears on its own once saving works again.</p><footer class="nwd-actions"><span class="nwd-spacer"></span> <button type="button" class="primary" @click="${this._onConfirm}">Got it</button></footer></dialog>`}};customElements.define(`persistence-degraded-dialog`,Br);function Vr(){return new Promise(e=>{let t=document.createElement(`persistence-degraded-dialog`);t.exclusive=!0;let n=n=>{t.remove(),e({shown:n})};t.addEventListener(`resolve`,()=>n(!0)),t.addEventListener(`modal-conflict`,()=>n(!1)),document.body.append(t)})}function Hr(e){if(jt(e))throw Error(`This is a links file, not a report. Wrap report rows in a groups or findings object to compare them.`);let t=e;try{let n=JSON.parse(e);Array.isArray(n)&&(t=JSON.stringify({groups:n}))}catch{}let{data:n,reason:r}=Wt(t);if(!n)throw Error(r);let i=[],a=new Map,o=0;for(let e of Ut(n)){let t=new Set;for(let n of Array.isArray(e)?e:[e])if(n&&typeof n==`object`&&!Array.isArray(n)){if(typeof n.id!=`string`||!n.id){o++;continue}t.add(n.id),a.has(n.id)||a.set(n.id,[]),a.get(n.id).push(n)}i.push([...t].toSorted())}return{rows:i,byId:a,missingIds:o}}function Ur(e){let t=new Set;for(let n of e){let e=[...new Set(n)].toSorted();for(let n=0;n<e.length;n++)for(let r=n+1;r<e.length;r++)t.add(JSON.stringify([e[n],e[r]]))}return t}function Wr(e){let t=new Map,n=e=>{let n=e;for(;t.get(n)!==n;)t.set(n,t.get(t.get(n))),n=t.get(n);return n};for(let r of e){let[e,i]=JSON.parse(r);for(let n of[e,i])t.has(n)||t.set(n,n);t.set(n(e),n(i))}let r=new Map;for(let e of[...t.keys()].toSorted()){let t=n(e);r.has(t)||r.set(t,[]),r.get(t).push(e)}return[...r.values()].toSorted((e,t)=>e[0].localeCompare(t[0]))}function W(e,t,n){let r=(e.byId.get(t)??[]).map(e=>n===`verdict`?Jt(e)||null:n===`severity`?Kt(e)??null:n===`originalSeverity`?e.severity??null:n===`confidence`&&Number.isFinite(e.confidence)?e.confidence:null);return[...new Set(r)].toSorted((e,t)=>String(e).localeCompare(String(t),void 0,{numeric:!0}))}var Gr=(e,t)=>e.length===t.length&&e.every((e,n)=>e===t[n]),G=(e,t)=>[...e].filter(e=>!t.has(e)).toSorted();function Kr(e,t){let n=new Set(e.byId.keys()),r=new Set(t.byId.keys()),i=[...n].filter(e=>r.has(e)).toSorted(),a=new Set(i),o=e=>Ur(e.rows.map(e=>e.filter(e=>a.has(e)))),s=o(e),c=o(t),l={confirmed:[],refuted:[],otherVerdicts:[],confidence:[],severity:[]};for(let n of i)for(let r of[`verdict`,`confidence`,`severity`]){let i=W(e,n,r),a=W(t,n,r),o={id:n,before:i,after:a};if(r===`severity`&&(o.originalBefore=W(e,n,`originalSeverity`),o.originalAfter=W(t,n,`originalSeverity`)),!Gr(i,a)||r===`severity`&&!Gr(o.originalBefore,o.originalAfter)){if(r!==`verdict`){l[r].push(o);continue}l[a.length===1&&a[0]===`confirmed`?`confirmed`:a.length===1&&a[0]===`refuted`?`refuted`:`otherVerdicts`].push(o)}}let u=Wr(G(c,s)),d=Wr(G(s,c)),f=G(r,n),p=G(n,r);return{before:e,after:t,added:f,removed:p,shared:i,joined:u,split:d,...l,unchanged:f.length===0&&p.length===0&&u.length===0&&d.length===0&&Object.values(l).every(e=>e.length===0)}}var qr=`dialog{width:min(94vw,1180px);max-width:1180px;height:min(88dvh,960px);max-height:92dvh;margin-block:auto;padding:0;overflow:hidden;background:var(--theme-bar)}dialog[open]{display:flex;flex-direction:column}header{display:flex;align-items:center;justify-content:space-between;gap:1rem;padding:1.1rem 1.3rem}header h3{margin-bottom:.25rem;font-size:1.1rem}header p,.muted,.note,.empty{color:var(--muted);font-size:.78rem;line-height:1.5}button,select,input{font:inherit;font-size:.8rem}button,select,.file-button,.search{color:var(--text);background:var(--surface);border:1px solid var(--border);border-radius:var(--ui-radius, 6px);padding:.45rem .65rem}button:not(:disabled):hover,.file-button:hover{background:var(--surface-active)}button:disabled{opacity:.45}button:focus-visible,select:focus-visible,.search:focus-visible,.file-button:focus-within{outline:2px solid var(--accent);outline-offset:2px}.pickers{display:grid;grid-template-columns:minmax(0,1fr) auto minmax(0,1fr);align-items:start;gap:1rem;padding:0 1.3rem 1rem;border-bottom:1px solid var(--border)}.picker{min-width:0}.picker>label{display:block;font-size:.72rem;font-weight:600;margin-bottom:.4rem;color:var(--muted)}.picker select{width:100%;text-overflow:ellipsis}.file-picker{display:flex;align-items:center;flex-wrap:wrap;gap:.6rem;margin-top:.55rem}.file-button{position:relative;display:inline-block;font-size:.74rem;padding:.3rem .5rem;overflow:hidden}.file-button input{position:absolute;inset:0;opacity:0;width:100%}.swap{align-self:start;margin-top:1.5rem;font-size:1rem}.results{overflow:auto;min-height:0;padding:1.1rem 1.3rem 1.4rem}.summary{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:.65rem}.summary>div{display:flex;flex-direction:column;gap:.35rem;padding:.8rem 1rem;background:var(--bg);border:1px solid var(--border);border-radius:var(--ui-radius, 7px)}.summary span{font-size:.72rem;color:var(--muted)}.summary strong{font-size:1.15rem;font-variant-numeric:tabular-nums}.positive h4{color:var(--green)}.negative h4,.verdict-confirmed h4,.summary .verdict-confirmed strong{color:var(--critical)}.verdict-refuted h4,.summary .verdict-refuted strong{color:var(--muted)}.change-counts{display:flex;flex-wrap:wrap;gap:.4rem;margin-top:.7rem}.change-counts span{font-size:.7rem;padding:.3rem .5rem;border-radius:5px;background:var(--bg);color:var(--muted)}.results-head{display:flex;align-items:center;flex-wrap:wrap;gap:.75rem;margin-block:1rem}.results-head p{flex:1;font-size:.78rem;color:var(--muted);overflow-wrap:anywhere}.search{width:min(100%,17rem)}.section-title{margin:1.2rem 0 .5rem;font-size:.87rem}.note{margin:-.15rem 0 .6rem}.columns{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.8rem}.change-section{border:1px solid var(--border);border-radius:var(--ui-radius, 7px);overflow:hidden;margin-bottom:.6rem}.change-section h4{display:flex;justify-content:space-between;align-items:center;gap:.5rem;font-size:.78rem;font-weight:600;padding:.65rem .8rem;background:var(--bg)}.count{font-variant-numeric:tabular-nums;font-size:.7rem}ul{list-style:none}li{border-top:1px solid var(--border);padding:.7rem .8rem}.change-row{display:flex;justify-content:space-between;align-items:start;gap:.8rem}.finding{display:flex;flex-direction:column;gap:.2rem;min-width:0;overflow-wrap:anywhere}.finding strong{font-size:.77rem;font-weight:500;line-height:1.4}.finding code,.finding small{color:var(--muted);font-size:.66rem;line-height:1.4}.change-value{display:flex;align-items:center;justify-content:end;flex-wrap:wrap;gap:.3rem;max-width:48%;flex-shrink:0;font-size:.73rem;text-align:end;overflow-wrap:anywhere}.from{color:var(--muted)}.change-value small{width:100%;color:var(--muted);font-size:.67rem}.group-members{display:flex;flex-direction:column;gap:.55rem}.group-shape{display:flex;flex-wrap:wrap;gap:.45rem;margin-top:.7rem;font:.66rem/1.5 monospace;overflow-wrap:anywhere}.group-shape>span{min-width:0}.empty{padding:.7rem .8rem}.more{margin:.5rem .8rem .7rem;font-size:.72rem}.placeholder{padding:4rem 1rem;text-align:center;color:var(--muted);font-size:.85rem}.identical{color:var(--green);font-size:.82rem;padding-block:.5rem}.error,.warning{font-size:.75rem;line-height:1.5;margin-top:.5rem;overflow-wrap:anywhere}.error{color:var(--critical)}.warning{color:var(--yellow)}dialog>.error{padding:0 1.3rem .6rem}@media(width<=650px){.pickers{gap:.5rem;padding-inline:.8rem}.columns{grid-template-columns:minmax(0,1fr);gap:0}.summary{grid-template-columns:repeat(2,minmax(0,1fr))}.summary>div{padding:.6rem}.results-head p{flex-basis:100%}.search{width:100%}header,.results{padding-inline:.8rem}.change-row{flex-wrap:wrap}.change-value{max-width:100%}}
|
|
38
|
-
`,K=()=>({key:``,name:``,report:null,error:``,loading:!1}),q=(e,t)=>e.map(e=>e===null?`Not supplied`:t===`confidence`?`${e}/10`:String(e).replaceAll(`_`,` `)).join(` / `),J=50,Jr=class extends b{static styles=[...b.styles,I(qr)];static properties={_names:{state:!0},_sides:{state:!0},_catalogError:{state:!0},_catalogLoading:{state:!0},_diff:{state:!0},_query:{state:!0},_limits:{state:!0}};constructor(){super(),this._names=[],this._sides=[K(),K()],this._tokens=[0,0],this._catalogError=``,this._catalogLoading=!0,this._diff=null,this._query=``,this._limits={}}beforeOpen(){this._loadCatalog()}focusInitial(){this.renderRoot.querySelector(`select`)?.focus()}async _loadCatalog(){try{let e=await wt();if(this._settled)return;this._names=e.filter(e=>fe(e)!==At);let t=this._names.includes(k.currentFile)?k.currentFile:this._names[0];t&&!this._tokens[0]&&this._select(0,t);let n=this._names.find(e=>e!==t);n&&!this._tokens[1]&&this._select(1,n)}catch(e){this._settled||(this._catalogError=`Couldn't list saved reports: ${e.message}`)}finally{this._settled||(this._catalogLoading=!1)}}_setSide(e,t){this._sides=this._sides.map((n,r)=>r===e?t:n),this._limits={};let[n,r]=this._sides.map(e=>e.report);this._diff=n&&r?Kr(n,r):null}async _select(e,t,n){let r=++this._tokens[e],i={...K(),key:n?``:t,name:t,loading:!!t};if(this._setSide(e,i),t)try{let a=n?await n.text():await Et(t);if(this._settled||this._tokens[e]!==r)return;this._setSide(e,{...i,loading:!1,report:Hr(a)})}catch(n){!this._settled&&this._tokens[e]===r&&this._setSide(e,{...i,loading:!1,error:`Couldn't read ${t}: ${n.message}`})}}_file(e,t){let n=t.target.files?.[0];t.target.value=``,n&&this._select(e,n.name,n)}_swap(){this._sides.some(e=>e.loading)||(this._tokens=this._tokens.map(e=>e+1),this._sides=this._sides.toReversed(),this._setSide(0,this._sides[0]))}_picker(e){let t=this._sides[e],n=e===0?`Before`:`After`;return L`<section class="picker"><label for="${`report-${e}`}">${n}</label> <select id="${`report-${e}`}" @change="${t=>this._select(e,t.target.value)}"><option value="" .selected="${!t.key}">${t.name&&!t.key?t.name:`Choose a saved report…`}</option>${this._names.map(e=>L`<option value="${e}" .selected="${t.key===e}">${e}</option>`)}</select><div class="file-picker"><label class="file-button">Open file…<input type="file" accept=".json,.md,.markdown" aria-label="${`Open ${n.toLowerCase()} report file`}" @change="${t=>this._file(e,t)}"></label> <span class="muted">${t.loading?`Reading report…`:t.report?`${t.report.byId.size.toLocaleString()} unique findings`:`JSON or Markdown`}</span></div>${t.error?L`<p class="error" role="alert">${t.error}</p>`:R} ${t.report?.missingIds?L`<p class="warning">${t.report.missingIds} findings without an id excluded.</p>`:R}</section>`}_finding(e){let t=this._diff.after.byId.get(e)?.[0]??this._diff.before.byId.get(e)?.[0];return L`<span class="finding"><strong>${P(t)||e}</strong><code>${e}</code>${t.file?L`<small>${t.file}</small>`:R}</span>`}_matches(e){let t=this._query.trim().toLowerCase();return!t||[e,...[...this._diff.before.byId.get(e)??[],...this._diff.after.byId.get(e)??[]].flatMap(e=>[P(e),e.file])].filter(Boolean).some(e=>e.toLowerCase().includes(t))}_section(e,t,n,r,i=``){let a=n.filter(e=>(Array.isArray(e)?e:[e.id??e]).some(e=>this._matches(e))),o=this._limits[e]??J;return L`<section class="${`change-section ${i}`}" aria-label="${t}"><h4>${t}<span class="count">${this._query.trim()?`${a.length} / `:``}${n.length}</span></h4>${a.length>0?L`<ul>${a.slice(0,o).map(r)}</ul>`:L`<p class="empty">${n.length>0?`No matching findings.`:`No changes.`}</p>`} ${a.length>o?L`<button class="more" @click="${()=>{this._limits={...this._limits,[e]:o+J}}}">Show ${Math.min(J,a.length-o)} more (${a.length-o} remaining)</button>`:R}</section>`}_changeRow(e,t){let n=t===`severity`&&JSON.stringify(e.originalBefore)!==JSON.stringify(e.originalAfter);return L`<li class="change-row">${this._finding(e.id)}<span class="change-value"> <span class="from">${q(e.before,t)}</span><span aria-label="to">→</span><span>${q(e.after,t)}</span> ${n?L`<small>Original: ${q(e.originalBefore)} → ${q(e.originalAfter)}</small>`:R}</span></li>`}_presenceRow(e,t){let n=q(W(t,e,`verdict`));return L`<li class="change-row">${this._finding(e)}<span class="change-value"> <span>${q(W(t,e,`severity`))}</span><span>${q(W(t,e,`confidence`),`confidence`)}</span> ${n===`Not supplied`?R:L`<small>${n}</small>`}</span></li>`}_groupRow(e){let t=new Set(e),n=e=>e.rows.map(e=>e.filter(e=>t.has(e))).filter(e=>e.length).map(e=>e.join(`, `)).filter((e,t,n)=>n.indexOf(e)===t).join(` | `);return L`<li class="group-row"><div class="group-members">${e.map(e=>this._finding(e))}</div><div class="group-shape"><span class="from">${n(this._diff.before)}</span><span aria-label="to">→</span><span>${n(this._diff.after)}</span></div></li>`}_verdictCount(e,t){return[...e.byId.keys()].filter(n=>W(e,n,`verdict`).includes(t)).length}_results(){let e=this._diff;return e?L`<div class="summary" aria-label="Comparison summary"><div><span>Unique findings</span><strong>${e.before.byId.size} → ${e.after.byId.size}</strong></div><div><span>In common</span><strong>${e.shared.length}</strong></div><div class="verdict-confirmed"><span>Confirmed</span><strong>${this._verdictCount(e.before,`confirmed`)} → ${this._verdictCount(e.after,`confirmed`)}</strong></div><div class="verdict-refuted"><span>Refuted</span><strong>${this._verdictCount(e.before,`refuted`)} → ${this._verdictCount(e.after,`refuted`)}</strong></div></div><div class="change-counts" aria-label="Change counts"><span>${e.added.length} added</span><span>${e.removed.length} removed</span> <span>${e.joined.length} joined</span><span>${e.split.length} split</span> <span>${e.confidence.length} confidence</span><span>${e.severity.length} severity</span></div><div class="results-head"><p>Changes from <strong>${this._sides[0].name}</strong> to <strong>${this._sides[1].name}</strong>.</p><input class="search" type="search" aria-label="Filter comparison findings" placeholder="Filter by id, title or file…" .value="${this._query}" @input="${e=>{this._query=e.target.value,this._limits={}}}"></div>${e.shared.length===0&&(e.before.missingIds||e.after.missingIds)?L`<p class="warning" role="status">No shared ids to compare. Findings without ids cannot be matched.</p>`:e.unchanged?L`<p class="identical" role="status">No differences in finding ids, grouping, verdicts, confidence or severity.</p>`:R}<h3 class="section-title">Findings</h3><div class="columns">${this._section(`added`,`Added`,e.added,t=>this._presenceRow(t,e.after),`positive`)} ${this._section(`removed`,`Removed`,e.removed,t=>this._presenceRow(t,e.before),`negative`)}</div><h3 class="section-title">Revalidation</h3><p class="note">Changed verdicts for shared ids, grouped by the after report’s verdict.</p><div class="columns">${this._section(`confirmed`,`Confirmed`,e.confirmed,e=>this._changeRow(e,`verdict`),`verdict-confirmed`)} ${this._section(`refuted`,`Refuted`,e.refuted,e=>this._changeRow(e,`verdict`),`verdict-refuted`)}</div>${e.otherVerdicts.length>0?this._section(`other`,`Other verdict changes`,e.otherVerdicts,e=>this._changeRow(e,`verdict`)):R}<h3 class="section-title">Grouping</h3><p class="note">Only shared ids. Each cluster connects changed pairs; added or removed findings do not count as regrouping.</p><div class="columns">${this._section(`joined`,`Joined`,e.joined,e=>this._groupRow(e),`positive`)} ${this._section(`split`,`Split`,e.split,e=>this._groupRow(e),`negative`)}</div><h3 class="section-title">Ratings</h3><p class="note">Shared ids only. Severity includes report corrections; changes to original severity are also shown.</p><div class="columns">${this._section(`confidence`,`Confidence changes`,e.confidence,e=>this._changeRow(e,`confidence`))} ${this._section(`severity`,`Severity changes`,e.severity,e=>this._changeRow(e,`severity`))}</div>`:L`<p class="placeholder" role="status">${this._sides.some(e=>e.loading)||this._catalogLoading?`Loading reports…`:`Choose two saved reports or open two files to compare.`}</p>`}render(){return L`<dialog aria-labelledby="compare-title" @close="${this._onClose}"><header><div><h3 id="compare-title">Compare reports</h3><p>Compare findings, grouping and ratings between two reports.</p></div><button aria-label="Close report comparison" @click="${this._onClose}">Close</button></header><div class="pickers">${this._picker(0)}<button class="swap" aria-label="Swap before and after reports" ?disabled="${this._sides.some(e=>e.loading)}" @click="${this._swap}">⇄</button>${this._picker(1)}</div>${this._catalogError?L`<p class="error" role="alert">${this._catalogError} You can still open files directly.</p>`:R}<div class="results">${this._results()}</div></dialog>`}};customElements.define(`report-compare-dialog`,Jr);function Yr(){if(St())throw Error(`Report comparison is available in local/E2E mode.`);return x(`report-compare-dialog`)}var Xr=`dialog{--issue-open: var(--green);--issue-merged: #a371f7;--issue-closed: var(--critical);--issue-quiet: var(--muted);--claude: #d97757;width:min(94vw,52rem);max-width:none;max-height:calc(100dvh - 7rem);padding:0;overflow:hidden}dialog[open]{display:flex;flex-direction:column}header{display:flex;align-items:start;gap:.6rem;flex-shrink:0;padding:.8rem 1rem;border-bottom:1px solid var(--border)}header h3{display:flex;align-items:baseline;gap:.5rem;flex:1;min-width:0;margin:0;font-size:1rem;line-height:1.35;overflow-wrap:anywhere}header button{flex-shrink:0;border:0;background:transparent;color:var(--muted);font-family:inherit;font-size:1.4rem;line-height:1;padding:0 .25rem;&:hover{color:var(--text)}&.back{font-size:1.1rem}}.repo-item-body{display:flex;flex-direction:column;gap:.75rem;min-height:0;padding:.9rem 1rem 1rem;overflow:auto;overscroll-behavior:contain;font-size:.85rem}.repo-item-number{color:var(--muted);font-weight:400;font-variant-numeric:tabular-nums}.repo-item-meta{display:flex;flex-wrap:wrap;align-items:center;gap:.3rem .9rem;color:var(--muted)}.repo-item-meta strong{color:var(--text);font-weight:500}code{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.9em;color:var(--text)}.repo-item-badge{--state: var(--issue-open);padding:.1rem .55rem;border-radius:999px;background:var(--state);color:var(--surface);font-weight:500;&.is-merged,&.is-completed{--state: var(--issue-merged)}&.is-closed{--state: var(--issue-closed)}&.is-draft,&.is-not-planned{--state: var(--issue-quiet)}}svg{flex:none;width:14px;height:14px;fill:none;stroke:currentcolor;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round}svg .fill{fill:currentcolor;stroke:none}svg .dotted{stroke-dasharray:1 2.2}.repo-item-state{--state: var(--issue-open);display:inline-flex;align-self:center;color:var(--state);&.is-merged,&.is-completed{--state: var(--issue-merged)}&.is-closed{--state: var(--issue-closed)}&.is-draft,&.is-not-planned{--state: var(--issue-quiet)}}.repo-item-mark{display:inline-flex;align-items:center;gap:.25rem;& svg{width:12px;height:12px}&.is-approved,&.is-success{color:var(--green)}&.is-changes,&.is-failure{color:var(--critical)}&.is-pending{color:var(--medium)}}.is-added{color:var(--green)}.is-deleted{color:var(--critical)}npm-delta{display:inline-block;flex:none;min-width:1px;min-height:1em}.npm-delta{flex:none;color:var(--muted);font-size:.7rem;white-space:nowrap;font-variant-numeric:tabular-nums}.npm-delta-typeless{margin-left:.35em;opacity:.8}.repo-item-labels{display:flex;flex-wrap:wrap;gap:.3rem}.repo-item-label{padding:0 .45rem;border:1px solid var(--border);border-radius:999px;font-size:.75rem;line-height:1.5;&:before{content:"";display:inline-block;width:.5rem;height:.5rem;margin-right:.3rem;border-radius:50%;background:var(--label, var(--muted));vertical-align:middle}}.repo-item-agents{display:inline-flex;align-items:center;align-self:start;gap:.25rem;height:1lh}.agent-mark{display:inline-flex}.agent-mark :is(svg,img){width:16px;height:16px}.agent-mark:is(.is-claude,.is-codex,.is-copilot) svg{fill:currentcolor;stroke:none}.agent-mark.is-claude svg{color:var(--claude)}.agent-logo{display:inline-flex}.agent-logo svg{flex:none}.agent-avatar{border-radius:3px}.repo-item-section{& h4{margin:0 0 .3rem;color:var(--muted);font-size:.72rem;font-weight:600;letter-spacing:.04em;text-transform:uppercase}& h4.repo-item-deps-heading{display:flex;align-items:center;justify-content:space-between;gap:.5rem;min-height:1.6rem}& ul{margin:0;padding:0;list-style:none;border:1px solid var(--border);border-radius:var(--ui-radius, 6px);overflow:hidden}& li+li{border-top:1px solid var(--border)}}.dependency-review-copy-button{display:inline-flex;align-items:center;justify-content:center;width:1.6rem;height:1.6rem;padding:0;border:0;border-radius:var(--ui-radius, 4px);background:transparent;color:var(--muted);&:hover{background:var(--surface-active);color:var(--text)}&.copied{color:var(--green)}& svg{width:.9rem;height:.9rem}}.repo-item-row{display:flex;align-items:center;gap:.5rem;width:100%;padding:.35rem .6rem;border:0;background:none;color:var(--text);font:inherit;text-align:left;text-decoration:none;&:hover{background:var(--surface-active)}&:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}}.repo-item-title{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.review-row{display:flex;flex-wrap:wrap;align-items:center;gap:.25rem .7rem;padding:.35rem .6rem;& time{margin-left:auto;color:var(--muted);font-variant-numeric:tabular-nums}}.review-state{display:inline-flex;align-items:center;gap:.25rem;& svg{width:12px;height:12px}&.is-approved>svg{color:var(--green)}&:is(.is-changes,.is-failed)>svg{color:var(--critical)}&.is-running>svg{color:var(--medium)}&:is(.is-commented,.is-dismissed,.is-requested){color:var(--muted)}}.review-on{color:var(--muted);& a{color:var(--text);text-decoration:none;&:hover{color:var(--accent)}}}.review-path{flex:none;max-width:45%;margin-left:auto;overflow:hidden;color:var(--muted);text-overflow:ellipsis;white-space:nowrap}.review-severity{flex:none;padding:0 .35rem;border-radius:999px;background:var(--low);color:#fff;font-size:.68rem;font-weight:600;line-height:1.5;&.is-p0{background:var(--critical)}&.is-p1{background:var(--high)}&.is-p2{background:var(--medium);color:#000}}.repo-item-lockfiles+.repo-item-lockfiles{margin-top:.6rem}.repo-item-lockfiles>p{margin:0 0 .3rem;overflow-wrap:anywhere}.repo-item-version{color:var(--muted)}.repo-item-was{flex:0 1000 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--muted)}span.repo-item-row:hover{background:none}.repo-item-review{display:inline-flex;align-items:center;gap:.2rem;flex:none;font-size:.75rem;font-variant-numeric:tabular-nums;&.is-passed{color:var(--green)}&.is-wanted{color:var(--medium)}&.is-failed{color:var(--critical)}& svg{width:12px;height:12px}}.repo-item-failed{margin-bottom:.6rem;>p{margin:0 0 .3rem;color:var(--critical)}}.repo-item-unreviewed{margin-top:.6rem;& summary{color:var(--medium);cursor:default}& ul{margin-top:.3rem}}.repo-item-tag{flex:none;padding:0 .4rem;border:1px solid var(--border);border-radius:999px;color:var(--muted);font-size:.72rem;line-height:1.5;&.is-new{border-color:currentcolor;color:var(--medium)}}.repo-item-description{padding-top:.75rem;border-top:1px solid var(--border);white-space:pre-wrap;overflow-wrap:anywhere;line-height:1.6}.repo-item-quiet{color:var(--muted)}.nwd-actions{margin:0;padding:.6rem 1rem}.nwd-actions a{border:1px solid var(--border);border-radius:var(--ui-radius, 5px);padding:.35rem .9rem;color:var(--text);font-size:.8rem;text-decoration:none;&:hover{border-color:var(--accent);color:var(--accent)}&.primary{background:var(--accent);border-color:var(--accent);color:var(--surface);&:hover{color:var(--surface);filter:brightness(1.08)}}&:focus-visible{outline:2px solid var(--accent);outline-offset:2px}}a,button{cursor:default}:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}.repo-item-body:focus{outline:none}
|
|
39
|
-
`,
|
|
40
|
-
`,Y={open:[`open`,`Open`],merged:[`merged`,`Merged`],closed:[`closed`,`Closed`],branch:[`draft`,`A branch alone`]},X=e=>e==null?``:new Date(e).toLocaleDateString(`en-US`,{month:`short`,day:`numeric`,year:`numeric`}),Z=(e,t)=>`${e.toLocaleString(`en-US`)} ${t}${e===1?``:`s`}`,Q=(e,t,n=!1)=>e?L`<a class="${n?`primary`:``}" href="${e}" target="_blank" rel="noopener noreferrer">${t}</a>`:R,Qr={mirror:`mirror`,git:`git`,tarball:`tarball`,file:`file`},$r={git:`From git, which isn’t reviewed`,tarball:`A tarball not npm’s registry’s of its version, which isn’t reviewed`,integrity:`Locked with no integrity, which its reviews are held to`},ei={"lockfile-unreadable":`A lockfile here couldn’t be read`,"lockfile-unsupported":`Bun’s lockfile here isn’t read`,"github-not-found":`GitHub has none of a lockfile here at a commit compared`},ti=class extends b{static styles=[...b.styles,I(tn),I(e),I(Zr),I(Xr)];static properties={repository:{attribute:!1},target:{attribute:!1},_trail:{state:!0},_items:{state:!0}};constructor(){super(),this._trail=[],this._items=new Map,this._controller=new AbortController}connectedCallback(){super.connectedCallback(),this._unsubscribe=he(e=>{(!e||e.repoId===this.repository?.repoId&&e.number===this._trail.at(-1)?.number)&&this.requestUpdate()}),this._unobserve=T(e=>{je(),e||this.requestUpdate()}),this._trail.length===0&&this.target&&this.open(this.target)}disconnectedCallback(){this._controller.abort(),this._unsubscribe?.(),this._unobserve?.(),super.disconnectedCallback()}focusInitial(){this.renderRoot.querySelector(`.repo-item-body`)?.focus()}open(e){if(this._trail=[...this._trail,e],e.number==null||this._items.has(e.number))return;let t=e.number;this._items=new Map(this._items).set(t,{status:`loading`});let{repoId:n}=this.repository;bt(n,t,{signal:this._controller.signal}).then(e=>(Ee(n,[e],Te(e)),{status:`ready`,item:e}),e=>e?.name===`AbortError`?null:{status:`error`,error:e?.message||`It could not be read.`}).then(e=>(e&&(this._items=new Map(this._items).set(t,e)),null))}back(){this._trail=this._trail.slice(0,-1)}render(){let e=this._trail.at(-1);if(!e)return R;let[t,n,r]=e.session?this.renderSession(e.session):this.renderItem(e);return L`<dialog aria-labelledby="repo-item-title" @close="${this._onClose}" @click="${this._onBackdrop}"><header>${this._trail.length>1?L`<button type="button" class="back" aria-label="Back" @click="${()=>this.back()}">←</button>`:R}<h3 id="repo-item-title">${t}</h3><button type="button" aria-label="Close" @click="${this._onClose}">×</button></header><div class="repo-item-body" tabindex="-1">${n}</div><div class="nwd-actions"><span class="nwd-spacer"></span>${r}</div></dialog>`}renderItem({number:e,preview:t}){let n=this._items.get(e),r=ke(this.repository.repoId,e)?.item,i=r?{...t,...n?.item,...r}:n?.item??t??null,a=i?.kind??`issues`,o=this.repository.repo,s=o?`https://github.com/${o}/${a===`pulls`?`pull`:`issues`}/${e}`:null,[c,l]=i?ie(a,i):[`open`,``],u=L`${i?L`<span class="${`repo-item-state is-${c}`}" role="img" aria-label="${l}">${kt[a][c]}</span>`:R} <span>${i?.title||`(no title)`} <span class="repo-item-number">#${e}</span></span>`,m=n?.status===`ready`?n.item:null,h=a===`pulls`,g=([e,t],n)=>L`<span class="${`repo-item-mark is-${n}`}">${t}${e}</span>`;return[u,L`<p class="repo-item-meta">${i?L`<span class="${`repo-item-badge is-${c}`}">${l}</span>`:R} ${i?.author?L`<span><strong>${O(i.author)}</strong> opened ${X(i.createdAt)}</span>`:i?.createdAt?L`<span>Opened ${X(i.createdAt)}</span>`:R} ${i?.state===`merged`?L`<span>merged${m?.mergedBy?L` by <strong>${O(m.mergedBy)}</strong>`:R} ${X(i.mergedAt??i.closedAt)}</span>`:i?.closedAt&&i.state!==`open`?L`<span>closed ${X(i.closedAt)}</span>`:R} ${i?.comments>0?L`<span class="repo-item-mark">${d}${Z(i.comments,`comment`)}</span>`:R}</p>${i?.labels?.length>0?L`<p class="repo-item-labels">${i.labels.map(e=>L`<span class="repo-item-label" style="${M(e.color?{"--label":`#${e.color}`}:{})}">${e.name}</span>`)}${i.moreLabels>0?L`<span class="repo-item-label">+${i.moreLabels}</span>`:R}</p>`:R} ${h&&i?.head?L`<p class="repo-item-meta"><span><code>${i.head}</code>${i.base?L` → <code>${i.base}</code>`:R}</span> <span><span class="is-added">+${i.additions.toLocaleString(`en-US`)}</span> <span class="is-deleted">−${i.deletions.toLocaleString(`en-US`)}</span></span> ${m?L`<span>${Z(m.commits,`commit`)}</span><span>${Z(m.files,`file`)} changed</span>`:R} ${p[i.review]&&i.state===`open`?g(p[i.review],i.review):R} ${f[i.checks]?g(f[i.checks],i.checks):R}</p>`:R} ${h&&m?Ce(m.reviews,o):R} ${m?this.renderLinked(h?`Closes`:`Closed by`,h?`issues`:`pulls`,h?m.closes:m.closedBy,m.moreLinked):R} ${h&&_e(i)?this.renderDependencies(e,i.state===`open`):R} ${m?L`<div class="repo-item-description">${m.body?ee(Ft(m.body)):L`<p class="repo-item-quiet">No description.</p>`}</div>${m.bodyShortened?L`<p class="repo-item-quiet">Shortened here; the rest of it is on GitHub.</p>`:R}`:n?.status===`error`?L`<p class="repo-item-quiet" role="alert">${n.error}</p>`:L`<p class="repo-item-quiet" role="status">Reading it…</p>`}`,Q(s,`Open on GitHub`,!0)]}renderDependencies(e,t){let n=ve(this.repository.repoId,e,t);if(n.status===`ready`&&n.lockfiles.length===0&&!n.cut)return R;let r=n.status===`loading`?L`<p class="repo-item-quiet" role="status">Reading its lockfiles…</p>`:n.status===`error`?L`<p class="repo-item-quiet" role="alert">${n.error}</p>`:this.renderAdded(n);return L`<section class="repo-item-section"><h4 class="repo-item-deps-heading">New dependency versions${n.formatted?L`<dependency-review-copy .text="${n.formatted}" .label="${`Copy your dependency reviews of its versions`}"></dependency-review-copy>`:R}</h4>${r}</section>`}renderAdded(e){let{lockfiles:t,cut:n,review:r}=e,i=t.filter(e=>e.error||e.added.length>0),a=r?be(e):null;return L`${r?L`${this.renderReviewTerms(r)}${this.renderFailures(Se(e),r)}`:R} ${i.length===0&&t.length>0?L`<p class="repo-item-quiet">None: every version its lockfiles lock, they locked before.</p>`:R} ${i.map(e=>L`<div class="repo-item-lockfiles"><p>${e.paths.map((e,t)=>L`${t>0?`, `:``}<code>${e}</code>`)}</p>${e.error?L`<p class="repo-item-quiet" role="alert">${ei[e.error]??`These lockfiles couldn’t be read`}${e.detail?`: ${e.detail}`:`.`}</p>`:L`<ul>${e.added.toSorted((e,t)=>+!!e.was-!!t.was).map(e=>L`<li>${this.renderPackage(e,r)}</li>`)}</ul>${e.more>0?L`<p class="repo-item-quiet">and ${e.more.toLocaleString(`en-US`)} more</p>`:R}`}</div>`)} ${a?.count>0?L`<details class="repo-item-unreviewed"><summary>${Z(a.count,`other version`)} in its lockfiles ${a.count===1?`wants`:`want`} reviews</summary><ul>${a.versions.map(e=>L`<li>${this.renderPackage(e,r,!0)}</li>`)}</ul>${a.count>a.versions.length?L`<p class="repo-item-quiet">and ${(a.count-a.versions.length).toLocaleString(`en-US`)} more</p>`:R}</details>`:R} ${n?L`<p class="repo-item-quiet">It changes more files, or lockfiles in more directories, than are read; those past them aren’t told.</p>`:R}`}renderReviewTerms({required:e,since:t,reviewedDependencies:n,unasked:r,baselineError:i,baselineUnread:a=[],commentReviews:o,commentsFailed:s,lockfiles:c}){let l=a.slice(0,5);return L`<p class="repo-item-quiet">Each version needs ${Z(e,`full, accepting review`)} by as many reviewers${n?`, or the server’s reviewed dependencies to pass it; a patched one, not reviews here`:t?L`; those the repository’s lockfiles locked at <code>${t.slice(0,7)}</code> need none`:R}.</p>${i?L`<p class="repo-item-quiet" role="alert">The versions its lockfiles locked at that commit couldn’t be read (${i}), so every version wants its reviews.</p>`:R} ${l.length>0?L`<p class="repo-item-quiet" role="alert">At that commit, ${l.map((e,t)=>L`${t>0?`, `:``}<code>${e}</code>`)}${a.length>l.length?` and ${(a.length-l.length).toLocaleString(`en-US`)} more`:``} couldn’t be read, so the versions ${a.length===1?`it`:`they`} locked want reviews.</p>`:R} ${r?L`<p class="repo-item-quiet" role="alert">The server’s reviewed dependencies didn’t answer for ${Z(r,`version`)}, so ${r===1?`it wants`:`they want`} reviews.</p>`:R} ${o?L`<p class="repo-item-quiet">Reviews in its comments by ${Z(o,`reviewer`)} count too while it is open, a patched version’s of its patch.</p>`:R} ${s?L`<p class="repo-item-quiet" role="alert">Its comments couldn’t be read for reviews, so none of theirs count.</p>`:R} ${c?L`<p class="repo-item-quiet">${c.mode===`include`?`Only these lockfiles are reviewed: `:`These lockfiles aren’t reviewed: `}${c.paths.length===0?`none`:c.paths.map((e,t)=>L`${t>0?`, `:``}<code>${e}</code>`)}.</p>`:R}`}renderFailures(e,t){if(!e)return R;let{versions:n,count:r,unread:i,unchecked:a,cut:o}=e;return L`<div class="repo-item-failed" role="alert">${r>0?L`<p>Its review fails: ${Z(r,`version`)} its lockfiles lock can’t be reviewed, as only npm’s registry’s, locked with an integrity, are.</p><ul>${n.map(e=>L`<li>${this.renderPackage(e,t,!0)}</li>`)}</ul>${r>n.length?L`<p class="repo-item-quiet">and ${(r-n.length).toLocaleString(`en-US`)} more</p>`:R}`:R} ${i.length>0||a>0||o?L`<p>Its review fails: not every lockfile it changes was read${a>0?`, and they lock ${Z(a,`version`)} past those checked`:``}.</p>`:R}</div>`}renderPackage(e,t=null,n=!1){let{name:r,version:i,source:a,from:o,dev:s,was:c}=e,l=(a===`registry`||a===`mirror`)&&i,u=n?void 0:c?.find(e=>e!==i),d=l&&!e.patched&&t?.canReview&&y(e,t)!==`unsupported`?N({view:`npm`,packageName:r,packageSpec:i,bundleTab:`review`,...u?{reviewPackage:r,reviewSpec:u}:{}}):null,f=l?d||u&&N({view:`npm`,packageName:r,packageSpec:i,bundleTab:`compare`,compareSpec:u})||N({view:`npm`,packageName:r,packageSpec:i}):null,p=L`${g}<span class="repo-item-title">${r}${i?L`<span class="repo-item-version">@${i}</span>`:R}</span> ${n?R:c?L`<span class="repo-item-was" data-tooltip-truncated data-tooltip="${`Was ${c.join(`, `)}`}">from ${c.join(`, `)}</span>`:L`<span class="repo-item-tag is-new" data-tooltip="Its lockfiles locked no version of it before">new</span>`} ${this.renderReview(e,t)} ${a===`registry`&&i&&y(e,t)===`wanted`?L`<npm-delta .name="${r}" .version="${i}" .base="${u??`(empty)`}"></npm-delta>`:R} ${s?L`<span class="repo-item-tag" data-tooltip="Only devDependencies need it">dev</span>`:R} ${e.patched?L`<span class="repo-item-tag" data-tooltip="pnpm patches it as it installs it">patched</span>`:R} ${Qr[a]?L`<span class="repo-item-tag" data-tooltip="${o??R}">${Qr[a]}</span>`:R}`;return f?L`<a class="repo-item-row" href="${f}" target="_blank" rel="noopener noreferrer">${p}</a>`:L`<span class="repo-item-row">${p}</span>`}renderReview(e,t){let n=y(e,t);if(n===`unsupported`)return L`<span class="repo-item-review is-failed" data-tooltip="${$r[e.unsupported]}">can’t review</span>`;if(e.reviewed&&n===`passed`)return L`<span class="repo-item-review is-passed" data-tooltip="The server’s reviewed dependencies pass it">${f.success[1]}reviewed</span>`;if(e.rejected)return L`<span class="repo-item-review is-failed" data-tooltip="A reviewer rejects it: it wants their newer accepting review">rejected</span>`;if(e.patched&&n===`wanted`)return L`<span class="repo-item-review is-wanted" data-tooltip="Reviews here pass no patch: the server’s reviewed dependencies, or reviews of its patch in the pull request’s comments, pass it">unreviewed</span>`;if(n===`baseline`)return L`<span class="repo-item-tag" data-tooltip="Locked at the commit reviews are counted since: it needs none">baseline</span>`;if(!n)return R;let r=`${Z(e.reviews,`full, accepting review`)} of ${t.required} required`;return L`<span class="${`repo-item-review is-${n}`}" data-tooltip="${r}">${n===`passed`?f.success[1]:R}${e.reviews}/${t.required}</span>`}renderLinked(e,t,n,r=0){return!n?.length&&!r?R:L`<section class="repo-item-section"><h4>${e}</h4>${n?.length?L`<ul>${n.map(e=>{let n={...e,kind:t,labels:[],comments:0},[r,i]=ie(t,n);return L`<li><button type="button" class="repo-item-row" @click="${()=>this.open({number:e.number,preview:n})}"><span class="${`repo-item-state is-${r}`}" role="img" aria-label="${i}">${kt[t][r]}</span> <span class="repo-item-number">#${e.number}</span><span class="repo-item-title">${e.title||`(no title)`}</span></button></li>`})}</ul>`:R}${r>0?L`<p class="repo-item-quiet">${n?.length?`and `:``}${r.toLocaleString(`en-US`)} more, on GitHub</p>`:R}</section>`}renderSession(e){let t=this.repository.repo,[n,r]=Y[e.state]??Y.branch,i=e.pulls.length+e.commits.length,a=L`<span class="repo-item-agents">${e.agents.map(e=>L`<span class="${`agent-mark is-${e}`}" role="img" aria-label="${oe(e)}">${ce(e)}</span>`)}</span><span>${e.title||`(no title)`}</span>`,o=ue(e);return[a,L`<p class="repo-item-meta"><span class="${`repo-item-badge is-${n}`}">${r}</span> ${e.authors.length>0?L`<span><strong>${ft(e.authors).map((e,t)=>L`${t>0?`, `:``}${O(e)}`)}</strong></span>`:R} ${e.startedAt==null?R:L`<span>${X(e.startedAt)}${e.updatedAt!=null&&X(e.updatedAt)!==X(e.startedAt)?` \u2013 ${X(e.updatedAt)}`:``}</span>`} <span>${i>0?Z(i,`task`):`no pull request`}</span> ${e.additions||e.deletions?L`<span><span class="is-added">+${e.additions.toLocaleString(`en-US`)}</span> <span class="is-deleted">−${e.deletions.toLocaleString(`en-US`)}</span></span>`:R}</p>${this.renderLinked(`Pull requests`,`pulls`,e.pulls)} ${e.commits.length>0?L`<section class="repo-item-section"><h4>Commits to the default branch</h4><ul>${e.commits.map(e=>L`<li><a class="repo-item-row" href="${t?`https://github.com/${t}/commit/${e.oid}`:R}" target="_blank" rel="noopener noreferrer"><code>${e.oid.slice(0,7)}</code><span class="repo-item-title">${e.title||`(no message)`}</span></a></li>`)}</ul></section>`:R} ${this.renderLinked(`Issues`,`issues`,e.issues.map(e=>({...e,reason:null})))} ${e.branches.length>0?L`<section class="repo-item-section"><h4>${e.branches.length===1?`Branch`:`Branches`}</h4><ul>${e.branches.map(e=>L`<li><a class="repo-item-row" href="${t?me(t,e):R}" target="_blank" rel="noopener noreferrer"><code>${e}</code></a></li>`)}</ul></section>`:R}`,L`${Q(o,e.sessions[0]?`Open in Claude Code`:`Open the Codex task`)}${Q(pe(e,t),`Open on GitHub`,!0)}`]}};customElements.define(`repository-item-dialog`,ti);function ni(e){return x(`repository-item-dialog`,e)}var ri=`dialog{width:min(94vw,880px);max-width:880px}.conflicts-list{list-style:none;display:flex;flex-direction:column;gap:.6rem;max-height:65vh;overflow-y:auto;overscroll-behavior:contain}.conflict-card{padding:.6rem .7rem;border:1px solid var(--border);border-radius:var(--ui-radius, 6px)}.finding-meta{display:flex;flex-wrap:wrap;align-items:center;gap:.4rem;color:var(--muted);font-size:.72rem}.location,code{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;overflow-wrap:anywhere}.location{flex:1}h4{margin:.4rem 0 .55rem;font-size:.88rem;font-weight:600;overflow-wrap:anywhere}dt{margin-top:.5rem;color:var(--muted);font-size:.72rem}.variant{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,15rem);gap:.65rem;padding:.4rem 0;font-size:.8rem;+&{border-top:1px solid var(--border)}}.value{white-space:pre-wrap;overflow-wrap:anywhere}.reports{display:flex;flex-wrap:wrap;place-content:start flex-end;gap:.3rem}.report-button{display:inline-flex;align-items:center;gap:.4rem;max-width:100%;min-height:22px;padding:0 .4rem 0 0;border:1px solid var(--border);border-radius:var(--ui-radius, 4px);overflow:hidden;background:var(--bg);color:var(--text);font:inherit;font-size:.72rem;cursor:default;text-align:left;>span:last-child{min-width:0;overflow-wrap:anywhere}&:hover{border-color:var(--accent)}}
|
|
41
|
-
`,ii={revalidate:`Outcome`,revalidateVerdict:`Explanation`,revalidateRecommendation:`Recommendation`},ai=class extends
|
|
42
|
-
`,ci=class extends
|
|
43
|
-
`,di=10,fi=class extends
|
|
44
|
-
`,_i={triage:`Triage state`,color:`Color`,fix:`Fix`,flagged:`Flag`,comment:`Comments`,ignoredReports:`Per-report ignores`},vi={"managed-only":`Only managed`,"local-only":`Only local`,mismatch:`Different`},yi={inprogress:`In progress`,fixed:`Fixed`,invalid:`Invalid`,deleted:`Deleted`,ignored:`Ignored`};function bi(e,t){return t===void 0||Array.isArray(t)&&t.length===0?
|
|
45
|
-
`)}`:e===`flagged`?t?`Flagged`:`Not flagged`:e===`triage`?yi[t]??t:t}var xi=class extends
|
|
46
|
-
`)[0]||e}</h4>${t.file?
|
|
47
|
-
`,wi=class extends
|
|
48
|
-
`,$=e=>Rt(e.own?`__own__`:e.name),Di=()=>Math.floor(Math.min(document.documentElement.clientWidth*.94,80*Number.parseFloat(getComputedStyle(document.documentElement).fontSize))),Oi=class extends
|
|
49
|
-
style=${
|
|
31
|
+
`,i=e.body+t+r.text,a=e.body.length+t.length;return{body:i,sections:e.sections.map(e=>e===r?{...e,selected:n,separator:t,start:a,end:i.length}:e)}}let i=e.body.slice(r.start,r.end),a=r.separator??``,o=r.start-a.length,s=e.body.slice(o,r.start)===a&&!e.sections.some(e=>e!==r&&e.selected&&e.start<r.start&&e.end>o)?o:r.start,c=e.body.slice(0,s)+e.body.slice(r.end),l=s-r.end;return{body:c,sections:e.sections.map(e=>e===r?{...e,text:i,selected:n,start:0,end:0}:e.selected&&e.start>=r.end?{...e,start:e.start+l,end:e.end+l}:e)}}var _r=`dialog{max-width:1040px;width:min(1040px,calc(100vw - 32px));margin-top:2rem;max-height:calc(100dvh - 4rem);overflow:hidden}dialog[open]{display:flex;flex-direction:column}header,.nwd-actions{flex-shrink:0}.dialog-content{min-height:0;overflow:auto;scrollbar-gutter:stable;padding:3px}.nwd-intro{overflow-wrap:anywhere}label{display:flex;flex-direction:column;gap:6px;margin:12px 0}textarea{min-height:clamp(240px,45dvh,540px);resize:vertical;line-height:1.55}.description-heading{display:flex;align-items:center;flex-wrap:wrap;gap:8px 16px;margin:16px 0 6px}.description-heading label{margin:0}.description-sections{display:flex;flex:1;flex-wrap:wrap;justify-content:flex-end;gap:6px}.description-sections label{flex-direction:row;align-items:center;font-size:.8rem;line-height:1.3;cursor:default;padding:5px 8px;border:1px solid var(--border);border-radius:var(--ui-radius, 6px);color:var(--muted);background:var(--surface)}.description-sections label:has(input:checked){color:var(--text);border-color:color-mix(in srgb,var(--accent) 65%,var(--border));background:color-mix(in srgb,var(--accent) 10%,var(--surface))}.description-sections label:has(input:focus-visible){outline:2px solid var(--accent);outline-offset:2px}.description-sections label:has(input:disabled){opacity:.55}.description-sections input{accent-color:var(--accent);flex-shrink:0}.description-help{margin:0 0 8px}.nwd-actions{flex-wrap:wrap}.nwd-actions a{font-size:.8rem}@media(width<=600px){dialog{margin-top:1rem;max-height:calc(100dvh - 2rem)}.description-sections{flex-basis:100%;justify-content:flex-start}}a{color:var(--accent)}.created-issue{padding:16px;border:1px solid var(--border);border-radius:var(--ui-radius, 8px)}.issue-heading{display:flex;align-items:center;gap:8px;margin-bottom:12px}.issue-status{padding:3px 10px;border-radius:20px;background:#238636;color:#fff;font-size:.8rem}.issue-status.closed{background:#8250df}.issue-status.not_planned,.issue-status.duplicate,.issue-status.unknown{background:#656d76}.issue-number{color:var(--muted)}h4{font-size:1.15rem;line-height:1.4;overflow-wrap:anywhere}.issue-description{margin-top:16px;white-space:pre-wrap;overflow-wrap:anywhere;max-height:40vh;overflow:auto;line-height:1.5;font-size:.9rem}.issue-labels{display:flex;flex-wrap:wrap;gap:6px;margin-top:12px}.issue-labels span{border:1px solid var(--border);border-radius:12px;padding:2px 8px;font-size:.75rem}.para+.para,.md-list{margin-top:.75em}.md-list{padding-left:1.5em}.md-list li.md-task{list-style:none;position:relative}.md-task>input{position:absolute;left:-1.4em;top:.2em;margin:0}.md-heading{font-weight:600;margin-top:.75em}.md-rule{border:0;border-top:1px solid var(--border)}.inline-code,.code-block{font-family:monospace;background:var(--bg, var(--surface));border-radius:4px}.inline-code{padding:1px 4px}.code-block{white-space:pre;overflow:auto;padding:10px;margin:10px 0}
|
|
32
|
+
`,W={"github-unavailable":`GitHub could not be reached. Try checking again.`,"github-issue-forbidden":`GitHub did not permit issue creation. Check your repository access and authorization.`,"github-create-uncertain":`GitHub may have created this issue. Check the repository before trying again.`,"github-create-failed":`GitHub could not create the issue. Check the title and description before trying again.`,unauthenticated:`Your session expired. Sign in again before creating an issue.`,"workspace-changed":`Your workspace access changed. Reopen the finding before creating an issue.`,"bad-issue-repository":`The finding’s repository changed. Reopen the finding before creating an issue.`,"no-finding":`This finding is no longer available in this workspace.`},vr=class extends C{static styles=[...C.styles,L(_r)];static properties={teamId:{attribute:!1},context:{attribute:!1},session:{attribute:!1},formUrl:{attribute:!1},title:{state:!0},body:{state:!0},prepared:{state:!0},busy:{state:!0},creating:{state:!0},message:{state:!0},uncertain:{state:!0},createdUrl:{state:!0},createdIssue:{state:!0},detailsUnavailable:{state:!0},sections:{state:!0}};constructor(){super(),this.title=``,this.body=``,this.prepared=null,this.busy=!1,this.creating=!1,this.message=``,this.uncertain=!1,this.createdUrl=``,this.createdIssue=null,this.detailsUnavailable=!1,this.sections=[]}beforeOpen(){this.finding&&Object.assign(this,mr(this.finding,this.draftOptions)),this.check(!0)}editBody(e){Object.assign(this,hr(this,e))}toggleSection(e,t){Object.assign(this,gr(this,e,t))}current(){return!this._settled&&this.isCurrent()}fallbackUrl(){let e=new URL(this.formUrl);return e.searchParams.set(`title`,this.title),e.searchParams.set(`body`,this.body),this.prepared?.labels&&e.searchParams.set(`labels`,this.prepared.labels.join(`,`)),e.href}async check(e=!1){if(!this.busy&&this.current()){this.busy=!0,this.message=``;try{let t=await dr(this.teamId,this.context,this.session.csrfToken);if(!this.current())return;this.prepared=t,this.uncertain=!1,t.mode===`existing`&&(this.onCreated?.(t.url),window.location.assign(t.url)),e&&t.mode===`form`&&window.location.assign(this.fallbackUrl())}catch(e){!this.uncertain&&![`pending`,`created-unavailable`].includes(this.prepared?.mode)&&(this.prepared=null),this.message=W[e.message]??`Unable to check this issue. Please retry.`}finally{this.busy=!1}}}async create(){if(!(this.busy||this.uncertain||this.createdUrl||this.prepared?.mode!==`api`)){if(!this.current()){this.message=W[`workspace-changed`],this.prepared=null;return}this.busy=!0,this.creating=!0,this.message=``;try{let e=await dr(this.teamId,this.context,this.session.csrfToken,{title:this.title,body:this.body});if(!this.current())return;e.mode===`existing`?(this.onCreated?.(e.url),window.location.assign(e.url)):e.url?(this.createdUrl=e.url,this.createdIssue=e.issue,this.detailsUnavailable=e.detailsUnavailable===!0,this.onCreated?.(e.url)):this.prepared=e}catch(e){e.message===`github-authorization-required`?this.prepared={mode:`authorize`,authorizationPath:`/api/oauth/github/issues/login`}:(this.uncertain=e.message===`github-create-uncertain`,[`workspace-changed`,`bad-issue-repository`,`no-team`,`no-report`,`no-finding`,`unauthenticated`,`forbidden`].includes(e.message)&&(this.prepared=null),this.message=W[e.message]??`Could not create this issue. Check the title, description, and repository access.`)}finally{this.busy=!1,this.creating=!1}}}render(){let e=this.createdIssue,t=this.prepared,n=t?.mode===`created-unavailable`,r=this.createdUrl||n,i=this.uncertain||t?.mode===`pending`,a=this.creating||i,o=!this.busy&&!i&&t?.mode===`form`;return R`<dialog aria-labelledby="issue-dialog-title" @close="${this._onClose}" @cancel="${e=>{this.creating&&e.preventDefault()}}"><header><h3 id="issue-dialog-title">${r?`Issue created`:`Create a GitHub issue`}</h3></header><div class="dialog-content"><p class="nwd-intro">${r?`Created`:`Create`} in <strong>${this.context?.repository}</strong> as <strong>@${this.session?.login}</strong>.</p>${this.createdUrl?R`<section class="created-issue" aria-label="Created GitHub issue"><div class="issue-heading"><span class="${`issue-status ${e?.status??`open`} ${e?.stateReason??``}`}">${e?.status===`closed`?e.stateReason===`not_planned`?`Not planned`:e.stateReason===`duplicate`?`Duplicate`:e.stateReason===`completed`?`Completed`:`Closed`:`Open`}</span> <span class="issue-number">${e?.number?`#${e.number}`:``}</span></div><h4><a href="${this.createdUrl}" target="_blank" rel="noopener">${e?.title??this.title}</a></h4><p class="nwd-note">Opened by @${e?.author??this.session?.login}</p>${e?.labels?.length?R`<div class="issue-labels">${e.labels.map(e=>R`<span>${e}</span>`)}</div>`:z}<div class="issue-description">${this.renderBody?this.renderBody(e?.description??this.body):e?.description??this.body}</div><p class="nwd-note ui-hint">Linked permanently to this finding. The Issue button will open this issue next time.</p>${this.detailsUnavailable?R`<p class="nwd-note">GitHub’s latest details could not be loaded. The issue was created and its link is saved.</p>`:z}</section>`:n?R`<p role="status">The issue was created and linked permanently to this finding. Its details cannot be shown because workspace access could not be confirmed. Check its status again once workspace access is available.</p>`:R`<label>Title<input class="nwd-input" maxlength="256" .value="${this.title}" ?disabled="${a}" @input="${e=>{this.title=e.target.value}}"></label><div class="description-heading"><label for="issue-description">Description</label> ${this.sections.length>0?R`<div class="description-sections" role="group" aria-label="Include in description">${this.sections.map(e=>R`<label><input type="checkbox" .checked="${e.selected}" ?disabled="${a}" @change="${t=>this.toggleSection(e.id,t.target.checked)}">${e.label}</label>`)}</div>`:z}</div><p id="description-help" class="nwd-note description-help ui-hint">${this.sections.length>0?`Add or remove sections above. Your edits are kept when toggling them.`:`Edit the description before creating the issue.`}</p><textarea id="issue-description" class="nwd-input" maxlength="65536" .value="${this.body}" aria-describedby="description-help" ?disabled="${a}" @input="${e=>this.editBody(e.target.value)}"></textarea> ${t?.labels?.length?R`<p class="nwd-note">Labels: ${t.labels.join(`, `)}</p>`:z} ${t?.mode===`permissions`?R`<p>Ask a repository owner to approve the app’s Issues read and write permission. ${t.authorizationPath?R`<a href="${t.authorizationPath}" target="_blank" rel="noopener">Review repository permissions</a>`:z}</p>`:z} ${t?.mode===`authorize`?R`<p><a href="${t.authorizationPath}" target="_blank" rel="noopener">Authorize GitHub</a>, then check authorization here.</p>`:z} ${t?.mode===`pending`?R`<p role="status">An issue is being created, or its creation could not be confirmed. Check its status before continuing; another issue will not be created.</p>`:z} ${t?.mode===`unavailable`?R`<p role="status">This finding already has a managed issue in a repository outside this workspace.</p>`:z}`} ${!this.createdUrl&&this.message?R`<p class="nwd-note" role="status">${this.message}</p>`:z} ${!this.createdUrl&&this.busy?R`<p role="status">${this.creating?`Creating issue…`:`Checking repository access…`}</p>`:z}</div><footer class="nwd-actions">${this.createdUrl?R`<a href="${this.createdUrl}" target="_blank" rel="noopener">Open issue on GitHub</a>`:!this.busy&&i?R`<a href="${t?.repositoryUrl??`https://github.com/${this.context?.repository}/issues`}" target="_blank" rel="noopener">Check repository issues</a>`:o?R`<a href="${this.fallbackUrl()}" target="_blank" rel="noopener">Use GitHub form</a>`:z} <span class="nwd-spacer"></span> ${!this.createdUrl&&t?.mode!==`unavailable`?t?.mode===`api`&&!this.uncertain?R`<button class="primary" ?disabled="${this.busy||!this.title.trim()}" @click="${this.create}">Create issue</button>`:R`<button ?disabled="${this.busy}" @click="${()=>this.check()}">${i||n?`Check issue status`:`Check authorization`}</button>`:z} <button ?disabled="${this.creating}" @click="${()=>this._finish(null)}">${r?`Close`:`Cancel`}</button></footer></dialog>`}};customElements.define(`managed-issue-dialog`,vr);function yr(e){return w(`managed-issue-dialog`,e)}var br=`.share-field{display:grid;gap:.35rem;margin-top:.8rem;font-size:.82rem}fieldset{display:grid;gap:.55rem;border:0;margin-top:1rem;font-size:.82rem}legend{margin-bottom:.55rem;font-weight:600}fieldset label{display:flex;align-items:center;gap:.5rem}input[type=checkbox]{accent-color:var(--accent)}.nwd-actions{flex-wrap:wrap}.revoke-all{margin-top:.75rem;border:0;background:none;color:var(--muted);font:inherit;font-size:.74rem}.revoke-all:hover:not(:disabled){color:var(--critical)}
|
|
33
|
+
`,xr=class extends C{static styles=[...C.styles,L(br)];static properties={team:{attribute:!1},session:{attribute:!1},initialId:{attribute:!1},selectedId:{state:!0},links:{state:!0},dependencies:{state:!0},security:{state:!0},busy:{state:!0},message:{state:!0}};constructor(){super(),this.team=null,this.initialId=``,this.selectedId=``,this.links=[],this.dependencies=!1,this.security=!1,this.busy=!0,this.message=``,this.urls=new Map}beforeOpen(){this.load(this.initialId)}select(e){let t=this.links.find(t=>t.id===e);this.selectedId=t?.id??``,this.dependencies=t?.permissions.dependencies===!0,this.security=t?.permissions.security===!0,this.message=``}async load(e=this.selectedId){this.busy=!0;try{let t=await p(this.team.id);return this._settled?void 0:(this.links=t,this.select(e),!0)}catch(e){return this.message=e.message,!1}finally{this.busy=!1}}async change(e=!1,t=!1){if(!this.busy){this.busy=!0,this.message=``;try{let n=await u(this.team.id,this.session?.csrfToken,{id:t?void 0:this.selectedId||void 0,revoke:e,dependencies:this.dependencies,security:this.security});if(f.invalidate([`links`,`history`]),this._settled)return;n.path&&(this.urls.set(n.id,new URL(n.path,location.origin).href),this.links=[{id:n.id,createdAt:Date.now(),createdBy:this.session?.login??``,permissions:{dependencies:this.dependencies,security:this.security}},...this.links],this.selectedId=n.id),e&&(t?this.urls.clear():this.urls.delete(this.selectedId)),await this.load(e?``:n.id??this.selectedId)&&(this.message=e?t?`All public links revoked.`:`Link revoked.`:n.path?`Link created.`:`Permissions saved. The link URL is unchanged.`)}catch(e){this.message=e.message}finally{this.busy=!1}}}async copy(){try{await navigator.clipboard.writeText(this.urls.get(this.selectedId)),this.message=`Link copied.`}catch{this.message=`Select the link and copy it.`}}render(){let e=this.urls.get(this.selectedId);return R`<dialog @close="${this._onClose}"><header><h3>Share ${this.team?.name}</h3></header><p class="nwd-intro ui-hint">Anyone with this link can read this workspace’s published reports, comments, triage, and available source files without signing in.<br>New published reports will also be included.</p><label class="share-field">Public links <select class="nwd-input" ?disabled="${this.busy}" @change="${e=>this.select(e.target.value)}"><option value="" ?selected="${!this.selectedId}">Create a new link</option>${this.links.map(e=>R`<option value="${e.id}" ?selected="${this.selectedId===e.id}">${new Date(e.createdAt).toLocaleString()} · ${e.createdBy} · ${e.id.slice(0,8)}</option>`)}</select></label><fieldset ?disabled="${this.busy}"><legend>Include in this link</legend><label><input type="checkbox" .checked="${this.security}" @change="${e=>{this.security=e.target.checked}}"> Security findings and advisories</label> <label><input type="checkbox" .checked="${this.dependencies}" @change="${e=>{this.dependencies=e.target.checked}}"> Findings in dependencies</label></fieldset><p class="nwd-note ui-hint">Both options are off for new links.<br>Existing links keep their URL when permissions change.</p>${e?R`<label class="share-field">Public link<input class="nwd-input" readonly="readonly" .value="${e}" @focus="${e=>e.target.select()}"></label>`:z} ${this.message?R`<p class="nwd-note" role="status">${this.message}</p>`:z}<footer class="nwd-actions">${this.selectedId?R`<button class="danger" ?disabled="${this.busy}" @click="${()=>this.change(!0)}">Revoke link</button>`:z} <span class="nwd-spacer"></span> ${e?R`<button @click="${()=>this.copy()}">Copy link</button>`:z} <button class="primary" ?disabled="${this.busy}" @click="${()=>this.change()}">${this.selectedId?`Save changes`:`Create public link`}</button> <button @click="${()=>this._finish(null)}">Close</button></footer>${this.links.length>0?R`<button class="revoke-all" ?disabled="${this.busy}" @click="${()=>this.change(!0,!0)}">Revoke all links for this workspace</button>`:z}</dialog>`}};customElements.define(`managed-share-dialog`,xr);function Sr(e,t=``,n=null){return w(`managed-share-dialog`,{team:e,initialId:t,session:n})}var Cr=class extends C{static properties={_value:{state:!0}};constructor(){super(),this._value=``}_onInput=e=>{this._value=e.target.value};_onCreate=()=>{let e=(this._value??``).trim();e&&this._finish(e)};_onCancel=()=>this._finish(null);_onKeydown=e=>{e.key===`Enter`&&(e.preventDefault(),this._onCreate())};render(){let e=(this._value??``).trim().length>0;return R`<dialog @close="${this._onClose}"><header><h3>New workspace</h3></header><input type="text" class="nwd-input" placeholder="Workspace name" maxlength="200" .value="${this._value}" @input="${this._onInput}" @keydown="${this._onKeydown}"><p class="nwd-note ui-hint">Workspace triage is synchronized by default over an end-to-end encrypted channel. Triage on unattached reports stays local to this browser.</p><footer class="nwd-actions"><span class="nwd-spacer"></span> <button type="button" @click="${this._onCancel}">Cancel</button> <button type="button" class="primary" ?disabled="${!e}" @click="${this._onCreate}">Create</button></footer></dialog>`}};customElements.define(`new-workspace-dialog`,Cr);function wr(){return w(`new-workspace-dialog`)}var Tr=`.bundle-advisories-rows{list-style:none;margin:0;padding:0;min-width:0;display:flex;flex-direction:column}.bundle-advisory-row{display:flex;align-items:flex-start;gap:.65rem;padding:.25rem 0;line-height:1.35}.bundle-advisory-row+.bundle-advisory-row{border-top:1px solid color-mix(in oklch,var(--border) 65%,transparent)}.bundle-advisory-rail{flex-shrink:0;display:flex;flex-direction:column;align-items:center;gap:.125rem;min-width:4.5rem;padding-top:.1rem}.bundle-advisory-severity{text-transform:uppercase;letter-spacing:.04em;font-size:.6rem;font-weight:700;padding:.15rem .4rem;border-radius:3px;color:#000;&.sev-critical{background:var(--critical);color:#fff}&.sev-high{background:var(--high)}&.sev-moderate{background:var(--medium)}&.sev-low{background:var(--low);color:var(--badge-low-text)}&.sev-info{background:var(--info);color:#fff}}.bundle-advisory-cvss-score{color:var(--muted);font-size:.62rem;letter-spacing:.02em}.bundle-advisory-body{flex:1;min-width:0;display:flex;flex-direction:column;gap:.125rem}.bundle-advisory-header{display:flex;align-items:baseline;gap:.5rem}.bundle-advisory-title{flex:1;min-width:0;color:var(--text);font-size:.82rem;font-weight:500;overflow-wrap:anywhere}button.bundle-advisory-title{border:0;background:transparent;padding:0;font-family:inherit;line-height:inherit;text-align:left}button.bundle-advisory-title:hover{color:var(--accent)}button.bundle-advisory-title:focus-visible{outline:2px solid var(--accent);outline-offset:3px}.bundle-advisory-reference{display:inline-flex;align-items:center;gap:.4rem;flex-shrink:0}.bundle-advisory-source{display:inline-flex;align-items:center;justify-content:center;width:14px;height:14px;color:var(--muted);user-select:none}.bundle-advisory-source>svg{width:14px;height:14px}.bundle-advisory-subrow{display:flex;align-items:baseline;gap:.75rem}.bundle-advisory-meta{flex:1;min-width:0;color:var(--muted);font-size:.7rem;display:flex;flex-wrap:wrap;gap:.65rem;align-items:baseline}.bundle-advisory-meta .mono{font-size:.68rem}.bundle-advisory-cwes{margin-left:1.25rem}.bundle-advisory-ghsa{flex-shrink:0;display:inline-flex;align-items:center;gap:.2rem;color:var(--muted);text-decoration:none;font-size:.7rem;font-family:var(--font-mono, ui-monospace, monospace);cursor:default;user-select:text}a.bundle-advisory-ghsa:hover{color:var(--accent, #60a5fa)}.bundle-advisory-ghsa .external-link-icon{flex-shrink:0;opacity:.75;user-select:none}a.bundle-advisory-ghsa:hover .external-link-icon{opacity:1}.bundle-advisory-cwe{color:var(--muted);text-decoration:none;user-select:text;cursor:default}.bundle-advisory-cwe:hover{color:var(--accent, #60a5fa)}.bundle-advisory-cvss-vector{flex-shrink:1;min-width:0;text-align:right;color:var(--muted);font-size:.65rem;font-family:var(--font-mono, ui-monospace, monospace);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
34
|
+
`,Er=`dialog{width:min(94vw,56rem)}.compare-advisories{min-height:0;padding:.75rem 1rem 1rem;overflow:auto;overscroll-behavior:contain}.compare-advisories:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}.compare-advisory-group+.compare-advisory-group{margin-top:.9rem}.compare-advisory-group h4{display:flex;align-items:baseline;gap:.4rem;margin:0 0 .25rem;padding:0 .55rem;color:var(--muted);font-size:.68rem;font-weight:600;letter-spacing:.04em;text-transform:uppercase}.compare-advisory-count{font-variant-numeric:tabular-nums}.compare-advisory-group>.bundle-advisories-rows{padding:.1rem .55rem;border:1px solid transparent;border-radius:var(--ui-radius, 4px)}.compare-advisory-group .bundle-advisory-rail{width:5rem;align-items:stretch;text-align:center}.compare-advisory-group.is-new{& h4{color:var(--critical)}>.bundle-advisories-rows{background:color-mix(in srgb,var(--critical) 6%,var(--bg));border-color:color-mix(in srgb,var(--critical) 55%,var(--border))}}.compare-advisory-group.is-fixed{.bundle-advisory-title{color:var(--muted);text-decoration:line-through}.bundle-advisory-severity{opacity:.55}}
|
|
35
|
+
`,Dr=class extends C{static styles=[...C.styles,L(Tr),L(tn),L(Er)];static properties={heading:{attribute:!1},groups:{attribute:!1},_current:{state:!0}};connectedCallback(){super.connectedCallback(),this._dispose=S(()=>{this._current=this.isCurrent(),this._current||queueMicrotask(this._onClose)})}disconnectedCallback(){this._dispose?.(),super.disconnectedCallback()}render(){return R`<dialog aria-labelledby="compare-advisories-title" @close="${this._onClose}"><header><h3 id="compare-advisories-title"><span>${this.heading}</span></h3><button type="button" aria-label="Close advisories" @click="${this._onClose}">×</button></header><div class="compare-advisories" tabindex="0">${this._current?this.groups.map(e=>R`<section class="${`compare-advisory-group is-${e.status}`}"><h4>${e.heading} <span class="compare-advisory-count">${e.rows.length}</span></h4><ul class="bundle-advisories-rows">${e.rows}</ul></section>`):z}</div></dialog>`}};customElements.define(`npm-compare-advisories-dialog`,Dr);function Or(e){return w(`npm-compare-advisories-dialog`,e)}var kr=class extends C{static styles=[...C.styles,L(B)];static properties={workspaceId:{type:String},items:{type:Array},_running:{state:!0},_errors:{state:!0},_done:{state:!0},_settled:{state:!0}};constructor(){super(),this.workspaceId=``,this.items=[],this._running=!1,this._errors=[],this._done=!1,this._settled=!1}focusInitial(){this.renderRoot.querySelector(`button[data-role="download"]`)?.focus()}_onClose=()=>this._finish({downloaded:[],failed:[]});_onCancel=()=>this._finish({downloaded:[],failed:[]});_fail(e,t,n,r){e.push({kind:n,identifier:t.identifier,reason:r}),this._errors=[...this._errors,{label:O(t),reason:r}]}_onDownload=async()=>{if(this._running)return;this._running=!0,this._errors=[];let e=[],t=[];for(let n of this.items)try{if(n.kind===`bundle`){let r=await oe(this.workspaceId,n.identifier);if(!r.ok){this._fail(t,n,`bundle`,r.reason??`unknown`);continue}await Oe(n.identifier,this.workspaceId),e.push({kind:`bundle`,identifier:n.identifier});continue}let r=await re(this.workspaceId,n.identifier);if(!r||!r.ok){this._fail(t,n,`report`,r?.reason??`sync is not available`);continue}e.push({kind:`report`,identifier:n.identifier})}catch(e){this._fail(t,n,n.kind,e?.message??String(e))}if(this._running=!1,this._done=!0,e.some(e=>e.kind===`report`))try{A.currentWorkspace===this.workspaceId&&await gt(this.workspaceId)}catch{}t.length===0&&this._finish({downloaded:e,failed:t})};render(){let{count:e,singular:t,kindLabel:n}=_t(this.items),r=t?R`Download <strong>"${O(this.items[0])}"</strong> from the workspace's remote inventory?`:R`Download <strong>${e}</strong> remote ${n} into this workspace?`,i=t?z:vt(this.items),a=this._running?t?`Downloading…`:`Downloading ${e} ${n}\u2026`:t?`Download`:`Download ${e}`;return R`<dialog @close="${this._onClose}"><header><h3>Download from remote</h3></header><p class="lwd-body">${r}</p>${i} ${yt(this._errors)}<footer class="nwd-actions"><span class="nwd-spacer"></span> <button type="button" data-role="cancel" @click="${this._onCancel}" ?disabled="${this._running}">${this._done&&this._errors.length>0?`Close`:`Cancel`}</button> ${this._done&&this._errors.length>0?z:R`<button type="button" data-role="download" @click="${this._onDownload}" ?disabled="${this._running||e===0}">${a}</button>`}</footer></dialog>`}};customElements.define(`sync-download-dialog`,kr);function Ar({workspaceId:e,items:t}={}){return w(`sync-download-dialog`,{workspaceId:e??``,items:Array.isArray(t)?[...t]:[]})}var jr=`.rec-close{position:absolute;top:6px;right:6px;z-index:1}header{padding-right:1.5rem}.rec-list{margin:.2rem 0 .55rem;padding:0;list-style:none;max-height:320px;overflow-y:auto}.rec-row{display:flex;align-items:center;gap:.5rem;padding:.28rem .15rem;font-size:.8rem;line-height:1.4;border-bottom:1px solid rgb(from var(--text) r g b / .06)}.rec-row:last-child{border-bottom:none}.rec-label{flex:1 1 auto;min-width:0;color:var(--text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.rec-status{flex:0 0 auto;padding:0 .4rem;border-radius:4px;font-size:.68rem;font-weight:600;text-transform:uppercase;letter-spacing:.04em}.rec-status-checking{color:var(--muted)}.rec-status-good{color:var(--ok, #16a34a)}.rec-status-uploaded,.rec-status-reuploaded{color:var(--accent, #2563eb)}.rec-status-updated{color:var(--high, #d57422)}.rec-status-differs{color:var(--warning, #d97706)}.rec-choice{flex:0 0 auto;display:flex;gap:.3rem;& button{background:transparent;border:1px solid var(--border);color:var(--text);border-radius:var(--ui-radius, 4px);padding:.1rem .45rem;font:inherit;font-size:.72rem;cursor:default;&:hover:not(:disabled){border-color:var(--accent);color:var(--accent)}&:disabled{opacity:.5;cursor:default}}}.rec-status-failed,.rec-status-check-failed{color:var(--warning, #d97706)}.rec-status-differs,.rec-status-failed,.rec-status-check-failed{background:rgb(from var(--warning, #d97706) r g b / .14)}.rec-status-missing{color:var(--critical, #dc2626)}.rec-summary{margin:.1rem 0 .2rem;color:var(--muted);font-size:.76rem;line-height:1.45}.rec-error{margin:.3rem 0 .2rem;color:var(--critical, #f87171);font-size:.76rem;line-height:1.4}
|
|
36
|
+
`,Mr={checking:{label:`checking…`,hint:`Checking the cloud copy…`},good:{label:`available`,hint:`The cloud copy is intact.`},updated:{label:`local updated`,hint:`Your copy was out of date. It was replaced with the cloud copy.`},uploaded:{label:`cloud updated`,hint:`Your copy was newer. It was uploaded to the cloud.`},differs:{label:`differs`,hint:`Your copy differs from the cloud copy, and nothing shows which one is newer. Choose which to keep.`},reuploaded:{label:`re-uploaded`,hint:`The cloud copy's data was missing. It was restored from your copy.`},failed:{label:`failed`,hint:`Couldn't bring this back in line. Re-check again to retry.`},"check-failed":{label:`check failed`,hint:`Couldn't download the cloud copy to check it, so its state is unknown. Re-check again to retry.`},missing:{label:`missing`,hint:`The cloud copy's data is gone, and there's no matching copy here to restore it from.`}};function Nr(e){let t={good:0,updated:0,uploaded:0,differs:0,reuploaded:0,failed:0,"check-failed":0,missing:0};for(let n of e)n.status in t&&(t[n.status]+=1);return t}var Pr=e=>e===1?`copy`:`copies`,Fr=class extends C{static styles=[...C.styles,L(j),L(B),L(jr)];static properties={workspaceId:{type:String},cloudCount:{type:Number},localFileNames:{type:Array},localBundles:{type:Array},autoRun:{type:Boolean},_rows:{state:!0},_running:{state:!0},_ran:{state:!0},_error:{state:!0},_settled:{state:!0},_resolving:{state:!0},_bulk:{state:!0}};constructor(){super(),this.workspaceId=``,this.cloudCount=0,this.localFileNames=[],this.localBundles=[],this.autoRun=!1,this._rows=[],this._running=!1,this._ran=!1,this._error=null,this._settled=!1,this._resolving=new Set,this._bulk=!1}focusInitial(){this.renderRoot.querySelector(`button[data-role="recheck"]`)?.focus(),this.autoRun&&this._onRecheck()}_result(){return{items:this._rows,counts:this._counts()}}_onClose=()=>this._finish(this._ran?this._result():null);_onCancel=()=>this._finish(this._ran?this._result():null);_counts(){return Nr(this._rows)}_onResolve=async(e,t)=>{if(this._resolving.has(e.resourceTag))return;this._resolving=new Set([...this._resolving,e.resourceTag]);let n;try{let r=await he(this.workspaceId,e.identifier,t,e.compared);n=r?{status:r.status,detail:r.detail}:{status:`failed`,detail:`sync is not available`}}catch(e){n={status:`failed`,detail:e?.message??String(e)}}this._rows=this._rows.map(t=>t.resourceTag===e.resourceTag?{...t,...n}:t);let r=new Set(this._resolving);r.delete(e.resourceTag),this._resolving=r};_onUseCloudForAll=async()=>{if(!(this._bulk||this._running)){this._bulk=!0;try{for(let e of this._differingRows())await this._onResolve(e,`cloud`)}finally{this._bulk=!1}}};_differingRows(){return this._rows.filter(e=>e.status===`differs`)}_onRecheck=async()=>{if(!(this._running||this._bulk||this._resolving.size>0)){this._running=!0,this._error=null,this._rows=[];try{let{items:e}=await pe(this.workspaceId,{onList:e=>{this._rows=e},onItem:e=>{this._rows=this._rows.map(t=>t.resourceTag===e.resourceTag?e:t)}});this._rows=e}catch(e){this._error=e?.message??String(e)}finally{this._running=!1,this._ran=!0}}};_downloadableItems(){let e=new Set(this.localFileNames),t=new Set(this.localBundles),n=[];for(let r of this._rows)r.status!==`good`||!r.identifier||(r.kind===`report`&&!e.has(r.identifier)?n.push({kind:`report`,identifier:r.identifier}):r.kind===`bundle`&&!t.has(r.identifier)&&n.push({kind:`bundle`,identifier:r.identifier,label:r.label}));return n}_onDownload=async()=>{let e=this._downloadableItems();e.length!==0&&await Ar({workspaceId:this.workspaceId,items:e})};_statusHint(e){let t=Mr[e.status]?.hint??``;return e.status===`good`&&this._heldLocally(e)&&(t=`The cloud copy is intact and matches your copy.`),e.detail?`${t}
|
|
37
|
+
${e.detail}`:t}_heldLocally(e){return e.identifier?e.kind===`bundle`?this.localBundles.includes(e.identifier):this.localFileNames.includes(e.identifier):!1}_choice(e){if(e.status!==`differs`)return z;let t=this._running||this._bulk||this._resolving.has(e.resourceTag);return R`<span class="rec-choice"><button type="button" data-role="keep-cloud" data-tooltip="Replace your copy with the cloud copy" @click="${()=>this._onResolve(e,`cloud`)}" ?disabled="${t}">Use cloud copy</button> <button type="button" data-role="keep-local" data-tooltip="Upload your copy, replacing the cloud copy" @click="${()=>this._onResolve(e,`local`)}" ?disabled="${t}">Upload mine</button></span>`}_rowsSection(){return this._rows.length===0?this._running?R`<p class="lwd-body">Fetching remote listing…</p>`:z:R`<ul class="rec-list">${this._rows.map(e=>R`<li class="rec-row"><span class="rec-label">${e.label}${e.kind===`bundle`?R`<span class="lwd-kind-tag">bundle</span>`:z}</span> ${this._choice(e)} <span class="${`rec-status rec-status-${e.status}`}" data-tooltip="${this._statusHint(e)}">${Mr[e.status]?.label??e.status}</span></li>`)}</ul>`}_summarySection(){if(!this._ran||this._rows.length===0)return z;let e=this._counts(),t=[];return e.good&&t.push(`${e.good} available`),e.updated&&t.push(`${e.updated} local ${Pr(e.updated)} updated from the cloud`),e.uploaded&&t.push(`${e.uploaded} cloud ${Pr(e.uploaded)} updated from yours`),e.differs&&t.push(`${e.differs} ${e.differs===1?`differs`:`differ`} \u2014 choose which copy to keep`),e.reuploaded&&t.push(`${e.reuploaded} re-uploaded`),e.failed&&t.push(`${e.failed} failed`),e[`check-failed`]&&t.push(`${e[`check-failed`]} check failed`),e.missing&&t.push(`${e.missing} missing`),t.length>0?R`<p class="rec-summary" role="status" aria-live="polite">${t.join(` · `)}</p>`:z}render(){let e=this.cloudCount===1?``:`s`,t=this.cloudCount>0?R`the <strong>${this.cloudCount}</strong> remote`:`remote`,n=this._ran?z:R`<p class="lwd-body">Re-check ${t} object${e} for this workspace. Each is re-fetched from the relay and compared with your copy. When one copy is provably newer, the other is updated to match; when it isn't clear, you choose which to keep. Any whose bytes are missing are re-uploaded from a matching local copy.</p>`,r=this._ran&&this._rows.length===0&&!this._error?R`<p class="lwd-empty">No remote objects to check.</p>`:z,i=this._ran?this._downloadableItems().length:0,a=this._differingRows().length>1||this._bulk;return R`<dialog @close="${this._onClose}"><button type="button" class="detail-action rec-close" data-role="close" aria-label="Close" @click="${this._onCancel}">×</button><header><h3>Re-check cloud storage</h3></header>${n} ${this._rowsSection()} ${r} ${this._summarySection()} ${this._error?R`<p class="rec-error" role="alert">${this._error}</p>`:z}<footer class="nwd-actions"><span class="nwd-spacer"></span> ${a?R`<button type="button" data-role="use-cloud-all" data-tooltip="Replace your copy of every report that differs with its cloud copy" @click="${this._onUseCloudForAll}" ?disabled="${this._running||this._bulk}">${this._bulk?`Using cloud copies…`:`Use cloud for all ${this._differingRows().length} conflicts`}</button>`:z} ${!a&&i>0?R`<button type="button" data-role="download" @click="${this._onDownload}" ?disabled="${this._running}">Download ${i} not stored locally</button>`:z} ${a?z:R`<button type="button" data-role="cancel" @click="${this._onCancel}" ?disabled="${this._running}">${this._ran?`Close`:`Cancel`}</button>`} <button type="button" data-role="recheck" @click="${this._onRecheck}" ?disabled="${this._running||this._bulk||this._resolving.size>0}">${this._running?`Re-checking…`:this._ran?`Re-check again`:`Re-check`}</button></footer></dialog>`}};customElements.define(`objstore-recovery-dialog`,Fr);function Ir({workspaceId:e,cloudCount:t,localFileNames:n,localBundles:r,autoRun:i}={}){return w(`objstore-recovery-dialog`,{workspaceId:e??``,cloudCount:typeof t==`number`?t:0,localFileNames:Array.isArray(n)?[...n]:[],localBundles:Array.isArray(r)?[...r]:[],autoRun:i===!0})}var Lr=class extends C{static styles=[...C.styles,L(x)];static properties={_userName:{state:!0},_busy:{state:!0},_error:{state:!0},_phase:{state:!0},_success:{state:!0},_orphan:{state:!0},_orphanAck:{state:!0},_canCancel:{state:!0}};constructor(){super(),this._userName=``,this._busy=!1,this._error=``,this._phase=``,this._success=!1,this._orphan=!1,this._orphanAck=!1,this._canCancel=!0,this._abortController=null}updated(e){if(e.has(`_success`)&&this._success){let e=this.renderRoot.querySelector(`footer button.primary`);e&&e.focus()}}beforeOpen(){typeof location<`u`&&(this._userName=location.hostname||`DeepView`),this._orphan=at(),this._vaultStateUnsub=st(()=>{if(this._settled)return;let e=at();this._orphan&&!e&&Je()?(this._error=`Encryption was just enabled in another tab. Close this dialog and reload to unlock.`,this._busy=!1,this._canCancel=!0):this._orphan!==e&&(this._orphan=e)})}async focusInitial(){await this.updateComplete;let e=this._orphan?`input[data-role="orphan-ack"]`:`input[data-role="name"]`;this.renderRoot.querySelector(e)?.focus()}disconnectedCallback(){if(super.disconnectedCallback(),this._vaultStateUnsub){try{this._vaultStateUnsub()}catch{}this._vaultStateUnsub=null}}_finish(e){if(!this._settled){if(this._abortController)try{this._abortController.abort()}catch{}super._finish(!!e)}}_onClose=()=>this._finish(this._success);_onCancel=()=>{this._busy&&!this._canCancel||this._finish(!1)};_onDialogCancel=e=>{if(this._busy&&!this._canCancel){e.preventDefault();return}this._finish(!1)};_onNameInput=e=>{this._userName=e.target.value};_onOrphanAckInput=e=>{this._orphanAck=e.target.checked};_onWipeOrphan=async()=>{if(!this._busy&&this._orphanAck){if(Je()){this._error=`Encryption was just enabled in another tab. Reload this page to unlock.`;return}this._busy=!0,this._canCancel=!1,this._error=``,this._phase=`Clearing previous vault state…`;try{await ft({refuseIfEnabled:!0}),location.reload()}catch(e){this._error=`Could not clear previous vault state: ${e?.message??e}`,this._phase=``}finally{this._busy=!1,this._canCancel=!0}}};_onEnable=async()=>{if(!this._busy){if(!nt()){this._error=`Passkeys are not supported in this browser.`;return}this._busy=!0,this._canCancel=!0,this._error=``,this._phase=`Registering passkey…`,this._abortController=new AbortController;try{await lt({userName:this._userName.trim()||`DeepView user`,rpName:`DeepView`,signal:this._abortController.signal,migrate:async({seal:e})=>{this._canCancel=!1,this._phase=`Encrypting workspaces and metadata…`,await mt({seal:e}),this._phase=`Encrypting triage data…`,await Pe({seal:e}),this._phase=`Encrypting report files…`,await ce({seal:e}),this._phase=`Encrypting bundles…`,await de({seal:e})}})&&(this._success=!0,this._phase=``)}catch(e){this._error=e?.name===`AbortError`?``:e?.message??String(e),this._phase=``}finally{this._busy=!1,this._canCancel=!0,this._abortController=null}}};_body(){if(this._orphan)return R`<p class="wsl-error">DeepView found an abandoned passkey setup on this device. The original passkey can no longer be accessed, so any local data still encrypted under it is <strong>unreadable</strong> and cannot be recovered.</p><p class="nwd-note ui-hint">To enable encryption, DeepView will <strong>permanently delete ALL local DeepView data on this device</strong> — including any plaintext workspaces or reports you may have added after the previous passkey was lost — and then register a fresh passkey. This is irreversible. There is no backup.</p><label class="wsl-ack"><input type="checkbox" data-role="orphan-ack" .checked="${this._orphanAck}" ?disabled="${this._busy}" @change="${this._onOrphanAckInput}"> <span>I understand all local DeepView data on this device will be deleted.</span></label> ${this._phase?R`<p class="nwd-note">${this._phase}</p>`:z} ${this._error?R`<p class="wsl-error">${this._error}</p>`:z}<footer class="nwd-actions"><span class="nwd-spacer"></span> <button type="button" ?disabled="${this._busy&&!this._canCancel}" @click="${this._onCancel}">Cancel</button> <button type="button" class="primary" ?disabled="${this._busy||!this._orphanAck}" @click="${this._onWipeOrphan}">${this._busy?`Working…`:`Wipe and continue`}</button></footer>`;if(this._success)return R`<p class="nwd-note ui-hint"><strong>Encryption is on.</strong> Your triage data and report files are now sealed under your passkey. You'll be asked to unlock with the passkey on every fresh load.</p><footer class="nwd-actions"><span class="nwd-spacer"></span> <button type="button" class="primary" @click="${()=>this._finish(!0)}">Close</button></footer>`;let e=nt();return R`<p class="nwd-note ui-hint">Passkey encryption protects your local triage and report data with a hardware-backed key (TouchID, Windows Hello, security key). Without the passkey, the data on disk is unreadable — even to scripts running on this origin.</p><p class="wsl-error"><strong>If you lose access to this passkey, your data CANNOT be recovered.</strong> DeepView has no backup key, no recovery code, and no escrow. Make sure your passkey is backed up (iCloud Keychain / Google Password Manager sync, or a second registered device) before relying on encryption for important data.</p><p class="nwd-note ui-hint"><strong>Opt-in.</strong> You can disable encryption later from the lock icon in the sidebar header — the data is rewritten as plaintext and DeepView signals your authenticator to remove the passkey (where supported).</p>${e?z:R`<p class="wsl-error">Your browser doesn't expose the WebAuthn passkey API. Try Chrome, Safari, Edge, or Firefox in a recent version on a device with a platform authenticator.</p>`} <label class="wsl-field"><span>Passkey label</span> <input type="text" class="nwd-input" data-role="name" placeholder="DeepView" maxlength="200" .value="${this._userName}" @input="${this._onNameInput}"></label> ${this._phase?R`<p class="nwd-note">${this._phase}</p>`:z} ${this._error?R`<p class="wsl-error">${this._error}</p>`:z}<footer class="nwd-actions"><span class="nwd-spacer"></span> <button type="button" ?disabled="${this._busy&&!this._canCancel}" @click="${this._onCancel}">Cancel</button> <button type="button" class="primary" ?disabled="${!e||this._busy}" @click="${this._onEnable}">${this._busy?`Working…`:`Enable encryption`}</button></footer>`}render(){return R`<dialog @close="${this._onClose}" @cancel="${this._onDialogCancel}"><header><h3>${this._success?`Encryption enabled`:this._orphan?`Previous passkey setup detected`:`Enable passkey encryption`}</h3></header>${this._body()}</dialog>`}};customElements.define(`passkey-setup-dialog`,Lr);function Rr(){return w(`passkey-setup-dialog`)}var zr=class extends C{_onConfirm=()=>this._finish(!0);render(){return R`<dialog @close="${this._onClose}"><header><h3>Changes might not be saved</h3></header><p class="nwd-intro">Triage works in this tab, but this browser <strong>can't save its sync state right now</strong> — so changes that haven't reached the server could be lost if you reload the page.</p><p class="nwd-intro">This usually means the browser's storage is full, or another tab is running a newer version of the app (to avoid overwriting that newer version's data, this tab has paused saving).</p><p class="nwd-note ui-hint">Free up space if storage is full, and close or reload other tabs of this app to the same version. This notice clears on its own once saving works again.</p><footer class="nwd-actions"><span class="nwd-spacer"></span> <button type="button" class="primary" @click="${this._onConfirm}">Got it</button></footer></dialog>`}};customElements.define(`persistence-degraded-dialog`,zr);function Br(){return new Promise(e=>{let t=document.createElement(`persistence-degraded-dialog`);t.exclusive=!0;let n=n=>{t.remove(),e({shown:n})};t.addEventListener(`resolve`,()=>n(!0)),t.addEventListener(`modal-conflict`,()=>n(!1)),document.body.append(t)})}function Vr(e){if(At(e))throw Error(`This is a links file, not a report. Wrap report rows in a groups or findings object to compare them.`);let t=e;try{let n=JSON.parse(e);Array.isArray(n)&&(t=JSON.stringify({groups:n}))}catch{}let{data:n,reason:r}=Ut(t);if(!n)throw Error(r);let i=[],a=new Map,o=0;for(let e of Ht(n)){let t=new Set;for(let n of Array.isArray(e)?e:[e])if(n&&typeof n==`object`&&!Array.isArray(n)){if(typeof n.id!=`string`||!n.id){o++;continue}t.add(n.id),a.has(n.id)||a.set(n.id,[]),a.get(n.id).push(n)}i.push([...t].toSorted())}return{rows:i,byId:a,missingIds:o}}function Hr(e){let t=new Set;for(let n of e){let e=[...new Set(n)].toSorted();for(let n=0;n<e.length;n++)for(let r=n+1;r<e.length;r++)t.add(JSON.stringify([e[n],e[r]]))}return t}function Ur(e){let t=new Map,n=e=>{let n=e;for(;t.get(n)!==n;)t.set(n,t.get(t.get(n))),n=t.get(n);return n};for(let r of e){let[e,i]=JSON.parse(r);for(let n of[e,i])t.has(n)||t.set(n,n);t.set(n(e),n(i))}let r=new Map;for(let e of[...t.keys()].toSorted()){let t=n(e);r.has(t)||r.set(t,[]),r.get(t).push(e)}return[...r.values()].toSorted((e,t)=>e[0].localeCompare(t[0]))}function G(e,t,n){let r=(e.byId.get(t)??[]).map(e=>n===`verdict`?qt(e)||null:n===`severity`?Gt(e)??null:n===`originalSeverity`?e.severity??null:n===`confidence`&&Number.isFinite(e.confidence)?e.confidence:null);return[...new Set(r)].toSorted((e,t)=>String(e).localeCompare(String(t),void 0,{numeric:!0}))}var Wr=(e,t)=>e.length===t.length&&e.every((e,n)=>e===t[n]),K=(e,t)=>[...e].filter(e=>!t.has(e)).toSorted();function Gr(e,t){let n=new Set(e.byId.keys()),r=new Set(t.byId.keys()),i=[...n].filter(e=>r.has(e)).toSorted(),a=new Set(i),o=e=>Hr(e.rows.map(e=>e.filter(e=>a.has(e)))),s=o(e),c=o(t),l={confirmed:[],refuted:[],otherVerdicts:[],confidence:[],severity:[]};for(let n of i)for(let r of[`verdict`,`confidence`,`severity`]){let i=G(e,n,r),a=G(t,n,r),o={id:n,before:i,after:a};if(r===`severity`&&(o.originalBefore=G(e,n,`originalSeverity`),o.originalAfter=G(t,n,`originalSeverity`)),!Wr(i,a)||r===`severity`&&!Wr(o.originalBefore,o.originalAfter)){if(r!==`verdict`){l[r].push(o);continue}l[a.length===1&&a[0]===`confirmed`?`confirmed`:a.length===1&&a[0]===`refuted`?`refuted`:`otherVerdicts`].push(o)}}let u=Ur(K(c,s)),d=Ur(K(s,c)),f=K(r,n),p=K(n,r);return{before:e,after:t,added:f,removed:p,shared:i,joined:u,split:d,...l,unchanged:f.length===0&&p.length===0&&u.length===0&&d.length===0&&Object.values(l).every(e=>e.length===0)}}var Kr=`dialog{width:min(94vw,1180px);max-width:1180px;height:min(88dvh,960px);max-height:92dvh;margin-block:auto;padding:0;overflow:hidden;background:var(--theme-bar)}dialog[open]{display:flex;flex-direction:column}header{display:flex;align-items:center;justify-content:space-between;gap:1rem;padding:1.1rem 1.3rem}header h3{margin-bottom:.25rem;font-size:1.1rem}header p,.muted,.note,.empty{color:var(--muted);font-size:.78rem;line-height:1.5}button,select,input{font:inherit;font-size:.8rem}button,select,.file-button,.search{color:var(--text);background:var(--surface);border:1px solid var(--border);border-radius:var(--ui-radius, 6px);padding:.45rem .65rem}button:not(:disabled):hover,.file-button:hover{background:var(--surface-active)}button:disabled{opacity:.45}button:focus-visible,select:focus-visible,.search:focus-visible,.file-button:focus-within{outline:2px solid var(--accent);outline-offset:2px}.pickers{display:grid;grid-template-columns:minmax(0,1fr) auto minmax(0,1fr);align-items:start;gap:1rem;padding:0 1.3rem 1rem;border-bottom:1px solid var(--border)}.picker{min-width:0}.picker>label{display:block;font-size:.72rem;font-weight:600;margin-bottom:.4rem;color:var(--muted)}.picker select{width:100%;text-overflow:ellipsis}.file-picker{display:flex;align-items:center;flex-wrap:wrap;gap:.6rem;margin-top:.55rem}.file-button{position:relative;display:inline-block;font-size:.74rem;padding:.3rem .5rem;overflow:hidden}.file-button input{position:absolute;inset:0;opacity:0;width:100%}.swap{align-self:start;margin-top:1.5rem;font-size:1rem}.results{overflow:auto;min-height:0;padding:1.1rem 1.3rem 1.4rem}.summary{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:.65rem}.summary>div{display:flex;flex-direction:column;gap:.35rem;padding:.8rem 1rem;background:var(--bg);border:1px solid var(--border);border-radius:var(--ui-radius, 7px)}.summary span{font-size:.72rem;color:var(--muted)}.summary strong{font-size:1.15rem;font-variant-numeric:tabular-nums}.positive h4{color:var(--green)}.negative h4,.verdict-confirmed h4,.summary .verdict-confirmed strong{color:var(--critical)}.verdict-refuted h4,.summary .verdict-refuted strong{color:var(--muted)}.change-counts{display:flex;flex-wrap:wrap;gap:.4rem;margin-top:.7rem}.change-counts span{font-size:.7rem;padding:.3rem .5rem;border-radius:5px;background:var(--bg);color:var(--muted)}.results-head{display:flex;align-items:center;flex-wrap:wrap;gap:.75rem;margin-block:1rem}.results-head p{flex:1;font-size:.78rem;color:var(--muted);overflow-wrap:anywhere}.search{width:min(100%,17rem)}.section-title{margin:1.2rem 0 .5rem;font-size:.87rem}.note{margin:-.15rem 0 .6rem}.columns{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:.8rem}.change-section{border:1px solid var(--border);border-radius:var(--ui-radius, 7px);overflow:hidden;margin-bottom:.6rem}.change-section h4{display:flex;justify-content:space-between;align-items:center;gap:.5rem;font-size:.78rem;font-weight:600;padding:.65rem .8rem;background:var(--bg)}.count{font-variant-numeric:tabular-nums;font-size:.7rem}ul{list-style:none}li{border-top:1px solid var(--border);padding:.7rem .8rem}.change-row{display:flex;justify-content:space-between;align-items:start;gap:.8rem}.finding{display:flex;flex-direction:column;gap:.2rem;min-width:0;overflow-wrap:anywhere}.finding strong{font-size:.77rem;font-weight:500;line-height:1.4}.finding code,.finding small{color:var(--muted);font-size:.66rem;line-height:1.4}.change-value{display:flex;align-items:center;justify-content:end;flex-wrap:wrap;gap:.3rem;max-width:48%;flex-shrink:0;font-size:.73rem;text-align:end;overflow-wrap:anywhere}.from{color:var(--muted)}.change-value small{width:100%;color:var(--muted);font-size:.67rem}.group-members{display:flex;flex-direction:column;gap:.55rem}.group-shape{display:flex;flex-wrap:wrap;gap:.45rem;margin-top:.7rem;font:.66rem/1.5 monospace;overflow-wrap:anywhere}.group-shape>span{min-width:0}.empty{padding:.7rem .8rem}.more{margin:.5rem .8rem .7rem;font-size:.72rem}.placeholder{padding:4rem 1rem;text-align:center;color:var(--muted);font-size:.85rem}.identical{color:var(--green);font-size:.82rem;padding-block:.5rem}.error,.warning{font-size:.75rem;line-height:1.5;margin-top:.5rem;overflow-wrap:anywhere}.error{color:var(--critical)}.warning{color:var(--yellow)}dialog>.error{padding:0 1.3rem .6rem}@media(width<=650px){.pickers{gap:.5rem;padding-inline:.8rem}.columns{grid-template-columns:minmax(0,1fr);gap:0}.summary{grid-template-columns:repeat(2,minmax(0,1fr))}.summary>div{padding:.6rem}.results-head p{flex-basis:100%}.search{width:100%}header,.results{padding-inline:.8rem}.change-row{flex-wrap:wrap}.change-value{max-width:100%}}
|
|
38
|
+
`,q=()=>({key:``,name:``,report:null,error:``,loading:!1}),J=(e,t)=>e.map(e=>e===null?`Not supplied`:t===`confidence`?`${e}/10`:String(e).replaceAll(`_`,` `)).join(` / `),Y=50,qr=class extends C{static styles=[...C.styles,L(Kr)];static properties={_names:{state:!0},_sides:{state:!0},_catalogError:{state:!0},_catalogLoading:{state:!0},_diff:{state:!0},_query:{state:!0},_limits:{state:!0}};constructor(){super(),this._names=[],this._sides=[q(),q()],this._tokens=[0,0],this._catalogError=``,this._catalogLoading=!0,this._diff=null,this._query=``,this._limits={}}beforeOpen(){this._loadCatalog()}focusInitial(){this.renderRoot.querySelector(`select`)?.focus()}async _loadCatalog(){try{let e=await wt();if(this._settled)return;this._names=e.filter(e=>ve(e)!==kt);let t=this._names.includes(A.currentFile)?A.currentFile:this._names[0];t&&!this._tokens[0]&&this._select(0,t);let n=this._names.find(e=>e!==t);n&&!this._tokens[1]&&this._select(1,n)}catch(e){this._settled||(this._catalogError=`Couldn't list saved reports: ${e.message}`)}finally{this._settled||(this._catalogLoading=!1)}}_setSide(e,t){this._sides=this._sides.map((n,r)=>r===e?t:n),this._limits={};let[n,r]=this._sides.map(e=>e.report);this._diff=n&&r?Gr(n,r):null}async _select(e,t,n){let r=++this._tokens[e],i={...q(),key:n?``:t,name:t,loading:!!t};if(this._setSide(e,i),t)try{let a=n?await n.text():await Et(t);if(this._settled||this._tokens[e]!==r)return;this._setSide(e,{...i,loading:!1,report:Vr(a)})}catch(n){!this._settled&&this._tokens[e]===r&&this._setSide(e,{...i,loading:!1,error:`Couldn't read ${t}: ${n.message}`})}}_file(e,t){let n=t.target.files?.[0];t.target.value=``,n&&this._select(e,n.name,n)}_swap(){this._sides.some(e=>e.loading)||(this._tokens=this._tokens.map(e=>e+1),this._sides=this._sides.toReversed(),this._setSide(0,this._sides[0]))}_picker(e){let t=this._sides[e],n=e===0?`Before`:`After`;return R`<section class="picker"><label for="${`report-${e}`}">${n}</label> <select id="${`report-${e}`}" @change="${t=>this._select(e,t.target.value)}"><option value="" .selected="${!t.key}">${t.name&&!t.key?t.name:`Choose a saved report…`}</option>${this._names.map(e=>R`<option value="${e}" .selected="${t.key===e}">${e}</option>`)}</select><div class="file-picker"><label class="file-button">Open file…<input type="file" accept=".json,.md,.markdown" aria-label="${`Open ${n.toLowerCase()} report file`}" @change="${t=>this._file(e,t)}"></label> <span class="muted">${t.loading?`Reading report…`:t.report?`${t.report.byId.size.toLocaleString()} unique findings`:`JSON or Markdown`}</span></div>${t.error?R`<p class="error" role="alert">${t.error}</p>`:z} ${t.report?.missingIds?R`<p class="warning">${t.report.missingIds} findings without an id excluded.</p>`:z}</section>`}_finding(e){let t=this._diff.after.byId.get(e)?.[0]??this._diff.before.byId.get(e)?.[0];return R`<span class="finding"><strong>${F(t)||e}</strong><code>${e}</code>${t.file?R`<small>${t.file}</small>`:z}</span>`}_matches(e){let t=this._query.trim().toLowerCase();return!t||[e,...[...this._diff.before.byId.get(e)??[],...this._diff.after.byId.get(e)??[]].flatMap(e=>[F(e),e.file])].filter(Boolean).some(e=>e.toLowerCase().includes(t))}_section(e,t,n,r,i=``){let a=n.filter(e=>(Array.isArray(e)?e:[e.id??e]).some(e=>this._matches(e))),o=this._limits[e]??Y;return R`<section class="${`change-section ${i}`}" aria-label="${t}"><h4>${t}<span class="count">${this._query.trim()?`${a.length} / `:``}${n.length}</span></h4>${a.length>0?R`<ul>${a.slice(0,o).map(r)}</ul>`:R`<p class="empty">${n.length>0?`No matching findings.`:`No changes.`}</p>`} ${a.length>o?R`<button class="more" @click="${()=>{this._limits={...this._limits,[e]:o+Y}}}">Show ${Math.min(Y,a.length-o)} more (${a.length-o} remaining)</button>`:z}</section>`}_changeRow(e,t){let n=t===`severity`&&JSON.stringify(e.originalBefore)!==JSON.stringify(e.originalAfter);return R`<li class="change-row">${this._finding(e.id)}<span class="change-value"> <span class="from">${J(e.before,t)}</span><span aria-label="to">→</span><span>${J(e.after,t)}</span> ${n?R`<small>Original: ${J(e.originalBefore)} → ${J(e.originalAfter)}</small>`:z}</span></li>`}_presenceRow(e,t){let n=J(G(t,e,`verdict`));return R`<li class="change-row">${this._finding(e)}<span class="change-value"> <span>${J(G(t,e,`severity`))}</span><span>${J(G(t,e,`confidence`),`confidence`)}</span> ${n===`Not supplied`?z:R`<small>${n}</small>`}</span></li>`}_groupRow(e){let t=new Set(e),n=e=>e.rows.map(e=>e.filter(e=>t.has(e))).filter(e=>e.length).map(e=>e.join(`, `)).filter((e,t,n)=>n.indexOf(e)===t).join(` | `);return R`<li class="group-row"><div class="group-members">${e.map(e=>this._finding(e))}</div><div class="group-shape"><span class="from">${n(this._diff.before)}</span><span aria-label="to">→</span><span>${n(this._diff.after)}</span></div></li>`}_verdictCount(e,t){return[...e.byId.keys()].filter(n=>G(e,n,`verdict`).includes(t)).length}_results(){let e=this._diff;return e?R`<div class="summary" aria-label="Comparison summary"><div><span>Unique findings</span><strong>${e.before.byId.size} → ${e.after.byId.size}</strong></div><div><span>In common</span><strong>${e.shared.length}</strong></div><div class="verdict-confirmed"><span>Confirmed</span><strong>${this._verdictCount(e.before,`confirmed`)} → ${this._verdictCount(e.after,`confirmed`)}</strong></div><div class="verdict-refuted"><span>Refuted</span><strong>${this._verdictCount(e.before,`refuted`)} → ${this._verdictCount(e.after,`refuted`)}</strong></div></div><div class="change-counts" aria-label="Change counts"><span>${e.added.length} added</span><span>${e.removed.length} removed</span> <span>${e.joined.length} joined</span><span>${e.split.length} split</span> <span>${e.confidence.length} confidence</span><span>${e.severity.length} severity</span></div><div class="results-head"><p>Changes from <strong>${this._sides[0].name}</strong> to <strong>${this._sides[1].name}</strong>.</p><input class="search" type="search" aria-label="Filter comparison findings" placeholder="Filter by id, title or file…" .value="${this._query}" @input="${e=>{this._query=e.target.value,this._limits={}}}"></div>${e.shared.length===0&&(e.before.missingIds||e.after.missingIds)?R`<p class="warning" role="status">No shared ids to compare. Findings without ids cannot be matched.</p>`:e.unchanged?R`<p class="identical" role="status">No differences in finding ids, grouping, verdicts, confidence or severity.</p>`:z}<h3 class="section-title">Findings</h3><div class="columns">${this._section(`added`,`Added`,e.added,t=>this._presenceRow(t,e.after),`positive`)} ${this._section(`removed`,`Removed`,e.removed,t=>this._presenceRow(t,e.before),`negative`)}</div><h3 class="section-title">Revalidation</h3><p class="note">Changed verdicts for shared ids, grouped by the after report’s verdict.</p><div class="columns">${this._section(`confirmed`,`Confirmed`,e.confirmed,e=>this._changeRow(e,`verdict`),`verdict-confirmed`)} ${this._section(`refuted`,`Refuted`,e.refuted,e=>this._changeRow(e,`verdict`),`verdict-refuted`)}</div>${e.otherVerdicts.length>0?this._section(`other`,`Other verdict changes`,e.otherVerdicts,e=>this._changeRow(e,`verdict`)):z}<h3 class="section-title">Grouping</h3><p class="note">Only shared ids. Each cluster connects changed pairs; added or removed findings do not count as regrouping.</p><div class="columns">${this._section(`joined`,`Joined`,e.joined,e=>this._groupRow(e),`positive`)} ${this._section(`split`,`Split`,e.split,e=>this._groupRow(e),`negative`)}</div><h3 class="section-title">Ratings</h3><p class="note">Shared ids only. Severity includes report corrections; changes to original severity are also shown.</p><div class="columns">${this._section(`confidence`,`Confidence changes`,e.confidence,e=>this._changeRow(e,`confidence`))} ${this._section(`severity`,`Severity changes`,e.severity,e=>this._changeRow(e,`severity`))}</div>`:R`<p class="placeholder" role="status">${this._sides.some(e=>e.loading)||this._catalogLoading?`Loading reports…`:`Choose two saved reports or open two files to compare.`}</p>`}render(){return R`<dialog aria-labelledby="compare-title" @close="${this._onClose}"><header><div><h3 id="compare-title">Compare reports</h3><p>Compare findings, grouping and ratings between two reports.</p></div><button aria-label="Close report comparison" @click="${this._onClose}">Close</button></header><div class="pickers">${this._picker(0)}<button class="swap" aria-label="Swap before and after reports" ?disabled="${this._sides.some(e=>e.loading)}" @click="${this._swap}">⇄</button>${this._picker(1)}</div>${this._catalogError?R`<p class="error" role="alert">${this._catalogError} You can still open files directly.</p>`:z}<div class="results">${this._results()}</div></dialog>`}};customElements.define(`report-compare-dialog`,qr);function Jr(){if(Ct())throw Error(`Report comparison is available in local/E2E mode.`);return w(`report-compare-dialog`)}var Yr=`dialog{--issue-open: var(--green);--issue-merged: #a371f7;--issue-closed: var(--critical);--issue-quiet: var(--muted);--claude: #d97757;width:min(94vw,52rem);max-width:none;max-height:calc(100dvh - 7rem);padding:0;overflow:hidden}dialog[open]{display:flex;flex-direction:column}header{display:flex;align-items:start;gap:.6rem;flex-shrink:0;padding:.8rem 1rem;border-bottom:1px solid var(--border)}header h3{display:flex;align-items:baseline;gap:.5rem;flex:1;min-width:0;margin:0;font-size:1rem;line-height:1.35;overflow-wrap:anywhere}header button{flex-shrink:0;border:0;background:transparent;color:var(--muted);font-family:inherit;font-size:1.4rem;line-height:1;padding:0 .25rem;&:hover{color:var(--text)}&.back{font-size:1.1rem}}.repo-item-body{display:flex;flex-direction:column;gap:.75rem;min-height:0;padding:.9rem 1rem 1rem;overflow:auto;overscroll-behavior:contain;font-size:.85rem}.repo-item-number{color:var(--muted);font-weight:400;font-variant-numeric:tabular-nums}.repo-item-meta{display:flex;flex-wrap:wrap;align-items:center;gap:.3rem .9rem;color:var(--muted)}.repo-item-meta strong{color:var(--text);font-weight:500}code{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.9em;color:var(--text)}.repo-item-badge{--state: var(--issue-open);padding:.1rem .55rem;border-radius:999px;background:var(--state);color:var(--surface);font-weight:500;&.is-merged,&.is-completed{--state: var(--issue-merged)}&.is-closed{--state: var(--issue-closed)}&.is-draft,&.is-not-planned{--state: var(--issue-quiet)}}svg{flex:none;width:14px;height:14px;fill:none;stroke:currentcolor;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round}svg .fill{fill:currentcolor;stroke:none}svg .dotted{stroke-dasharray:1 2.2}.repo-item-state{--state: var(--issue-open);display:inline-flex;align-self:center;color:var(--state);&.is-merged,&.is-completed{--state: var(--issue-merged)}&.is-closed{--state: var(--issue-closed)}&.is-draft,&.is-not-planned{--state: var(--issue-quiet)}}.repo-item-mark{display:inline-flex;align-items:center;gap:.25rem;& svg{width:12px;height:12px}&.is-approved,&.is-success{color:var(--green)}&.is-changes,&.is-failure{color:var(--critical)}&.is-pending{color:var(--medium)}}.is-added{color:var(--green)}.is-deleted{color:var(--critical)}npm-delta{display:inline-block;flex:none;min-width:1px;min-height:1em}.npm-delta{flex:none;color:var(--muted);font-size:.7rem;white-space:nowrap;font-variant-numeric:tabular-nums}.npm-delta-typeless{margin-left:.35em;opacity:.8}.repo-item-labels{display:flex;flex-wrap:wrap;gap:.3rem}.repo-item-label{padding:0 .45rem;border:1px solid var(--border);border-radius:999px;font-size:.75rem;line-height:1.5;&:before{content:"";display:inline-block;width:.5rem;height:.5rem;margin-right:.3rem;border-radius:50%;background:var(--label, var(--muted));vertical-align:middle}}.repo-item-agents{display:inline-flex;align-items:center;align-self:start;gap:.25rem;height:1lh}.agent-mark{display:inline-flex}.agent-mark :is(svg,img){width:16px;height:16px}.agent-mark:is(.is-claude,.is-codex,.is-copilot) svg{fill:currentcolor;stroke:none}.agent-mark.is-claude svg{color:var(--claude)}.agent-logo{display:inline-flex}.agent-logo svg{flex:none}.agent-avatar{border-radius:3px}.repo-item-section{& h4{margin:0 0 .3rem;color:var(--muted);font-size:.72rem;font-weight:600;letter-spacing:.04em;text-transform:uppercase}& h4.repo-item-deps-heading{display:flex;align-items:center;justify-content:space-between;gap:.5rem;min-height:1.6rem}& ul{margin:0;padding:0;list-style:none;border:1px solid var(--border);border-radius:var(--ui-radius, 6px);overflow:hidden}& li+li{border-top:1px solid var(--border)}}.dependency-review-copy-button{display:inline-flex;align-items:center;justify-content:center;width:1.6rem;height:1.6rem;padding:0;border:0;border-radius:var(--ui-radius, 4px);background:transparent;color:var(--muted);&:hover{background:var(--surface-active);color:var(--text)}&.copied{color:var(--green)}& svg{width:.9rem;height:.9rem}}.repo-item-row{display:flex;align-items:center;gap:.5rem;width:100%;padding:.35rem .6rem;border:0;background:none;color:var(--text);font:inherit;text-align:left;text-decoration:none;&:hover{background:var(--surface-active)}&:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}}.repo-item-title{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.review-row{display:flex;flex-wrap:wrap;align-items:center;gap:.25rem .7rem;padding:.35rem .6rem;& time{margin-left:auto;color:var(--muted);font-variant-numeric:tabular-nums}}.review-state{display:inline-flex;align-items:center;gap:.25rem;& svg{width:12px;height:12px}&.is-approved>svg{color:var(--green)}&:is(.is-changes,.is-failed)>svg{color:var(--critical)}&.is-running>svg{color:var(--medium)}&:is(.is-commented,.is-dismissed,.is-requested){color:var(--muted)}}.review-on{color:var(--muted);& a{color:var(--text);text-decoration:none;&:hover{color:var(--accent)}}}.review-path{flex:none;max-width:45%;margin-left:auto;overflow:hidden;color:var(--muted);text-overflow:ellipsis;white-space:nowrap}.review-severity{flex:none;padding:0 .35rem;border-radius:999px;background:var(--low);color:#fff;font-size:.68rem;font-weight:600;line-height:1.5;&.is-p0{background:var(--critical)}&.is-p1{background:var(--high)}&.is-p2{background:var(--medium);color:#000}}.repo-item-lockfiles+.repo-item-lockfiles{margin-top:.6rem}.repo-item-lockfiles>p{margin:0 0 .3rem;overflow-wrap:anywhere}.repo-item-version{color:var(--muted)}.repo-item-was{flex:0 1000 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--muted)}span.repo-item-row:hover{background:none}.repo-item-review{display:inline-flex;align-items:center;gap:.2rem;flex:none;font-size:.75rem;font-variant-numeric:tabular-nums;&.is-passed{color:var(--green)}&.is-wanted{color:var(--medium)}&.is-failed{color:var(--critical)}& svg{width:12px;height:12px}}.repo-item-failed{margin-bottom:.6rem;>p{margin:0 0 .3rem;color:var(--critical)}}.repo-item-unreviewed{margin-top:.6rem;& summary{color:var(--medium);cursor:default}& ul{margin-top:.3rem}}.repo-item-tag{flex:none;padding:0 .4rem;border:1px solid var(--border);border-radius:999px;color:var(--muted);font-size:.72rem;line-height:1.5;&.is-new{border-color:currentcolor;color:var(--medium)}}.repo-item-description{padding-top:.75rem;border-top:1px solid var(--border);white-space:pre-wrap;overflow-wrap:anywhere;line-height:1.6}.repo-item-quiet{color:var(--muted)}.nwd-actions{margin:0;padding:.6rem 1rem}.nwd-actions a{border:1px solid var(--border);border-radius:var(--ui-radius, 5px);padding:.35rem .9rem;color:var(--text);font-size:.8rem;text-decoration:none;&:hover{border-color:var(--accent);color:var(--accent)}&.primary{background:var(--accent);border-color:var(--accent);color:var(--surface);&:hover{color:var(--surface);filter:brightness(1.08)}}&:focus-visible{outline:2px solid var(--accent);outline-offset:2px}}a,button{cursor:default}:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}.repo-item-body:focus{outline:none}
|
|
39
|
+
`,Xr=`.app-user{display:inline-flex;align-items:center;gap:.3em;min-width:0;max-width:100%;vertical-align:bottom}.app-user-avatar{position:relative;display:inline-flex;align-items:center;justify-content:center;flex:none;width:1.6em;height:1.6em;overflow:hidden;border-radius:50%;background:var(--surface-active);color:var(--muted);font-size:.7em;font-weight:600;line-height:1;& img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}}.app-user-mark{display:inline-flex;flex:none;width:1.15em;height:1.15em;& svg{flex:none;width:100%;height:100%;fill:currentcolor;stroke:none}& img{width:100%;height:100%;border-radius:3px;object-fit:cover}&.is-claude{color:var(--claude, #d97757)}&:is(.is-codex,.is-copilot){color:var(--text)}}.app-user-name{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.commit-avatars{display:inline-flex;flex:none;isolation:isolate}.commit-avatar{position:relative;display:inline-flex;align-items:center;justify-content:center;flex:none;width:1.6em;height:1.6em;overflow:hidden;border-radius:50%;box-shadow:0 0 0 1.5px var(--bg);background:var(--surface-active);color:var(--muted);font-size:.7em;font-weight:600;line-height:1;+.commit-avatar{margin-left:-.45em}&:nth-child(1){z-index:4}&:nth-child(2){z-index:3}&:nth-child(3){z-index:2}& img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}& svg{flex:none;width:62%;height:62%;fill:currentcolor;stroke:none}&.is-claude{background:#d97757;color:#fff}&.is-codex{background:#fff;color:#000}&.is-copilot{background:#0d1117;color:#fff}&:is(.is-codex,.is-copilot){box-shadow:0 0 0 1.5px var(--bg),inset 0 0 0 1px var(--border)}}
|
|
40
|
+
`,Zr={open:[`open`,`Open`],merged:[`merged`,`Merged`],closed:[`closed`,`Closed`],branch:[`draft`,`A branch alone`]},X=e=>e==null?``:new Date(e).toLocaleDateString(`en-US`,{month:`short`,day:`numeric`,year:`numeric`}),Z=(e,t)=>`${e.toLocaleString(`en-US`)} ${t}${e===1?``:`s`}`,Q=(e,t,n=!1)=>e?R`<a class="${n?`primary`:``}" href="${e}" target="_blank" rel="noopener noreferrer">${t}</a>`:z,Qr={mirror:`mirror`,git:`git`,tarball:`tarball`,file:`file`},$r={git:`From git, which isn’t reviewed`,tarball:`A tarball not npm’s registry’s of its version, which isn’t reviewed`,integrity:`Locked with no integrity, which its reviews are held to`},ei={"lockfile-unreadable":`A lockfile here couldn’t be read`,"lockfile-unsupported":`Bun’s lockfile here isn’t read`,"github-not-found":`GitHub has none of a lockfile here at a commit compared`},ti=class extends C{static styles=[...C.styles,L(en),L(e),L(Xr),L(Yr)];static properties={repository:{attribute:!1},target:{attribute:!1},_trail:{state:!0},_items:{state:!0}};constructor(){super(),this._trail=[],this._items=new Map,this._controller=new AbortController}connectedCallback(){super.connectedCallback(),this._unsubscribe=xe(e=>{(!e||e.repoId===this.repository?.repoId&&e.number===this._trail.at(-1)?.number)&&this.requestUpdate()}),this._unobserve=S(e=>{l(),e||this.requestUpdate()}),this._trail.length===0&&this.target&&this.open(this.target)}disconnectedCallback(){this._controller.abort(),this._unsubscribe?.(),this._unobserve?.(),super.disconnectedCallback()}focusInitial(){this.renderRoot.querySelector(`.repo-item-body`)?.focus()}open(e){if(this._trail=[...this._trail,e],e.number==null||this._items.has(e.number))return;let t=e.number;this._items=new Map(this._items).set(t,{status:`loading`});let{repoId:n}=this.repository;ye(n,t,{signal:this._controller.signal}).then(e=>(s(n,[e],o(e)),{status:`ready`,item:e}),e=>e?.name===`AbortError`?null:{status:`error`,error:e?.message||`It could not be read.`}).then(e=>(e&&(this._items=new Map(this._items).set(t,e)),null))}back(){this._trail=this._trail.slice(0,-1)}render(){let e=this._trail.at(-1);if(!e)return z;let[t,n,r]=e.session?this.renderSession(e.session):this.renderItem(e);return R`<dialog aria-labelledby="repo-item-title" @close="${this._onClose}" @click="${this._onBackdrop}"><header>${this._trail.length>1?R`<button type="button" class="back" aria-label="Back" @click="${()=>this.back()}">←</button>`:z}<h3 id="repo-item-title">${t}</h3><button type="button" aria-label="Close" @click="${this._onClose}">×</button></header><div class="repo-item-body" tabindex="-1">${n}</div><div class="nwd-actions"><span class="nwd-spacer"></span>${r}</div></dialog>`}renderItem({number:e,preview:t}){let n=this._items.get(e),r=c(this.repository.repoId,e)?.item,i=r?{...t,...n?.item,...r}:n?.item??t??null,o=i?.kind??`issues`,s=this.repository.repo,l=s?`https://github.com/${s}/${o===`pulls`?`pull`:`issues`}/${e}`:null,[u,d]=i?Be(o,i):[`open`,``],f=R`${i?R`<span class="${`repo-item-state is-${u}`}" role="img" aria-label="${d}">${Me[o][u]}</span>`:z} <span>${i?.title||`(no title)`} <span class="repo-item-number">#${e}</span></span>`,p=n?.status===`ready`?n.item:null,m=o===`pulls`,h=([e,t],n)=>R`<span class="${`repo-item-mark is-${n}`}">${t}${e}</span>`;return[f,R`<p class="repo-item-meta">${i?R`<span class="${`repo-item-badge is-${u}`}">${d}</span>`:z} ${i?.author?R`<span><strong>${k(i.author)}</strong> opened ${X(i.createdAt)}</span>`:i?.createdAt?R`<span>Opened ${X(i.createdAt)}</span>`:z} ${i?.state===`merged`?R`<span>merged${p?.mergedBy?R` by <strong>${k(p.mergedBy)}</strong>`:z} ${X(i.mergedAt??i.closedAt)}</span>`:i?.closedAt&&i.state!==`open`?R`<span>closed ${X(i.closedAt)}</span>`:z} ${i?.comments>0?R`<span class="repo-item-mark">${Ne}${Z(i.comments,`comment`)}</span>`:z}</p>${i?.labels?.length>0?R`<p class="repo-item-labels">${i.labels.map(e=>R`<span class="repo-item-label" style="${N(e.color?{"--label":`#${e.color}`}:{})}">${e.name}</span>`)}${i.moreLabels>0?R`<span class="repo-item-label">+${i.moreLabels}</span>`:z}</p>`:z} ${m&&i?.head?R`<p class="repo-item-meta"><span><code>${i.head}</code>${i.base?R` → <code>${i.base}</code>`:z}</span> <span><span class="is-added">+${i.additions.toLocaleString(`en-US`)}</span> <span class="is-deleted">−${i.deletions.toLocaleString(`en-US`)}</span></span> ${p?R`<span>${Z(p.commits,`commit`)}</span><span>${Z(p.files,`file`)} changed</span>`:z} ${Fe[i.review]&&i.state===`open`?h(Fe[i.review],i.review):z} ${b[i.checks]?h(b[i.checks],i.checks):z}</p>`:z} ${m&&p?a(p.reviews,s):z} ${p?this.renderLinked(m?`Closes`:`Closed by`,m?`issues`:`pulls`,m?p.closes:p.closedBy,p.moreLinked):z} ${m&&Ce(i)?this.renderDependencies(e,i.state===`open`):z} ${p?R`<div class="repo-item-description">${p.body?Ie(Pt(p.body)):R`<p class="repo-item-quiet">No description.</p>`}</div>${p.bodyShortened?R`<p class="repo-item-quiet">Shortened here; the rest of it is on GitHub.</p>`:z}`:n?.status===`error`?R`<p class="repo-item-quiet" role="alert">${n.error}</p>`:R`<p class="repo-item-quiet" role="status">Reading it…</p>`}`,Q(l,`Open on GitHub`,!0)]}renderDependencies(e,t){let n=Te(this.repository.repoId,e,t);if(n.status===`ready`&&n.lockfiles.length===0&&!n.cut)return z;let r=n.status===`loading`?R`<p class="repo-item-quiet" role="status">Reading its lockfiles…</p>`:n.status===`error`?R`<p class="repo-item-quiet" role="alert">${n.error}</p>`:this.renderAdded(n);return R`<section class="repo-item-section"><h4 class="repo-item-deps-heading">New dependency versions${n.formatted?R`<dependency-review-copy .text="${n.formatted}" .label="${`Copy your dependency reviews of its versions`}"></dependency-review-copy>`:z}</h4>${r}</section>`}renderAdded(e){let{lockfiles:t,cut:n,review:r}=e,i=t.filter(e=>e.error||e.added.length>0),a=r?Ae(e):null;return R`${r?R`${this.renderReviewTerms(r)}${this.renderFailures(je(e),r)}`:z} ${i.length===0&&t.length>0?R`<p class="repo-item-quiet">None: every version its lockfiles lock, they locked before.</p>`:z} ${i.map(e=>R`<div class="repo-item-lockfiles"><p>${e.paths.map((e,t)=>R`${t>0?`, `:``}<code>${e}</code>`)}</p>${e.error?R`<p class="repo-item-quiet" role="alert">${ei[e.error]??`These lockfiles couldn’t be read`}${e.detail?`: ${e.detail}`:`.`}</p>`:R`<ul>${e.added.toSorted((e,t)=>+!!e.was-!!t.was).map(e=>R`<li>${this.renderPackage(e,r)}</li>`)}</ul>${e.more>0?R`<p class="repo-item-quiet">and ${e.more.toLocaleString(`en-US`)} more</p>`:z}`}</div>`)} ${a?.count>0?R`<details class="repo-item-unreviewed"><summary>${Z(a.count,`other version`)} in its lockfiles ${a.count===1?`wants`:`want`} reviews</summary><ul>${a.versions.map(e=>R`<li>${this.renderPackage(e,r,!0)}</li>`)}</ul>${a.count>a.versions.length?R`<p class="repo-item-quiet">and ${(a.count-a.versions.length).toLocaleString(`en-US`)} more</p>`:z}</details>`:z} ${n?R`<p class="repo-item-quiet">It changes more files, or lockfiles in more directories, than are read; those past them aren’t told.</p>`:z}`}renderReviewTerms({required:e,since:t,reviewedDependencies:n,unasked:r,baselineError:i,baselineUnread:a=[],commentReviews:o,commentsFailed:s,lockfiles:c}){let l=a.slice(0,5);return R`<p class="repo-item-quiet">Each version needs ${Z(e,`full, accepting review`)} by as many reviewers${n?`, or the server’s reviewed dependencies to pass it; a patched one, not reviews here`:t?R`; those the repository’s lockfiles locked at <code>${t.slice(0,7)}</code> need none`:z}.</p>${i?R`<p class="repo-item-quiet" role="alert">The versions its lockfiles locked at that commit couldn’t be read (${i}), so every version wants its reviews.</p>`:z} ${l.length>0?R`<p class="repo-item-quiet" role="alert">At that commit, ${l.map((e,t)=>R`${t>0?`, `:``}<code>${e}</code>`)}${a.length>l.length?` and ${(a.length-l.length).toLocaleString(`en-US`)} more`:``} couldn’t be read, so the versions ${a.length===1?`it`:`they`} locked want reviews.</p>`:z} ${r?R`<p class="repo-item-quiet" role="alert">The server’s reviewed dependencies didn’t answer for ${Z(r,`version`)}, so ${r===1?`it wants`:`they want`} reviews.</p>`:z} ${o?R`<p class="repo-item-quiet">Reviews in its comments by ${Z(o,`reviewer`)} count too while it is open, a patched version’s of its patch.</p>`:z} ${s?R`<p class="repo-item-quiet" role="alert">Its comments couldn’t be read for reviews, so none of theirs count.</p>`:z} ${c?R`<p class="repo-item-quiet">${c.mode===`include`?`Only these lockfiles are reviewed: `:`These lockfiles aren’t reviewed: `}${c.paths.length===0?`none`:c.paths.map((e,t)=>R`${t>0?`, `:``}<code>${e}</code>`)}.</p>`:z}`}renderFailures(e,t){if(!e)return z;let{versions:n,count:r,unread:i,unchecked:a,cut:o}=e;return R`<div class="repo-item-failed" role="alert">${r>0?R`<p>Its review fails: ${Z(r,`version`)} its lockfiles lock can’t be reviewed, as only npm’s registry’s, locked with an integrity, are.</p><ul>${n.map(e=>R`<li>${this.renderPackage(e,t,!0)}</li>`)}</ul>${r>n.length?R`<p class="repo-item-quiet">and ${(r-n.length).toLocaleString(`en-US`)} more</p>`:z}`:z} ${i.length>0||a>0||o?R`<p>Its review fails: not every lockfile it changes was read${a>0?`, and they lock ${Z(a,`version`)} past those checked`:``}.</p>`:z}</div>`}renderPackage(e,t=null,n=!1){let{name:r,version:i,source:a,from:o,dev:s,was:c}=e,l=(a===`registry`||a===`mirror`)&&i,u=n?void 0:c?.find(e=>e!==i),d=l&&!e.patched&&t?.canReview&&y(e,t)!==`unsupported`?P({view:`npm`,packageName:r,packageSpec:i,bundleTab:`review`,...u?{reviewPackage:r,reviewSpec:u}:{}}):null,f=l?d||u&&P({view:`npm`,packageName:r,packageSpec:i,bundleTab:`compare`,compareSpec:u})||P({view:`npm`,packageName:r,packageSpec:i}):null,p=R`${ze}<span class="repo-item-title">${r}${i?R`<span class="repo-item-version">@${i}</span>`:z}</span> ${n?z:c?R`<span class="repo-item-was" data-tooltip-truncated data-tooltip="${`Was ${c.join(`, `)}`}">from ${c.join(`, `)}</span>`:R`<span class="repo-item-tag is-new" data-tooltip="Its lockfiles locked no version of it before">new</span>`} ${this.renderReview(e,t)} ${a===`registry`&&i&&y(e,t)===`wanted`?R`<npm-delta .name="${r}" .version="${i}" .base="${u??`(empty)`}"></npm-delta>`:z} ${s?R`<span class="repo-item-tag" data-tooltip="Only devDependencies need it">dev</span>`:z} ${e.patched?R`<span class="repo-item-tag" data-tooltip="pnpm patches it as it installs it">patched</span>`:z} ${Qr[a]?R`<span class="repo-item-tag" data-tooltip="${o??z}">${Qr[a]}</span>`:z}`;return f?R`<a class="repo-item-row" href="${f}" target="_blank" rel="noopener noreferrer">${p}</a>`:R`<span class="repo-item-row">${p}</span>`}renderReview(e,t){let n=y(e,t);if(n===`unsupported`)return R`<span class="repo-item-review is-failed" data-tooltip="${$r[e.unsupported]}">can’t review</span>`;if(e.reviewed&&n===`passed`)return R`<span class="repo-item-review is-passed" data-tooltip="The server’s reviewed dependencies pass it">${b.success[1]}reviewed</span>`;if(e.rejected)return R`<span class="repo-item-review is-failed" data-tooltip="A reviewer rejects it: it wants their newer accepting review">rejected</span>`;if(e.patched&&n===`wanted`)return R`<span class="repo-item-review is-wanted" data-tooltip="Reviews here pass no patch: the server’s reviewed dependencies, or reviews of its patch in the pull request’s comments, pass it">unreviewed</span>`;if(n===`baseline`)return R`<span class="repo-item-tag" data-tooltip="Locked at the commit reviews are counted since: it needs none">baseline</span>`;if(!n)return z;let r=`${Z(e.reviews,`full, accepting review`)} of ${t.required} required`;return R`<span class="${`repo-item-review is-${n}`}" data-tooltip="${r}">${n===`passed`?b.success[1]:z}${e.reviews}/${t.required}</span>`}renderLinked(e,t,n,r=0){return!n?.length&&!r?z:R`<section class="repo-item-section"><h4>${e}</h4>${n?.length?R`<ul>${n.map(e=>{let n={...e,kind:t,labels:[],comments:0},[r,i]=Be(t,n);return R`<li><button type="button" class="repo-item-row" @click="${()=>this.open({number:e.number,preview:n})}"><span class="${`repo-item-state is-${r}`}" role="img" aria-label="${i}">${Me[t][r]}</span> <span class="repo-item-number">#${e.number}</span><span class="repo-item-title">${e.title||`(no title)`}</span></button></li>`})}</ul>`:z}${r>0?R`<p class="repo-item-quiet">${n?.length?`and `:``}${r.toLocaleString(`en-US`)} more, on GitHub</p>`:z}</section>`}renderSession(e){let t=this.repository.repo,[n,r]=Zr[e.state]??Zr.branch,i=e.pulls.length+e.commits.length,a=R`<span class="repo-item-agents">${e.agents.map(e=>R`<span class="${`agent-mark is-${e}`}" role="img" aria-label="${m(e)}">${He(e)}</span>`)}</span><span>${e.title||`(no title)`}</span>`,o=qe(e);return[a,R`<p class="repo-item-meta"><span class="${`repo-item-badge is-${n}`}">${r}</span> ${e.authors.length>0?R`<span><strong>${St(e.authors).map((e,t)=>R`${t>0?`, `:``}${k(e)}`)}</strong></span>`:z} ${e.startedAt==null?z:R`<span>${X(e.startedAt)}${e.updatedAt!=null&&X(e.updatedAt)!==X(e.startedAt)?` \u2013 ${X(e.updatedAt)}`:``}</span>`} <span>${i>0?Z(i,`task`):`no pull request`}</span> ${e.additions||e.deletions?R`<span><span class="is-added">+${e.additions.toLocaleString(`en-US`)}</span> <span class="is-deleted">−${e.deletions.toLocaleString(`en-US`)}</span></span>`:z}</p>${this.renderLinked(`Pull requests`,`pulls`,e.pulls)} ${e.commits.length>0?R`<section class="repo-item-section"><h4>Commits to the default branch</h4><ul>${e.commits.map(e=>R`<li><a class="repo-item-row" href="${t?`https://github.com/${t}/commit/${e.oid}`:z}" target="_blank" rel="noopener noreferrer"><code>${e.oid.slice(0,7)}</code><span class="repo-item-title">${e.title||`(no message)`}</span></a></li>`)}</ul></section>`:z} ${this.renderLinked(`Issues`,`issues`,e.issues.map(e=>({...e,reason:null})))} ${e.branches.length>0?R`<section class="repo-item-section"><h4>${e.branches.length===1?`Branch`:`Branches`}</h4><ul>${e.branches.map(e=>R`<li><a class="repo-item-row" href="${t?tt(t,e):z}" target="_blank" rel="noopener noreferrer"><code>${e}</code></a></li>`)}</ul></section>`:z}`,R`${Q(o,e.sessions[0]?`Open in Claude Code`:`Open the Codex task`)}${Q(Qe(e,t),`Open on GitHub`,!0)}`]}};customElements.define(`repository-item-dialog`,ti);function ni(e){return w(`repository-item-dialog`,e)}var ri=`dialog{width:min(94vw,880px);max-width:880px}.conflicts-list{list-style:none;display:flex;flex-direction:column;gap:.6rem;max-height:65vh;overflow-y:auto;overscroll-behavior:contain}.conflict-card{padding:.6rem .7rem;border:1px solid var(--border);border-radius:var(--ui-radius, 6px)}.finding-meta{display:flex;flex-wrap:wrap;align-items:center;gap:.4rem;color:var(--muted);font-size:.72rem}.location,code{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;overflow-wrap:anywhere}.location{flex:1}h4{margin:.4rem 0 .55rem;font-size:.88rem;font-weight:600;overflow-wrap:anywhere}dt{margin-top:.5rem;color:var(--muted);font-size:.72rem}.variant{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,15rem);gap:.65rem;padding:.4rem 0;font-size:.8rem;+&{border-top:1px solid var(--border)}}.value{white-space:pre-wrap;overflow-wrap:anywhere}.reports{display:flex;flex-wrap:wrap;place-content:start flex-end;gap:.3rem}.report-button{display:inline-flex;align-items:center;gap:.4rem;max-width:100%;min-height:22px;padding:0 .4rem 0 0;border:1px solid var(--border);border-radius:var(--ui-radius, 4px);overflow:hidden;background:var(--bg);color:var(--text);font:inherit;font-size:.72rem;cursor:default;text-align:left;>span:last-child{min-width:0;overflow-wrap:anywhere}&:hover{border-color:var(--accent)}}
|
|
41
|
+
`,ii={revalidate:`Outcome`,revalidateVerdict:`Explanation`,revalidateRecommendation:`Recommendation`},ai=class extends C{static styles=[...C.styles,L(E),L(Ze),L(ri)];static properties={conflicts:{attribute:!1}};constructor(){super(),this.conflicts=[]}render(){return R`<dialog aria-labelledby="revalidation-conflicts-title" @close="${this._onClose}"><header><h3 id="revalidation-conflicts-title">Revalidation conflicts</h3><p class="nwd-intro">Reports disagree about ${this.conflicts.length} finding${this.conflicts.length===1?``:`s`}. App view is unavailable until the conflicting reports are separated or updated.</p></header><ul class="conflicts-list">${this.conflicts.map(({finding:e,copies:t})=>R`<li class="conflict-card"><div class="finding-meta">${D(e.severity)} ${e.file?R`<span class="location">${e.file}${M(e.line)?`:${Ft(M(e.line))}`:``}</span>`:z} <code>${It(e.id)??e.id}</code></div><h4>${F(e)}</h4><dl>${Re(t).map(({field:t,variants:n})=>R`<dt>${ii[t]??t}</dt>${n.map(({value:t,reports:n})=>R`<dd class="variant"><div class="value">${t||R`<em>Empty</em>`}</div><div class="reports">${n.map(t=>t?R`<button type="button" class="report-button" aria-label="${`Open ${Mt(t)} at this finding`}" @click="${()=>this._finish({id:e.id,reportName:t})}">${I(jt[it(t)]??jt.default)}<span>${Mt(t)}</span></button>`:R`<span>Loaded report</span>`)}</div></dd>`)}`)}</dl></li>`)}</ul><footer class="nwd-actions"><span class="nwd-spacer"></span><button type="button" @click="${this._onClose}">Close</button></footer></dialog>`}};customElements.define(`revalidation-conflicts-dialog`,ai);function oi(e){return w(`revalidation-conflicts-dialog`,{conflicts:[...e.values()]})}var si=`.spd-list{margin:0 0 .8rem 1.15rem;color:var(--muted);font-size:.82rem;line-height:1.5;& li{margin:.3rem 0}& strong{color:var(--text);font-weight:600}}.spd-denied{color:light-dark(#b25b00,#f0a060);font-size:.78rem}code{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.92em}
|
|
42
|
+
`,ci=class extends C{static styles=[...C.styles,L(si)];static properties={_retryDenied:{state:!0}};constructor(){super(),this._retryDenied=!1}_onConfirm=()=>this._finish(null);_onRetry=async()=>{if(await Le()){this._finish(`granted`);return}this._retryDenied=!0};_chromiumBody(){let e=location.hostname===`localhost`?R` Plain <code>localhost</code> can never qualify — for local testing open the app via <code>127.0.0.1</code> instead.`:z;return R`<p class="nwd-intro">Chromium-based browsers (Chrome, Edge, Brave, …) never show a prompt — persistence is granted silently, and only to sites the browser considers important. Any of these qualifies it:</p><ul class="spd-list"><li><strong>Install the app</strong> — the install icon at the right end of the address bar, or the browser menu's Install entry.</li><li><strong>Bookmark</strong> this page.</li><li>Keep using the site — regular visits raise its engagement score.</li></ul><p class="nwd-note ui-hint">The browser re-evaluates on every request, so after any of the above press <strong>Try again</strong>.${e}</p>`}_firefoxBody(){return R`<p class="nwd-intro">Firefox asks with a permission prompt. A dismissed or blocked prompt is remembered, and the site can't re-ask until the old answer is cleared:</p><ol class="spd-list"><li>Click the permissions icon next to the address bar.</li><li>Clear the <strong>Store Data in Persistent Storage</strong> entry if it's blocked.</li><li>Press <strong>Try again</strong> below and choose <strong>Allow</strong>.</li></ol>`}_webkitBody(){return R`<p class="nwd-intro">Safari — and every iOS browser — additionally deletes a site's storage after <strong>7 days</strong> of browser use without interacting with the site, and no permission exempts a normal tab. Installing the app is what does:</p><ul class="spd-list"><li>On iPhone / iPad: open the <strong>Share</strong> menu, choose <strong>Add to Home Screen</strong>, and use the app from that icon.</li><li>In Safari on macOS: choose <strong>File → Add to Dock…</strong> and use the app from the Dock.</li><li>Installed web apps are exempt from the 7-day cleanup. Otherwise, use the site at least once a week.</li></ul>`}render(){let e=et(),t=e===`webkit`?this._webkitBody():e===`firefox`?this._firefoxBody():this._chromiumBody();return R`<dialog @close="${this._onClose}"><header><h3>Protect stored data</h3></header><p class="nwd-intro">Reports, bundles and triage live in the browser's <strong>best-effort storage</strong> — the browser is allowed to delete all of it at once under disk pressure or after long inactivity, without asking. The persistent-storage request that would prevent this was just declined.</p>${t}<footer class="nwd-actions">${this._retryDenied?R`<span class="spd-denied" role="status">Still not granted.</span>`:z} <span class="nwd-spacer"></span> ${e===`webkit`?z:R`<button type="button" @click="${this._onRetry}">Try again</button>`} <button type="button" class="primary" @click="${this._onConfirm}">Got it</button></footer></dialog>`}};customElements.define(`storage-persist-dialog`,ci);function li(){return new Promise(e=>{let t=document.createElement(`storage-persist-dialog`),n=n=>{t.remove(),e(n)};t.addEventListener(`resolve`,e=>n(e.detail)),t.addEventListener(`modal-conflict`,()=>n(null)),document.body.append(t)})}var ui=`.ssd-close{position:absolute;top:6px;right:6px;z-index:1}header{padding-right:1.5rem}.lwd-list{margin:.5rem 0 0}
|
|
43
|
+
`,di=10,fi=class extends C{static styles=[...C.styles,L(j),L(B),L(ui)];static properties={names:{type:Array}};constructor(){super(),this.names=[]}focusInitial(){this.renderRoot.querySelector(`button[data-role="sync"]`)?.focus()}_onSync=()=>this._finish(!0);_onDismiss=()=>this._finish(!1);_onClose=()=>this._finish(!1);_list(){if(this.names.length<2)return z;let e=this.names.slice(0,di),t=this.names.length-e.length;return R`<ul class="lwd-list">${e.map(e=>R`<li><strong>${e}</strong></li>`)} ${t>0?R`<li>and ${t} more</li>`:z}</ul>`}render(){let e=this.names.length===1,t=e?R`Your copy of <strong>"${this.names[0]}"</strong> no longer matches its cloud copy, so other members may be seeing a different version.`:R`<strong>${this.names.length}</strong> reports no longer match their cloud copies, so other members may be seeing different versions.`;return R`<dialog @close="${this._onClose}"><button type="button" class="detail-action ssd-close" data-role="close" aria-label="Close" @click="${this._onDismiss}">×</button><header><h3>Reports out of sync</h3></header><p class="lwd-body">${t}</p>${this._list()}<p class="nwd-note ui-hint">Sync compares ${e?`it`:`each one`} with the cloud. Where it's clear which copy is newer, the other is updated; otherwise you choose which to keep.</p><footer class="nwd-actions"><span class="nwd-spacer"></span> <button type="button" data-role="dismiss" @click="${this._onDismiss}">Not now</button> <button type="button" class="primary" data-role="sync" @click="${this._onSync}">Sync</button></footer></dialog>`}};customElements.define(`sync-suggest-dialog`,fi);function pi({names:e}={}){return new Promise(t=>{let n=document.createElement(`sync-suggest-dialog`);n.exclusive=!0,n.names=Array.isArray(e)?[...e]:[];let r=(e,r)=>{n.remove(),t({shown:e,sync:r})};n.addEventListener(`resolve`,e=>r(!0,e.detail===!0)),n.addEventListener(`modal-conflict`,()=>r(!1,!1)),document.body.append(n)})}var mi=class extends C{static styles=[...C.styles,L(B)];static properties={workspaceId:{type:String},items:{type:Array},_uploading:{state:!0},_errors:{state:!0},_done:{state:!0},_settled:{state:!0}};constructor(){super(),this.workspaceId=``,this.items=[],this._uploading=!1,this._errors=[],this._done=!1,this._settled=!1}focusInitial(){this.renderRoot.querySelector(`button[data-role="upload"]`)?.focus()}_onClose=()=>this._finish({uploaded:[],failed:[]});_onCancel=()=>this._finish({uploaded:[],failed:[]});_onUpload=async()=>{if(this._uploading)return;this._uploading=!0,this._errors=[];let e=[],t=[];for(let n of this.items)try{let r;if(n.kind===`bundle`)r=await fe(this.workspaceId,n.identifier);else{let e=await h(n.identifier);r=await le(this.workspaceId,n.identifier,e)}if(r.ok)e.push({kind:n.kind,identifier:n.identifier});else{let e=r.reason??`unknown`;t.push({kind:n.kind,identifier:n.identifier,reason:e}),this._errors=[...this._errors,{label:O(n),reason:e}]}}catch(e){let r=e?.message??String(e);t.push({kind:n.kind,identifier:n.identifier,reason:r}),this._errors=[...this._errors,{label:O(n),reason:r}]}this._uploading=!1,this._done=!0,t.length===0&&this._finish({uploaded:e,failed:t})};render(){let{count:e,singular:t,kindLabel:n}=_t(this.items),r=t?R`Upload <strong>"${O(this.items[0])}"</strong> to the workspace's remote inventory?`:R`Upload <strong>${e}</strong> local ${n} to the workspace's remote inventory?`,i=t?z:vt(this.items),a=this._uploading?t?`Uploading…`:`Uploading ${e} ${n}\u2026`:t?`Upload`:`Upload ${e}`;return R`<dialog @close="${this._onClose}"><header><h3>Upload to remote</h3></header><p class="lwd-body">${r}</p>${i}<p class="nwd-note ui-hint">End-to-end encrypted with the workspace key — only workspace members can decrypt the uploaded content.</p>${yt(this._errors)}<footer class="nwd-actions"><span class="nwd-spacer"></span> <button type="button" data-role="cancel" @click="${this._onCancel}" ?disabled="${this._uploading}">${this._done&&this._errors.length>0?`Close`:`Cancel`}</button> ${this._done&&this._errors.length>0?z:R`<button type="button" data-role="upload" @click="${this._onUpload}" ?disabled="${this._uploading||e===0}">${a}</button>`}</footer></dialog>`}};customElements.define(`sync-upload-dialog`,mi);function hi({workspaceId:e,items:t}={}){return w(`sync-upload-dialog`,{workspaceId:e??``,items:Array.isArray(t)?[...t]:[]})}var gi=`dialog{width:min(94vw,68rem);max-width:68rem;max-height:calc(100dvh - 7rem);display:flex;flex-direction:column}dialog:not([open]){display:none}.comparison-results{overflow:auto;min-height:0}.difference-counts{display:flex;flex-wrap:wrap;gap:.5rem 1rem;margin:.8rem 0;font-size:.8rem}.difference-counts span{border-inline-start:3px solid var(--difference);padding-inline-start:.5rem}.managed-only{--difference: var(--marker-blue, #559adf)}.local-only{--difference: var(--marker-green, #53a96a)}.mismatch{--difference: var(--high, #d69d37)}.finding{margin:.8rem 0 1.3rem}h4{font-size:.9rem;font-weight:600;overflow-wrap:anywhere}.location,.finding-id{margin-top:.3rem;color:var(--muted);font-size:.75rem;overflow-wrap:anywhere}.table-scroll{margin-top:.6rem;overflow-x:auto}table{width:100%;min-width:32rem;border-collapse:collapse;table-layout:fixed;font-size:.8rem}th,td{padding:.6rem .7rem;text-align:left;vertical-align:top;border:1px solid var(--border);overflow-wrap:anywhere}thead th{color:var(--muted);font-weight:500}th:first-child{width:8rem}tbody th{font-weight:500}td{white-space:pre-wrap}.difference-kind{display:block;margin-top:.3rem;color:var(--difference);font-size:.72rem}.highlight{background:rgb(from var(--difference) r g b / .08)}.unset{color:var(--muted);font-style:normal}.comment+.comment{margin-top:.6rem}.comment.changed{border-inline-start:3px solid var(--difference);padding-inline-start:.5rem}.skipped{color:var(--high, #d69d37);margin:.75rem 0;font-size:.8rem}.skipped ul{padding:.6rem 0 .6rem 1.2rem;overflow-wrap:anywhere}.empty{margin:1rem 0;color:var(--muted);font-size:.85rem}
|
|
44
|
+
`,_i={triage:`Triage state`,color:`Color`,fix:`Fix`,flagged:`Flag`,comment:`Comments`,ignoredReports:`Per-report ignores`},vi={"managed-only":`Only managed`,"local-only":`Only local`,mismatch:`Different`},yi={inprogress:`In progress`,fixed:`Fixed`,invalid:`Invalid`,deleted:`Deleted`,ignored:`Ignored`};function bi(e,t){return t===void 0||Array.isArray(t)&&t.length===0?R`<em class="unset">Not set</em>`:e===`comment`?t.map(e=>R`<div class="${`comment${e.different?` changed`:``}`}">${e.text}</div>`):e===`ignoredReports`?R`${t.join(`
|
|
45
|
+
`)}`:e===`flagged`?t?`Flagged`:`Not flagged`:e===`triage`?yi[t]??t:t}var xi=class extends V{static styles=[...V.styles,L(gi)];static properties={comparison:{attribute:!1}};constructor(){super(),this.comparison={matched:0,localFindings:0,findings:[],skipped:[]}}render(){let{matched:e,localFindings:t,findings:n,skipped:r}=this.comparison,i={"managed-only":0,"local-only":0,mismatch:0};for(let e of n)for(let t of e.differences)i[t.kind]++;return R`<dialog aria-labelledby="compare-triage-title" @close="${this._onClose}"><header><h3 id="compare-triage-title">Compare triage</h3><p class="lwd-body">${e} shared ${e===1?`finding`:`findings`} compared · ${n.length} with differences</p><p class="lwd-note">Local reports and managed reports are matched by finding ID. This comparison does not change either side. Comments are compared by text; per-report ignores only exist locally.</p>${n.length>0?R`<div class="difference-counts">Field differences: ${Object.entries(vi).map(([e,t])=>R`<span class="${e}">${t}: ${i[e]}</span>`)}</div>`:z}</header><div class="comparison-results">${r.length>0?R`<details class="skipped"><summary>${r.length} local ${r.length===1?`report could`:`reports could`} not be read; comparison is incomplete.</summary><ul>${r.map(e=>R`<li>${e.name}: ${e.reason}</li>`)}</ul></details>`:z} ${n.map(({id:e,finding:t,differences:n})=>R`<section class="finding"><h4>${t.title||t.description?.split(`
|
|
46
|
+
`)[0]||e}</h4>${t.file?R`<p class="location">${t.file}${t.line?`:${t.line}`:``}</p>`:z}<p class="finding-id"><code>${e}</code> · ${t.reports.join(`, `)}</p><div class="table-scroll"><table><thead><tr><th scope="col">Field</th><th scope="col">Local</th><th scope="col">Managed</th></tr></thead><tbody>${n.map(e=>R`<tr class="${e.kind}"><th scope="row">${_i[e.property]}<span class="difference-kind">${vi[e.kind]}</span></th><td class="${e.kind===`managed-only`?``:`highlight`}">${bi(e.property,e.local)}</td><td class="${e.kind===`local-only`?``:`highlight`}">${bi(e.property,e.managed)}</td></tr>`)}</tbody></table></div></section>`)} ${n.length===0?R`<p class="empty">${e>0?`No triage differences in the shared findings.`:t===0?`No findings were found in the readable local reports.`:`No findings are present in both local and managed reports.`}</p>`:z}</div><footer class="nwd-actions"><span class="nwd-spacer"></span><button type="button" data-role="cancel" @click="${this._onCancel}">Close</button></footer></dialog>`}};customElements.define(`triage-comparison-dialog`,xi);var Si=e=>w(`triage-comparison-dialog`,e),Ci=`dialog{max-width:560px;width:min(94vw,560px)}header h3{margin:0 0 .25rem}header p{margin:0 0 .9rem;color:var(--muted);font-size:.82rem}hr{border:0;border-top:1px solid var(--border);margin:.9rem 0}.section h4{margin:0 0 .25rem;font-size:.82rem;font-weight:600;text-transform:uppercase;letter-spacing:.08em;color:var(--muted)}.section p{margin:0 0 .55rem;color:var(--muted);font-size:.78rem}.section button{background:transparent;border:1px solid var(--border);color:var(--text);border-radius:var(--ui-radius, 5px);padding:.35rem .9rem;font:inherit;font-size:.8rem;&:hover{border-color:var(--accent);color:var(--accent)}&.primary{background:var(--accent);color:var(--surface);border-color:var(--accent)}&.primary:hover{filter:brightness(1.08)}&:disabled{opacity:.5}}input[type=file]{display:block;color:var(--text);font:inherit;font-size:.78rem;margin-bottom:.5rem}.error{margin:.5rem 0;padding:.4rem .6rem;border:1px solid var(--critical, #f87171);border-radius:var(--ui-radius, 5px);background:rgb(from var(--critical, #f87171) r g b / .07);color:var(--text);font-size:.78rem}.summary{margin:.5rem 0;font-size:.82rem;& strong{color:var(--text)}.meta{margin-left:.4rem;color:var(--muted);font-size:.74rem}}.mode{border:1px solid var(--border);border-radius:var(--ui-radius, 6px);padding:.55rem .7rem;margin:.55rem 0 .7rem;display:flex;flex-direction:column;gap:.35rem}.mode legend{padding:0 .35rem;font-size:.68rem;color:var(--muted);text-transform:uppercase;letter-spacing:.08em}.mode label{display:flex;align-items:flex-start;gap:.5rem;font-size:.82rem;& em{color:var(--muted);font-style:normal;font-size:.74rem}}.status{margin:.8rem 0 0;padding:.4rem .6rem;border-radius:5px;font-size:.78rem;&.status-ok{border:1px solid var(--accent);background:rgb(from var(--accent) r g b / .08);color:var(--text)}&.status-err{border:1px solid var(--critical, #f87171);background:rgb(from var(--critical, #f87171) r g b / .07);color:var(--text)}}.actions{display:flex;justify-content:flex-end;gap:.4rem;margin-top:.8rem;padding-top:.7rem;border-top:1px solid var(--border)}.actions button{background:transparent;border:1px solid var(--border);color:var(--text);border-radius:var(--ui-radius, 5px);padding:.3rem .8rem;font:inherit;font-size:.78rem;&:hover{border-color:var(--accent);color:var(--accent)}}
|
|
47
|
+
`,wi=class extends C{static styles=[...C.styles,L(Ci)];static properties={parsed:{state:!0},parseError:{state:!0},mode:{state:!0},busy:{state:!0},status:{state:!0}};constructor(){super(),this.parsed=null,this.parseError=null,this.mode=`prefer-current`,this.busy=!1,this.status=null}focusInitial(){this.renderRoot.querySelector(`button.primary`)?.focus()}async _onDownload(){this.busy=!0,this.status=null;try{let{blob:e,filename:t}=await Ve();ut(e,t),this.status={kind:`ok`,text:`Downloaded ${t}`}}catch(e){this.status={kind:`err`,text:`Export failed: ${e.message}`}}finally{this.busy=!1}}async _onFile(e){let t=e.target.files?.[0];if(this.parsed=null,this.parseError=null,this.status=null,t)try{this.parsed=await Ue(t)}catch(e){this.parseError=e.message}}_onMode(e){this.mode=e.target.value}async _onApply(){if(!(!this.parsed||this.busy||We())){this.busy=!0,this.status=null;try{let e=await Ge(this.parsed,this.mode);dt(),this.status=e.repoUrlError?{kind:`err`,text:`Applied ${e.triageEntries} triage entries (${this.mode}), but saving repo URLs failed: ${e.repoUrlError.message}`}:{kind:`ok`,text:`Applied ${e.triageEntries} triage entries and ${e.repoUrls} repo URLs (${this.mode}).`},this.parsed=null;let t=this.renderRoot.querySelector(`input[type="file"]`);t&&(t.value=``)}catch(e){this.status={kind:`err`,text:`Import failed: ${e.message}`}}finally{this.busy=!1}}}render(){return R`<dialog @close="${this._onClose}"><header><h3>Triage backup</h3><p>Bundles every triage entry (markers, triage states, comments, fixes, per-report ignores) and all saved repo URLs into a single gzipped JSON file.</p></header><section class="section"><h4>Export</h4><p>Download the current triage + repo-URL set as a portable backup.</p><button type="button" class="primary" @click="${this._onDownload}" ?disabled="${this.busy}">${this.busy?`Working…`:`Download backup`}</button></section>${We()?z:this._importTemplate()} ${this.status?R`<div class="${`status status-${this.status.kind}`}">${this.status.text}</div>`:z}<footer class="actions"><button type="button" @click="${this._onClose}">Close</button></footer></dialog>`}_importTemplate(){let e=this.parsed;return R`<hr><section class="section"><h4>Import</h4><p>Pick a previously-exported backup. Nothing is applied until you click Apply.</p><input type="file" accept=".gz,application/gzip" @change="${this._onFile}" ?disabled="${this.busy}"> ${this.parseError?R`<div class="error">Failed to parse: ${this.parseError}</div>`:z} ${e?R`<div class="summary">Found <strong>${Object.keys(e.triage).length}</strong> triage entries and <strong>${Object.keys(e.repoUrls).length}</strong> repo URLs. ${e.exportedAt?R`<span class="meta">Exported ${e.exportedAt}.</span>`:z}</div><fieldset class="mode"><legend>Merge mode</legend><label><input type="radio" name="te-mode" value="prefer-current" .checked="${this.mode===`prefer-current`}" @change="${this._onMode}"> <span>Merge — keep current on conflict <em>(safest, only fills gaps)</em></span></label> <label><input type="radio" name="te-mode" value="prefer-imported" .checked="${this.mode===`prefer-imported`}" @change="${this._onMode}"> <span>Merge — prefer imported on conflict <em>(overwrites where both have a value)</em></span></label> <label><input type="radio" name="te-mode" value="replace" .checked="${this.mode===`replace`}" @change="${this._onMode}"> <span>Replace all <em>(drops every current persisted entry first — destructive)</em></span></label></fieldset><button type="button" class="primary" @click="${this._onApply}" ?disabled="${this.busy}">${this.busy?`Applying…`:`Apply import`}</button>`:z}</section>`}};customElements.define(`triage-export-dialog`,wi);function Ti(){return w(`triage-export-dialog`)}var Ei=`dialog{width:min(94vw,var(--graph-dialog-width));max-width:80rem;max-height:90dvh;margin:auto;padding:0;overflow:hidden}dialog[open]{display:flex;flex-direction:column}header{display:flex;align-items:start;justify-content:space-between;gap:1rem;flex-shrink:0;padding:.7rem .9rem;border-bottom:1px solid var(--border)}header h3{display:flex;flex-wrap:wrap;gap:.35rem .7rem;min-width:0;margin:0;font-size:.92rem;overflow-wrap:anywhere}.heading-version{color:var(--accent);font-family:var(--font-mono, monospace)}header button{flex-shrink:0;border:0;background:transparent;color:var(--muted);font-family:inherit;font-size:1.4rem;line-height:1;padding:0 .25rem}header button:hover{color:var(--text)}.graph-caption{display:flex;flex-wrap:wrap;justify-content:space-between;gap:.5rem;padding:.45rem .9rem;font-size:.68rem;color:var(--muted);flex-shrink:0;user-select:none}.graph-scroll{min-height:0;overflow:auto;overscroll-behavior:contain;background:var(--bg, var(--surface));padding:.4rem}.graph{position:relative;margin-inline:auto}.connections{position:absolute;inset:0;overflow:visible;pointer-events:none}.connections>path{fill:none;stroke:var(--connection-color, var(--muted));stroke-width:1.5;opacity:.65}.package-group{position:absolute;cursor:default}.package-group.stacked{background:var(--bg);border-radius:var(--ui-radius, 6px)}.stacked.collapsed{background:linear-gradient(var(--surface),var(--surface)),var(--bg)}.stacked.selected-cycle{background:color-mix(in srgb,var(--accent) 9%,var(--bg))}.stacked:hover,.stacked:focus-within{z-index:1;box-shadow:0 2px 8px #0005}.stacked.dimmed{opacity:1}.stacked.dimmed:after{content:"";position:absolute;inset:0;background:var(--surface);opacity:.8;border-radius:inherit;pointer-events:none}.package{position:absolute;width:var(--package-width);height:var(--package-height);display:flex;flex-direction:column;gap:4px;padding:5px 7px;background:var(--surface);border:1px solid var(--border);border-left:3px solid var(--package-color);border-radius:var(--ui-radius, 6px);font-size:.75rem;overflow:hidden}.package.selected{border-color:var(--accent);background:color-mix(in srgb,var(--accent) 9%,var(--surface))}.name,.location,.version{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.package-main,.package-meta{display:flex;align-items:baseline;gap:.4rem;min-width:0}.name{flex:1;font-weight:600;line-height:17px}.version{max-width:45%;flex-shrink:0;font-family:var(--font-mono, monospace);font-size:.65rem}.badge{flex-shrink:0;font-size:.6rem;color:var(--muted);user-select:none}.selected-badge{color:var(--accent)}.location{flex:1;color:var(--muted);font-family:var(--font-mono, monospace);font-size:.61rem;line-height:14px}.cycle{border-radius:var(--ui-radius, 8px);outline:1px solid var(--border);background:color-mix(in srgb,var(--muted) 4%,transparent)}.cycle-label{position:absolute;top:7px;left:14px;color:var(--muted);font-size:.63rem;user-select:none}.cycle-toggle{display:flex;align-items:center;gap:.4rem;padding:0;border:0;background:transparent;font-family:inherit;line-height:16px}.cycle-toggle:hover{color:var(--text)}.cycle-chevron{font-size:.85rem}.cycle-summary{display:flex;gap:.4rem;text-align:left}.cycle-count{color:var(--muted)}.cycle-count:before{content:"\\b7 "}.collapsed .cycle-toggle{inset:0;width:100%;padding:5px 7px;border-radius:inherit;font-size:.75rem}.collapsed .cycle-summary{flex:1;flex-direction:column;gap:2px}.collapsed .cycle-count{font-size:.65rem}.collapsed .cycle-count:before{content:none}.selected-cycle{outline-color:var(--accent);background:color-mix(in srgb,var(--accent) 9%,var(--surface))}.cycle-connections>path{stroke:var(--connection-color);stroke-width:1.25;opacity:.8}.large-cycle .cycle-connections>path{stroke:var(--muted);opacity:.18}.cycle-connections>path.subdued{opacity:.04}.connections>path.traced{stroke:var(--connection-color);stroke-width:1.75;opacity:1}.package-subdued{opacity:.35}.dimmed,.connections>path.dimmed{opacity:.18}.note,.empty{padding:1rem;color:var(--muted);font-size:.78rem;line-height:1.5}.note{position:sticky;left:0;max-width:65rem;margin:auto}button:focus-visible,.graph-scroll:focus-visible,.package:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}
|
|
48
|
+
`,$=e=>Rt(e.own?`__own__`:e.name),Di=()=>Math.floor(Math.min(document.documentElement.clientWidth*.94,80*Number.parseFloat(getComputedStyle(document.documentElement).fontSize))),Oi=class extends C{static styles=[...C.styles,L(Ei)];static properties={_active:{state:!0},_current:{state:!0},_hovered:{state:!0},_focused:{state:!0},layout:{state:!0}};constructor(){super(),this._active=null,this._hovered=null,this._focused=null}_expandedCycles=new Set;layoutForViewport(){let e=this.renderRoot?.activeElement;this._hovered=null,this._maxWidth=Di(),this.layout=ie(this.graph,{maxWidth:this._maxWidth,expandedCycles:this._expandedCycles}),e?.matches(`.package, .cycle-toggle`)&&this.updateComplete.then(()=>{this._current&&e.isConnected&&(e.focus({preventScroll:!0}),e.scrollIntoView({block:`nearest`,inline:`nearest`}))})}toggleCycle(e){this._expandedCycles.has(e)?this._expandedCycles.delete(e):this._expandedCycles.add(e),this._focused=null,this.layoutForViewport()}connectedCallback(){super.connectedCallback(),this.graph=te(this.details,this),this.layoutForViewport(),this._dispose=S(()=>{this._current=this.isCurrent(),this._current||queueMicrotask(this._onClose)})}firstUpdated(){super.firstUpdated();let e=this.renderRoot.querySelector(`.graph-scroll`);e&&(this._resize=new ResizeObserver(()=>{if(Di()!==this._maxWidth){this.layoutForViewport();return}this.renderRoot.activeElement?.matches(`.package, .cycle-toggle`)||(e.scrollLeft=(this.layout.width-e.clientWidth)/2)}),this._resize.observe(e),this._resize.observe(document.documentElement))}disconnectedCallback(){this._dispose?.(),this._resize?.disconnect(),super.disconnectedCallback()}renderNode({id:e,x:t,y:n},r){let i=this.graph.nodes.get(e),a=[...this.graph.importedBy.get(e)].map(e=>this.graph.nodes.get(e)),o=`${i.version||`Source`}${i.ecosystem&&i.ecosystem!==`npm`?` \xB7 ${i.ecosystem}`:``}`,s=a.length>0?`Imported by ${a.map(e=>`${e.name}${e.version?`@${e.version}`:``}`).join(`, `)}.`:i.root?`Bundle entry point or app source.`:i.traceBoundary||i.excludedImporters?.size?`Dependency tracing stops here.`:`No importer is recorded in this scope.`;return R`<div class="${`package${i.target?` selected`:``}${r&&!r.has(e)?` package-subdued`:``}`}" tabindex="0" role="group" @pointerenter="${()=>{this._hovered=e}}" @pointerleave="${()=>{this._hovered=null}}" @focus="${()=>{this._focused=e}}" @blur="${()=>{this._focused=null}}" aria-label="${`${i.ecosystem?`${i.ecosystem}:`:``}${i.name}${i.version?`@${i.version}`:``}. ${s} Location: ${e}.`}" style="${N({left:`${t}px`,top:`${n}px`,"--package-color":$(i)})}"><div class="package-main"><span class="name" data-tooltip="${i.name}" data-tooltip-truncated>${i.name}</span> <span class="version" data-tooltip="${o}" data-tooltip-truncated>${o}</span></div><div class="package-meta"><span class="location" data-tooltip="${e}" data-tooltip-truncated>${i.own?`App source`:e}</span> ${i.target?R`<span class="badge selected-badge">Selected</span>`:z} ${i.root?R`<span class="badge">${i.own?`App`:`Entry point`}</span>`:z}</div></div>`}renderGraph(){let{boxes:e,edges:t,width:n,height:r}=this.layout,i=this._hovered??this._focused,a=i===null?this._active:this.layout.componentOf.get(i),o=se(this.layout,a),s=i===null?null:new Set([i,...this.graph.imports.get(i),...this.graph.importedBy.get(i)]),c=new Set(t.map(e=>e.to)),l=e.filter(e=>!c.has(e.id)&&!e.members.some(t=>{let n=this.graph.nodes.get(t);return n.root||n.traceBoundary||[...n.excludedImporters??[]].some(t=>this.layout.componentOf.get(t)!==e.id)}));return R`<div class="graph-scroll" tabindex="0" aria-label="Package dependency chains. Arrows point from importer to dependency."><div class="graph" style="${N({width:`${n}px`,height:`${r}px`,"--package-width":`${Tt}px`,"--package-height":`${Ot}px`})}"><svg class="connections" width="${n}" height="${r}" aria-hidden="true"><defs><marker id="dependency-arrow" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10z" fill="context-stroke"/></marker></defs>${t.map(e=>{let t=e.fromPackage===i||e.toPackage===i,n=o&&!o.edges.has(e)||i!==null&&(e.from===a||e.to===a)&&!t;return $t`<path d=${e.path} class=${n?`dimmed`:t?`traced`:``}
|
|
49
|
+
style=${N({"--connection-color":$(this.graph.nodes.get(e.fromPackage))})} marker-end="url(#dependency-arrow)"/>`})}</svg> ${e.map(e=>this.renderGroup(e,o,i,s))}</div>${l.length>0?R`<p class="note">Some chains have no recorded app or entry point. These packages are bundled, but their origin is not captured in this scope.</p>`:z}</div>`}renderGroup(e,t,n,r){let i=e.members.some(e=>this.graph.nodes.get(e).target),a=r&&e.members.some(e=>r.has(e));return R`<div class="${`package-group${e.stacked?` stacked`:``}${e.members.length>1?` cycle`:``}${e.members.length>8?` large-cycle`:``}${e.collapsed?` collapsed`:``}${e.collapsed&&i?` selected-cycle`:``}${t&&!t.groups.has(e.id)?` dimmed`:``}`}" style="${N({left:`${e.x}px`,top:`${e.y}px`,width:`${e.width}px`,height:`${e.height}px`})}" @pointerenter="${()=>{this._active=e.id}}" @pointerleave="${()=>{this._active=null}}">${e.collapsible?R`<button type="button" class="cycle-label cycle-toggle" aria-expanded="${!e.collapsed}" aria-controls="${`cycle-content-${e.id}`}" @click="${()=>this.toggleCycle(e.id)}"><span class="cycle-chevron" aria-hidden="true">${e.collapsed?`▸`:`▾`}</span> <span class="cycle-summary"><span>Circular imports</span><span class="cycle-count">${e.members.length} packages</span></span> ${e.collapsed&&i?R`<span class="badge selected-badge">Selected</span>`:z}</button>`:e.members.length>1?R`<div class="cycle-label">Circular imports${e.members.length>8?` \xB7 ${e.members.length} packages`:``}</div>`:z}<div id="${`cycle-content-${e.id}`}" ?hidden="${e.collapsed}">${e.internalEdges.length>0?R`<svg class="connections cycle-connections" width="${e.width}" height="${e.height}" aria-hidden="true">${e.internalEdges.map(e=>$t`<path d=${e.path}
|
|
50
50
|
class=${a?e.from===n||e.to===n?`traced`:`subdued`:``}
|
|
51
|
-
style=${M({"--connection-color":$(this.graph.nodes.get(e.from))})} marker-end="url(#dependency-arrow)"/>`)}</svg>`:R} ${Vt(e.packages,e=>e.id,e=>this.renderNode(e,a?r:null))}</div></div>`}render(){if(!this._current)return R;let{nodes:e,targets:t}=this.graph,n=this.packageGroup===void 0?this.packageKey:Bt(this.packageGroup);return L`<dialog aria-labelledby="why-title" style="${M({"--graph-dialog-width":`${Math.max(480,this.layout.width+ae)}px`})}" @close="${this._onClose}" @click="${this._onBackdrop}"><header><h3 id="why-title">${n}${this.version===void 0?R:L`<span class="heading-version">${this.version}</span>`}</h3><button type="button" aria-label="Close dependency chains" @click="${this._onClose}">×</button></header><div class="graph-caption"><span>${e.size} ${e.size===1?`package`:`packages`}${t.length>1?` \xB7 ${t.length} installations${this.version===void 0?``:` of this version`}`:``}${this.reason?` \xB7 Scope: ${this.reason}`:``}</span> <span>↓ imports</span></div>${t.length>0?this.renderGraph():L`<p class="empty">Dependency metadata for this ${this.version===void 0?`package`:`version`} is not available in this bundle scope.</p>`}</dialog>`}};customElements.define(`why-dialog`,Oi);function ki(e){return x(`why-dialog`,e)}var Ai=[{id:`workspace`,label:`Export workspace`},{id:`raw`,label:`Raw reports export`}],ji=class extends b{static styles=[...b.styles,I(E),I(Sn)];static properties={workspace:{attribute:!1},_tab:{state:!0},_password:{state:!0},_confirm:{state:!0},_noPassword:{state:!0},_includeBundleBytes:{state:!0},_busy:{state:!0},_error:{state:!0},_settled:{state:!0}};constructor(){super(),this.workspace=null,this._tab=`workspace`,this._password=``,this._confirm=``,this._noPassword=!1,this._includeBundleBytes=!1,this._busy=!1,this._error=``,this._settled=!1}_finish(e){this._settled||(this._password=``,this._confirm=``,this.workspace=null,super._finish(e))}_onClose=()=>this._finish(null);_onCancel=()=>this._finish(null);_reportCount(){return Array.isArray(this.workspace?.reports)?this.workspace.reports.length:0}_bundleCount(){return Array.isArray(this.workspace?.bundles)?this.workspace.bundles.length:0}_selectTab(e){this._busy||this._tab===e||(this._tab=e,this._error=``,e!==`workspace`&&(this._password=``,this._confirm=``))}_onPasswordInput=e=>{this._password=e.target.value,this._error=``};_onConfirmInput=e=>{this._confirm=e.target.value,this._error=``};_onNoPasswordToggle=e=>{this._noPassword=e.target.checked,this._error=``,this._noPassword&&(this._password=``,this._confirm=``)};_onIncludeBundleBytesToggle=e=>{this._includeBundleBytes=e.target.checked,this._error=``};_canExport(){return this._busy?!1:this._tab===`raw`?this._reportCount()>0:this._noPassword?!0:(this._password??``).length>0&&this._password===this._confirm}_onExport=async()=>{if(!this._canExport())return;if(!this.workspace){this._error=`No workspace selected.`;return}let e=this._tab;this._busy=!0,this._error=``;try{let{blob:t,filename:n}=e===`raw`?await He(this.workspace):await ze(this.workspace,{password:this._noPassword?void 0:this._password,includeBundleBytes:this._includeBundleBytes});if(this._settled)return;Oe(t,n),this._finish({ok:!0,tab:e})}catch(e){if(this._settled)return;this._error=e?.message??String(e)}finally{this._busy=!1}};_onKeydown=e=>{e.key===`Enter`&&(e.preventDefault(),this._onExport())};_tabsTemplate(){return Ne({tabs:Ai,current:this._tab,prefix:`wsl`,disabled:this._busy,select:e=>this._selectTab(e)})}_workspacePanel(){let e=!this._password||!this._confirm||this._password===this._confirm,t=this._bundleCount();return L`<p class="nwd-note ui-hint">The export file carries this workspace's reports, triage state, comments, fixes, references to attached bundles (integrities only by default), and its private key. Encrypting the file with a password ensures only those with the password can attach the workspace after download.</p><label class="wsl-field"><span>Password</span> <input type="password" class="nwd-input" autocomplete="off" data-1p-ignore="true" data-lpignore="true" data-bwignore="true" data-form-type="other" name="dv-export-pw" maxlength="1024" ?disabled="${this._noPassword}" .value="${this._password}" @input="${this._onPasswordInput}" @keydown="${this._onKeydown}"></label> <label class="wsl-field"><span>Confirm password</span> <input type="password" class="nwd-input" autocomplete="off" data-1p-ignore="true" data-lpignore="true" data-bwignore="true" data-form-type="other" name="dv-export-pw-confirm" maxlength="1024" ?disabled="${this._noPassword}" .value="${this._confirm}" @input="${this._onConfirmInput}" @keydown="${this._onKeydown}"></label> <label class="wsl-optout"><input type="checkbox" .checked="${this._noPassword}" @change="${this._onNoPasswordToggle}"> <span>Export without password (not recommended)</span></label> ${t>0?L`<label class="wsl-optout"><input type="checkbox" .checked="${this._includeBundleBytes}" @change="${this._onIncludeBundleBytesToggle}"> <span>Include bundle bytes (${t} bundle${t===1?``:`s`}; significantly larger file)</span></label>`:R} ${this._noPassword?L`<p class="wsl-error wsl-warning">Anyone who obtains this file can attach the workspace and read every report, triage decision, comment, and fix. Only opt out when you control where the file goes.</p>`:R} ${!this._noPassword&&!e?L`<p class="wsl-error" role="alert">Passwords don't match.</p>`:R}`}_rawPanel(){let e=this._reportCount(),t=this._bundleCount();return L`<div class="wsl-error wsl-warning"><p>This is the raw form of the reports.</p><p>It carries no triage data — no markers, triage states, comments, fixes, or per-report ignores${t>0?L` — and none of this workspace's ${t} attached bundle${t===1?``:`s`}`:R}.</p><p>This file is written unencrypted: anyone who obtains it can read every report in it.</p></div>${e===0?L`<p class="wsl-error" role="alert">This workspace has no reports to export.</p>`:R}`}_primaryLabel(){return this._busy?`Exporting…`:this._tab===`raw`?`Download reports`:this._noPassword?`Export without password`:`Export`}_primaryDanger(){return this._tab===`raw`||this._noPassword}_body(){return L`<div role="tabpanel" id="${`wsl-panel-${this._tab}`}" aria-labelledby="${`wsl-tab-${this._tab}`}">${this._tab===`raw`?this._rawPanel():this._workspacePanel()}</div>${this._error?L`<p class="wsl-error" role="alert">${this._error}</p>`:R}<footer class="nwd-actions"><span class="nwd-spacer"></span> <button type="button" @click="${this._onCancel}">Cancel</button> <button type="button" class="${this._primaryDanger()?`primary danger`:`primary`}" ?disabled="${!this._canExport()}" @click="${this._onExport}">${this._primaryLabel()}</button></footer>`}render(){return L`<dialog @close="${this._onClose}"><header>${this._tabsTemplate()}</header>${this._body()}</dialog>`}};customElements.define(`workspace-export-dialog`,ji);function Mi({workspace:e}={}){return S(`workspace-export-dialog`,{workspace:e??null},e=>{e.workspace=null})}var Ni=class extends b{static styles=[...b.styles,I(E)];static properties={workspaceId:{type:String},initialName:{type:String},privateKeyBase64:{type:String},_name:{state:!0},_password:{state:!0},_confirm:{state:!0},_url:{state:!0},_busy:{state:!0},_error:{state:!0},_copied:{state:!0},_settled:{state:!0}};constructor(){super(),this.workspaceId=``,this.initialName=``,this.privateKeyBase64=``,this._name=``,this._password=``,this._confirm=``,this._url=``,this._busy=!1,this._error=``,this._copied=!1,this._settled=!1}beforeOpen(){this._name=this.initialName}focusInitial(){this.renderRoot.querySelector(`input[data-role="name"]`)?.focus()}_finish(){this._settled||(this._copiedTimer&&=(clearTimeout(this._copiedTimer),null),this._password=``,this._confirm=``,this._url=``,this.privateKeyBase64=``,super._finish(null))}_onClose=()=>this._finish();_onCancel=()=>this._finish();_onNameInput=e=>{this._name=e.target.value};_onPasswordInput=e=>{this._password=e.target.value,this._error=``};_onConfirmInput=e=>{this._confirm=e.target.value,this._error=``};_canGenerate(){return(this._name??``).trim().length>0&&(this._password??``).length>0&&this._password===this._confirm&&!this._busy}_onGenerate=async()=>{if(this._canGenerate()){this._busy=!0,this._error=``;try{let e=await Ge({id:this.workspaceId,name:this._name.trim(),privateKeyBase64:this.privateKeyBase64,password:this._password});if(this._settled)return;this._url=Ye(e),this._password=``,this._confirm=``}catch(e){if(this._settled)return;this._error=e?.message??String(e)}finally{this._busy=!1}}};_onCopy=async()=>{if(this._url){this._error=``;try{if(await navigator.clipboard.writeText(this._url),this._settled)return;this._copied=!0,this._copiedTimer&&clearTimeout(this._copiedTimer),this._copiedTimer=setTimeout(()=>{this._copied=!1,this._copiedTimer=null},1500)}catch{if(this._settled)return;this._error=`Copy failed — select the link and copy it manually.`}}};_onKeydown=e=>{e.key===`Enter`&&!this._url&&(e.preventDefault(),this._onGenerate())};_body(){return this._url?L`<p class="nwd-note ui-hint">Anyone with the link <strong>and</strong> the password can attach this workspace. Share them through separate channels.</p><label class="wsl-field"><span>Share link</span> <textarea class="wsl-link" readonly="readonly" rows="3" @focus="${e=>e.target.select()}">${this._url}</textarea></label> ${this._error?L`<p class="wsl-error" role="alert">${this._error}</p>`:R}<footer class="nwd-actions"><span class="nwd-spacer"></span> <button type="button" @click="${this._onCancel}">Close</button> <button type="button" class="primary" @click="${this._onCopy}">${this._copied?`Copied`:`Copy link`}</button></footer>`:L`<label class="wsl-field"><span>Workspace name</span> <input type="text" class="nwd-input" data-role="name" placeholder="Workspace name" maxlength="200" .value="${this._name}" @input="${this._onNameInput}" @keydown="${this._onKeydown}"></label> <label class="wsl-field"><span>Password</span> <input type="password" class="nwd-input" autocomplete="off" data-1p-ignore="true" data-lpignore="true" data-bwignore="true" data-form-type="other" name="dv-share-link-pw" maxlength="1024" .value="${this._password}" @input="${this._onPasswordInput}" @keydown="${this._onKeydown}"></label> <label class="wsl-field"><span>Confirm password</span> <input type="password" class="nwd-input" autocomplete="off" data-1p-ignore="true" data-lpignore="true" data-bwignore="true" data-form-type="other" name="dv-share-link-pw-confirm" maxlength="1024" .value="${this._confirm}" @input="${this._onConfirmInput}" @keydown="${this._onKeydown}"></label><p class="nwd-note ui-hint">The link carries the workspace's identity (id, name, and private key) so the recipient joins the same sync chain. Triage, reports, and comments aren't in the link itself — they sync over once the recipient joins. The link decrypts with the password above; without it the link is useless.</p>${this._error?L`<p class="wsl-error" role="alert">${this._error}</p>`:R} ${this._password&&this._confirm&&this._password!==this._confirm?L`<p class="wsl-error" role="alert">Passwords don't match.</p>`:R}<footer class="nwd-actions"><span class="nwd-spacer"></span> <button type="button" @click="${this._onCancel}">Cancel</button> <button type="button" class="primary" ?disabled="${!this._canGenerate()}" @click="${this._onGenerate}">${this._busy?`Generating…`:`Generate link`}</button></footer>`}render(){return L`<dialog @close="${this._onClose}"><header><h3>${this._url?`Workspace share link`:`Share workspace by link`}</h3></header>${this._body()}</dialog>`}};customElements.define(`workspace-share-link-dialog`,Ni);function Pi({id:e,name:t,privateKeyBase64:n}={}){return new Promise((r,i)=>{let a=document.createElement(`workspace-share-link-dialog`);a.workspaceId=e??``,a.initialName=t??``,a.privateKeyBase64=n??``,a.addEventListener(`resolve`,()=>{a.remove(),r()}),a.addEventListener(`modal-conflict`,e=>{a.privateKeyBase64=``,a.remove(),i(Ae(e.detail?.cause))}),document.body.append(a)})}var Fi=class extends b{static styles=[...b.styles,I(E)];static properties={_password:{state:!0},_busy:{state:!0},_error:{state:!0},_settled:{state:!0}};constructor(){super(),this._password=``,this._busy=!1,this._error=``,this._settled=!1,this._tryPassword=null}_finish(e){this._settled||(this._password=``,this._tryPassword=null,super._finish(e))}_onClose=()=>this._finish(null);_onCancel=()=>this._finish(null);_onPasswordInput=e=>{this._password=e.target.value,this._error=``};_onUnlock=async()=>{if(this._password&&!this._busy){this._busy=!0,this._error=``;try{let e=await this._tryPassword(this._password);if(this._settled)return;this._finish(e)}catch(e){if(this._settled)return;this._error=e?.message??String(e)}finally{this._busy=!1}}};_onKeydown=e=>{e.key===`Enter`&&(e.preventDefault(),this._onUnlock())};render(){return L`<dialog @close="${this._onClose}"><header><h3>Unlock encrypted workspace</h3></header><p class="nwd-note ui-hint">This workspace bundle is password-protected. Enter the password the sender shared with you separately to decrypt and import it.</p><label class="wsl-field"><span>Password</span> <input type="password" class="nwd-input" autocomplete="off" data-1p-ignore="true" data-lpignore="true" data-bwignore="true" data-form-type="other" name="dv-bundle-unlock" maxlength="1024" .value="${this._password}" @input="${this._onPasswordInput}" @keydown="${this._onKeydown}"></label> ${this._error?L`<p class="wsl-error" role="alert">${this._error}</p>`:R}<footer class="nwd-actions"><span class="nwd-spacer"></span> <button type="button" @click="${this._onCancel}">Cancel</button> <button type="button" class="primary" ?disabled="${!this._password||this._busy}" @click="${this._onUnlock}">${this._busy?`Unlocking…`:`Unlock`}</button></footer></dialog>`}};customElements.define(`workspace-unlock-bundle-dialog`,Fi);function Ii({tryPassword:e}={}){if(typeof e!=`function`)throw TypeError(`openWorkspaceUnlockBundleDialog: tryPassword required`);return S(`workspace-unlock-bundle-dialog`,{_tryPassword:e},e=>{e._tryPassword=null})}var Li=class extends b{static styles=[...b.styles,I(E)];static properties={encoded:{type:String},_stage:{state:!0},_password:{state:!0},_name:{state:!0},_decoded:{state:!0},_existingById:{state:!0},_busy:{state:!0},_error:{state:!0},_settled:{state:!0}};constructor(){super(),this.encoded=``,this._stage=`password`,this._password=``,this._name=``,this._decoded=null,this._existingById=null,this._busy=!1,this._error=``,this._settled=!1}focusInitial(){this._focusActiveInput()}updated(e){e.has(`_stage`)&&this._focusActiveInput()}_focusActiveInput(){if(this._stage===`password`)this.renderRoot.querySelector(`input[type="password"]`)?.focus();else if(this._stage===`name`){let e=this.renderRoot.querySelector(`input[data-role="name"]`);e&&(e.focus(),e.select())}}_finish(e){this._settled||(this._decoded=null,this._password=``,this.encoded=``,super._finish(e))}_onClose=()=>this._finish(null);_onCancel=()=>this._finish(null);_onPasswordInput=e=>{this._password=e.target.value,this._error=``};_onNameInput=e=>{this._name=e.target.value};_onUnlock=async()=>{if(this._password&&!this._busy){this._busy=!0,this._error=``;try{let e=await qe({encoded:this.encoded,password:this._password});if(this._settled)return;let t=_().find(t=>t.id===e.id)??null;this._decoded=e,this._existingById=t,this._name=e.name,this._password=``,this._stage=`name`}catch(e){if(this._settled)return;this._error=e?.message??String(e)}finally{this._busy=!1}}};_onAttach=()=>{if(!this._decoded)return;let e=v(this._name);if(!e)return;let t=_().find(e=>e.id===this._decoded.id)??null;if(t){this._existingById=t;return}this._nameCollision(e)||this._finish({id:this._decoded.id,name:e,privateKeyBase64:this._decoded.privateKeyBase64})};_nameCollision(e){if(!e||!this._decoded)return null;for(let t of _())if(t.id!==this._decoded.id&&v(t.name)===e)return t;return null}_onKeydown=e=>{e.key===`Enter`&&(e.preventDefault(),this._stage===`password`?this._onUnlock():this._stage===`name`&&this._onAttach())};_passwordStage(){return L`<p class="nwd-note ui-hint">Someone shared a workspace with you. Enter the password they sent separately to attach it to this browser.</p><label class="wsl-field"><span>Password</span> <input type="password" class="nwd-input" autocomplete="off" data-1p-ignore="true" data-lpignore="true" data-bwignore="true" data-form-type="other" name="dv-share-link-unlock" maxlength="1024" .value="${this._password}" @input="${this._onPasswordInput}" @keydown="${this._onKeydown}"></label> ${this._error?L`<p class="wsl-error" role="alert">${this._error}</p>`:R}<footer class="nwd-actions"><span class="nwd-spacer"></span> <button type="button" @click="${this._onCancel}">Cancel</button> <button type="button" class="primary" ?disabled="${!this._password||this._busy}" @click="${this._onUnlock}">${this._busy?`Unlocking…`:`Unlock`}</button></footer>`}_alreadyAttachedStage(){let e=this._existingById;return L`<p class="nwd-note">This workspace is already attached as <strong>"${e?.name??``}"</strong>. Open it from the sidebar.</p><footer class="nwd-actions"><span class="nwd-spacer"></span> <button type="button" class="primary" @click="${this._onCancel}">Close</button></footer>`}_nameStage(){if(this._existingById)return this._alreadyAttachedStage();let e=v(this._name),t=this._nameCollision(e),n=!!e&&!t;return L`<p class="nwd-note ui-hint">Pick a name for the workspace on this device. The shared identity is preserved either way — only the visible label changes.</p><label class="wsl-field"><span>Workspace name</span> <input type="text" class="nwd-input" data-role="name" placeholder="Workspace name" maxlength="200" .value="${this._name}" @input="${this._onNameInput}" @keydown="${this._onKeydown}"></label> ${t?L`<p class="wsl-error" role="alert">A workspace named "${e}" already exists on this device. Pick a different name.</p>`:R}<footer class="nwd-actions"><span class="nwd-spacer"></span> <button type="button" @click="${this._onCancel}">Cancel</button> <button type="button" class="primary" ?disabled="${!n}" @click="${this._onAttach}">Attach</button></footer>`}render(){let e=this._stage===`password`?`Attach shared workspace`:this._existingById?`Workspace already attached`:`Name the workspace`;return L`<dialog @close="${this._onClose}"><header><h3>${e}</h3></header>${this._stage===`password`?this._passwordStage():this._nameStage()}</dialog>`}};customElements.define(`workspace-unlock-link-dialog`,Li);function Ri({encoded:e}={}){return S(`workspace-unlock-link-dialog`,{encoded:e??``},e=>{e.encoded=``})}export{an as openAdvisoryDetailsDialog,cn as openCommentDialog,un as openDeleteBundleDialog,fn as openDeleteReportDialog,mn as openDetachBundleDialog,gn as openDetachReportDialog,vn as openDiscardReviewDialog,bn as openDownloadBundleDialog,Tn as openExportConfirmDialog,Rn as openExportViewDialog,Gn as openFindingHistoryDialog,Jn as openFindingSourceDialog,Zn as openFixLinkDialog,$n as openImportConflictDialog,nr as openLeaveWorkspaceDialog,ar as openLocalContentImportDialog,sr as openLocalTriageImportDialog,ur as openManagedCommentsDialog,br as openManagedIssueDialog,Cr as openManagedShareDialog,Tr as openNewWorkspaceDialog,kr as openNpmCompareAdvisoriesDialog,Lr as openObjstoreRecoveryDialog,zr as openPasskeySetupDialog,Vr as openPersistenceDegradedDialog,Yr as openReportCompareDialog,ni as openRepositoryItemDialog,oi as openRevalidationConflictsDialog,li as openStoragePersistDialog,jr as openSyncDownloadDialog,pi as openSyncSuggestDialog,hi as openSyncUploadDialog,Si as openTriageComparisonDialog,Ti as openTriageExportDialog,ki as openWhyDialog,Mi as openWorkspaceExportDialog,Pi as openWorkspaceShareLinkDialog,Ii as openWorkspaceUnlockBundleDialog,Ri as openWorkspaceUnlockLinkDialog};
|
|
51
|
+
style=${N({"--connection-color":$(this.graph.nodes.get(e.from))})} marker-end="url(#dependency-arrow)"/>`)}</svg>`:z} ${Bt(e.packages,e=>e.id,e=>this.renderNode(e,a?r:null))}</div></div>`}render(){if(!this._current)return z;let{nodes:e,targets:t}=this.graph,n=this.packageGroup===void 0?this.packageKey:zt(this.packageGroup);return R`<dialog aria-labelledby="why-title" style="${N({"--graph-dialog-width":`${Math.max(480,this.layout.width+ee)}px`})}" @close="${this._onClose}" @click="${this._onBackdrop}"><header><h3 id="why-title">${n}${this.version===void 0?z:R`<span class="heading-version">${this.version}</span>`}</h3><button type="button" aria-label="Close dependency chains" @click="${this._onClose}">×</button></header><div class="graph-caption"><span>${e.size} ${e.size===1?`package`:`packages`}${t.length>1?` \xB7 ${t.length} installations${this.version===void 0?``:` of this version`}`:``}${this.reason?` \xB7 Scope: ${this.reason}`:``}</span> <span>↓ imports</span></div>${t.length>0?this.renderGraph():R`<p class="empty">Dependency metadata for this ${this.version===void 0?`package`:`version`} is not available in this bundle scope.</p>`}</dialog>`}};customElements.define(`why-dialog`,Oi);function ki(e){return w(`why-dialog`,e)}var Ai=[{id:`workspace`,label:`Export workspace`},{id:`raw`,label:`Raw reports export`}],ji=class extends C{static styles=[...C.styles,L(x),L(xn)];static properties={workspace:{attribute:!1},_tab:{state:!0},_password:{state:!0},_confirm:{state:!0},_noPassword:{state:!0},_includeBundleBytes:{state:!0},_busy:{state:!0},_error:{state:!0},_settled:{state:!0}};constructor(){super(),this.workspace=null,this._tab=`workspace`,this._password=``,this._confirm=``,this._noPassword=!1,this._includeBundleBytes=!1,this._busy=!1,this._error=``,this._settled=!1}_finish(e){this._settled||(this._password=``,this._confirm=``,this.workspace=null,super._finish(e))}_onClose=()=>this._finish(null);_onCancel=()=>this._finish(null);_reportCount(){return Array.isArray(this.workspace?.reports)?this.workspace.reports.length:0}_bundleCount(){return Array.isArray(this.workspace?.bundles)?this.workspace.bundles.length:0}_selectTab(e){this._busy||this._tab===e||(this._tab=e,this._error=``,e!==`workspace`&&(this._password=``,this._confirm=``))}_onPasswordInput=e=>{this._password=e.target.value,this._error=``};_onConfirmInput=e=>{this._confirm=e.target.value,this._error=``};_onNoPasswordToggle=e=>{this._noPassword=e.target.checked,this._error=``,this._noPassword&&(this._password=``,this._confirm=``)};_onIncludeBundleBytesToggle=e=>{this._includeBundleBytes=e.target.checked,this._error=``};_canExport(){return this._busy?!1:this._tab===`raw`?this._reportCount()>0:this._noPassword?!0:(this._password??``).length>0&&this._password===this._confirm}_onExport=async()=>{if(!this._canExport())return;if(!this.workspace){this._error=`No workspace selected.`;return}let e=this._tab;this._busy=!0,this._error=``;try{let{blob:t,filename:n}=e===`raw`?await $e(this.workspace):await Ye(this.workspace,{password:this._noPassword?void 0:this._password,includeBundleBytes:this._includeBundleBytes});if(this._settled)return;ut(t,n),this._finish({ok:!0,tab:e})}catch(e){if(this._settled)return;this._error=e?.message??String(e)}finally{this._busy=!1}};_onKeydown=e=>{e.key===`Enter`&&(e.preventDefault(),this._onExport())};_tabsTemplate(){return ht({tabs:Ai,current:this._tab,prefix:`wsl`,disabled:this._busy,select:e=>this._selectTab(e)})}_workspacePanel(){let e=!this._password||!this._confirm||this._password===this._confirm,t=this._bundleCount();return R`<p class="nwd-note ui-hint">The export file carries this workspace's reports, triage state, comments, fixes, references to attached bundles (integrities only by default), and its private key. Encrypting the file with a password ensures only those with the password can attach the workspace after download.</p><label class="wsl-field"><span>Password</span> <input type="password" class="nwd-input" autocomplete="off" data-1p-ignore="true" data-lpignore="true" data-bwignore="true" data-form-type="other" name="dv-export-pw" maxlength="1024" ?disabled="${this._noPassword}" .value="${this._password}" @input="${this._onPasswordInput}" @keydown="${this._onKeydown}"></label> <label class="wsl-field"><span>Confirm password</span> <input type="password" class="nwd-input" autocomplete="off" data-1p-ignore="true" data-lpignore="true" data-bwignore="true" data-form-type="other" name="dv-export-pw-confirm" maxlength="1024" ?disabled="${this._noPassword}" .value="${this._confirm}" @input="${this._onConfirmInput}" @keydown="${this._onKeydown}"></label> <label class="wsl-optout"><input type="checkbox" .checked="${this._noPassword}" @change="${this._onNoPasswordToggle}"> <span>Export without password (not recommended)</span></label> ${t>0?R`<label class="wsl-optout"><input type="checkbox" .checked="${this._includeBundleBytes}" @change="${this._onIncludeBundleBytesToggle}"> <span>Include bundle bytes (${t} bundle${t===1?``:`s`}; significantly larger file)</span></label>`:z} ${this._noPassword?R`<p class="wsl-error wsl-warning">Anyone who obtains this file can attach the workspace and read every report, triage decision, comment, and fix. Only opt out when you control where the file goes.</p>`:z} ${!this._noPassword&&!e?R`<p class="wsl-error" role="alert">Passwords don't match.</p>`:z}`}_rawPanel(){let e=this._reportCount(),t=this._bundleCount();return R`<div class="wsl-error wsl-warning"><p>This is the raw form of the reports.</p><p>It carries no triage data — no markers, triage states, comments, fixes, or per-report ignores${t>0?R` — and none of this workspace's ${t} attached bundle${t===1?``:`s`}`:z}.</p><p>This file is written unencrypted: anyone who obtains it can read every report in it.</p></div>${e===0?R`<p class="wsl-error" role="alert">This workspace has no reports to export.</p>`:z}`}_primaryLabel(){return this._busy?`Exporting…`:this._tab===`raw`?`Download reports`:this._noPassword?`Export without password`:`Export`}_primaryDanger(){return this._tab===`raw`||this._noPassword}_body(){return R`<div role="tabpanel" id="${`wsl-panel-${this._tab}`}" aria-labelledby="${`wsl-tab-${this._tab}`}">${this._tab===`raw`?this._rawPanel():this._workspacePanel()}</div>${this._error?R`<p class="wsl-error" role="alert">${this._error}</p>`:z}<footer class="nwd-actions"><span class="nwd-spacer"></span> <button type="button" @click="${this._onCancel}">Cancel</button> <button type="button" class="${this._primaryDanger()?`primary danger`:`primary`}" ?disabled="${!this._canExport()}" @click="${this._onExport}">${this._primaryLabel()}</button></footer>`}render(){return R`<dialog @close="${this._onClose}"><header>${this._tabsTemplate()}</header>${this._body()}</dialog>`}};customElements.define(`workspace-export-dialog`,ji);function Mi({workspace:e}={}){return T(`workspace-export-dialog`,{workspace:e??null},e=>{e.workspace=null})}var Ni=class extends C{static styles=[...C.styles,L(x)];static properties={workspaceId:{type:String},initialName:{type:String},privateKeyBase64:{type:String},_name:{state:!0},_password:{state:!0},_confirm:{state:!0},_url:{state:!0},_busy:{state:!0},_error:{state:!0},_copied:{state:!0},_settled:{state:!0}};constructor(){super(),this.workspaceId=``,this.initialName=``,this.privateKeyBase64=``,this._name=``,this._password=``,this._confirm=``,this._url=``,this._busy=!1,this._error=``,this._copied=!1,this._settled=!1}beforeOpen(){this._name=this.initialName}focusInitial(){this.renderRoot.querySelector(`input[data-role="name"]`)?.focus()}_finish(){this._settled||(this._copiedTimer&&=(clearTimeout(this._copiedTimer),null),this._password=``,this._confirm=``,this._url=``,this.privateKeyBase64=``,super._finish(null))}_onClose=()=>this._finish();_onCancel=()=>this._finish();_onNameInput=e=>{this._name=e.target.value};_onPasswordInput=e=>{this._password=e.target.value,this._error=``};_onConfirmInput=e=>{this._confirm=e.target.value,this._error=``};_canGenerate(){return(this._name??``).trim().length>0&&(this._password??``).length>0&&this._password===this._confirm&&!this._busy}_onGenerate=async()=>{if(this._canGenerate()){this._busy=!0,this._error=``;try{let e=await rt({id:this.workspaceId,name:this._name.trim(),privateKeyBase64:this.privateKeyBase64,password:this._password});if(this._settled)return;this._url=ct(e),this._password=``,this._confirm=``}catch(e){if(this._settled)return;this._error=e?.message??String(e)}finally{this._busy=!1}}};_onCopy=async()=>{if(this._url){this._error=``;try{if(await navigator.clipboard.writeText(this._url),this._settled)return;this._copied=!0,this._copiedTimer&&clearTimeout(this._copiedTimer),this._copiedTimer=setTimeout(()=>{this._copied=!1,this._copiedTimer=null},1500)}catch{if(this._settled)return;this._error=`Copy failed — select the link and copy it manually.`}}};_onKeydown=e=>{e.key===`Enter`&&!this._url&&(e.preventDefault(),this._onGenerate())};_body(){return this._url?R`<p class="nwd-note ui-hint">Anyone with the link <strong>and</strong> the password can attach this workspace. Share them through separate channels.</p><label class="wsl-field"><span>Share link</span> <textarea class="wsl-link" readonly="readonly" rows="3" @focus="${e=>e.target.select()}">${this._url}</textarea></label> ${this._error?R`<p class="wsl-error" role="alert">${this._error}</p>`:z}<footer class="nwd-actions"><span class="nwd-spacer"></span> <button type="button" @click="${this._onCancel}">Close</button> <button type="button" class="primary" @click="${this._onCopy}">${this._copied?`Copied`:`Copy link`}</button></footer>`:R`<label class="wsl-field"><span>Workspace name</span> <input type="text" class="nwd-input" data-role="name" placeholder="Workspace name" maxlength="200" .value="${this._name}" @input="${this._onNameInput}" @keydown="${this._onKeydown}"></label> <label class="wsl-field"><span>Password</span> <input type="password" class="nwd-input" autocomplete="off" data-1p-ignore="true" data-lpignore="true" data-bwignore="true" data-form-type="other" name="dv-share-link-pw" maxlength="1024" .value="${this._password}" @input="${this._onPasswordInput}" @keydown="${this._onKeydown}"></label> <label class="wsl-field"><span>Confirm password</span> <input type="password" class="nwd-input" autocomplete="off" data-1p-ignore="true" data-lpignore="true" data-bwignore="true" data-form-type="other" name="dv-share-link-pw-confirm" maxlength="1024" .value="${this._confirm}" @input="${this._onConfirmInput}" @keydown="${this._onKeydown}"></label><p class="nwd-note ui-hint">The link carries the workspace's identity (id, name, and private key) so the recipient joins the same sync chain. Triage, reports, and comments aren't in the link itself — they sync over once the recipient joins. The link decrypts with the password above; without it the link is useless.</p>${this._error?R`<p class="wsl-error" role="alert">${this._error}</p>`:z} ${this._password&&this._confirm&&this._password!==this._confirm?R`<p class="wsl-error" role="alert">Passwords don't match.</p>`:z}<footer class="nwd-actions"><span class="nwd-spacer"></span> <button type="button" @click="${this._onCancel}">Cancel</button> <button type="button" class="primary" ?disabled="${!this._canGenerate()}" @click="${this._onGenerate}">${this._busy?`Generating…`:`Generate link`}</button></footer>`}render(){return R`<dialog @close="${this._onClose}"><header><h3>${this._url?`Workspace share link`:`Share workspace by link`}</h3></header>${this._body()}</dialog>`}};customElements.define(`workspace-share-link-dialog`,Ni);function Pi({id:e,name:t,privateKeyBase64:n}={}){return new Promise((r,i)=>{let a=document.createElement(`workspace-share-link-dialog`);a.workspaceId=e??``,a.initialName=t??``,a.privateKeyBase64=n??``,a.addEventListener(`resolve`,()=>{a.remove(),r()}),a.addEventListener(`modal-conflict`,e=>{a.privateKeyBase64=``,a.remove(),i(pt(e.detail?.cause))}),document.body.append(a)})}var Fi=class extends C{static styles=[...C.styles,L(x)];static properties={_password:{state:!0},_busy:{state:!0},_error:{state:!0},_settled:{state:!0}};constructor(){super(),this._password=``,this._busy=!1,this._error=``,this._settled=!1,this._tryPassword=null}_finish(e){this._settled||(this._password=``,this._tryPassword=null,super._finish(e))}_onClose=()=>this._finish(null);_onCancel=()=>this._finish(null);_onPasswordInput=e=>{this._password=e.target.value,this._error=``};_onUnlock=async()=>{if(this._password&&!this._busy){this._busy=!0,this._error=``;try{let e=await this._tryPassword(this._password);if(this._settled)return;this._finish(e)}catch(e){if(this._settled)return;this._error=e?.message??String(e)}finally{this._busy=!1}}};_onKeydown=e=>{e.key===`Enter`&&(e.preventDefault(),this._onUnlock())};render(){return R`<dialog @close="${this._onClose}"><header><h3>Unlock encrypted workspace</h3></header><p class="nwd-note ui-hint">This workspace bundle is password-protected. Enter the password the sender shared with you separately to decrypt and import it.</p><label class="wsl-field"><span>Password</span> <input type="password" class="nwd-input" autocomplete="off" data-1p-ignore="true" data-lpignore="true" data-bwignore="true" data-form-type="other" name="dv-bundle-unlock" maxlength="1024" .value="${this._password}" @input="${this._onPasswordInput}" @keydown="${this._onKeydown}"></label> ${this._error?R`<p class="wsl-error" role="alert">${this._error}</p>`:z}<footer class="nwd-actions"><span class="nwd-spacer"></span> <button type="button" @click="${this._onCancel}">Cancel</button> <button type="button" class="primary" ?disabled="${!this._password||this._busy}" @click="${this._onUnlock}">${this._busy?`Unlocking…`:`Unlock`}</button></footer></dialog>`}};customElements.define(`workspace-unlock-bundle-dialog`,Fi);function Ii({tryPassword:e}={}){if(typeof e!=`function`)throw TypeError(`openWorkspaceUnlockBundleDialog: tryPassword required`);return T(`workspace-unlock-bundle-dialog`,{_tryPassword:e},e=>{e._tryPassword=null})}var Li=class extends C{static styles=[...C.styles,L(x)];static properties={encoded:{type:String},_stage:{state:!0},_password:{state:!0},_name:{state:!0},_decoded:{state:!0},_existingById:{state:!0},_busy:{state:!0},_error:{state:!0},_settled:{state:!0}};constructor(){super(),this.encoded=``,this._stage=`password`,this._password=``,this._name=``,this._decoded=null,this._existingById=null,this._busy=!1,this._error=``,this._settled=!1}focusInitial(){this._focusActiveInput()}updated(e){e.has(`_stage`)&&this._focusActiveInput()}_focusActiveInput(){if(this._stage===`password`)this.renderRoot.querySelector(`input[type="password"]`)?.focus();else if(this._stage===`name`){let e=this.renderRoot.querySelector(`input[data-role="name"]`);e&&(e.focus(),e.select())}}_finish(e){this._settled||(this._decoded=null,this._password=``,this.encoded=``,super._finish(e))}_onClose=()=>this._finish(null);_onCancel=()=>this._finish(null);_onPasswordInput=e=>{this._password=e.target.value,this._error=``};_onNameInput=e=>{this._name=e.target.value};_onUnlock=async()=>{if(this._password&&!this._busy){this._busy=!0,this._error=``;try{let e=await ot({encoded:this.encoded,password:this._password});if(this._settled)return;let t=_().find(t=>t.id===e.id)??null;this._decoded=e,this._existingById=t,this._name=e.name,this._password=``,this._stage=`name`}catch(e){if(this._settled)return;this._error=e?.message??String(e)}finally{this._busy=!1}}};_onAttach=()=>{if(!this._decoded)return;let e=v(this._name);if(!e)return;let t=_().find(e=>e.id===this._decoded.id)??null;if(t){this._existingById=t;return}this._nameCollision(e)||this._finish({id:this._decoded.id,name:e,privateKeyBase64:this._decoded.privateKeyBase64})};_nameCollision(e){if(!e||!this._decoded)return null;for(let t of _())if(t.id!==this._decoded.id&&v(t.name)===e)return t;return null}_onKeydown=e=>{e.key===`Enter`&&(e.preventDefault(),this._stage===`password`?this._onUnlock():this._stage===`name`&&this._onAttach())};_passwordStage(){return R`<p class="nwd-note ui-hint">Someone shared a workspace with you. Enter the password they sent separately to attach it to this browser.</p><label class="wsl-field"><span>Password</span> <input type="password" class="nwd-input" autocomplete="off" data-1p-ignore="true" data-lpignore="true" data-bwignore="true" data-form-type="other" name="dv-share-link-unlock" maxlength="1024" .value="${this._password}" @input="${this._onPasswordInput}" @keydown="${this._onKeydown}"></label> ${this._error?R`<p class="wsl-error" role="alert">${this._error}</p>`:z}<footer class="nwd-actions"><span class="nwd-spacer"></span> <button type="button" @click="${this._onCancel}">Cancel</button> <button type="button" class="primary" ?disabled="${!this._password||this._busy}" @click="${this._onUnlock}">${this._busy?`Unlocking…`:`Unlock`}</button></footer>`}_alreadyAttachedStage(){let e=this._existingById;return R`<p class="nwd-note">This workspace is already attached as <strong>"${e?.name??``}"</strong>. Open it from the sidebar.</p><footer class="nwd-actions"><span class="nwd-spacer"></span> <button type="button" class="primary" @click="${this._onCancel}">Close</button></footer>`}_nameStage(){if(this._existingById)return this._alreadyAttachedStage();let e=v(this._name),t=this._nameCollision(e),n=!!e&&!t;return R`<p class="nwd-note ui-hint">Pick a name for the workspace on this device. The shared identity is preserved either way — only the visible label changes.</p><label class="wsl-field"><span>Workspace name</span> <input type="text" class="nwd-input" data-role="name" placeholder="Workspace name" maxlength="200" .value="${this._name}" @input="${this._onNameInput}" @keydown="${this._onKeydown}"></label> ${t?R`<p class="wsl-error" role="alert">A workspace named "${e}" already exists on this device. Pick a different name.</p>`:z}<footer class="nwd-actions"><span class="nwd-spacer"></span> <button type="button" @click="${this._onCancel}">Cancel</button> <button type="button" class="primary" ?disabled="${!n}" @click="${this._onAttach}">Attach</button></footer>`}render(){let e=this._stage===`password`?`Attach shared workspace`:this._existingById?`Workspace already attached`:`Name the workspace`;return R`<dialog @close="${this._onClose}"><header><h3>${e}</h3></header>${this._stage===`password`?this._passwordStage():this._nameStage()}</dialog>`}};customElements.define(`workspace-unlock-link-dialog`,Li);function Ri({encoded:e}={}){return T(`workspace-unlock-link-dialog`,{encoded:e??``},e=>{e.encoded=``})}export{rn as openAdvisoryDetailsDialog,sn as openCommentDialog,ln as openDeleteBundleDialog,dn as openDeleteReportDialog,pn as openDetachBundleDialog,hn as openDetachReportDialog,_n as openDiscardReviewDialog,yn as openDownloadBundleDialog,wn as openExportConfirmDialog,Ln as openExportViewDialog,Wn as openFindingHistoryDialog,qn as openFindingSourceDialog,Xn as openFixLinkDialog,Qn as openImportConflictDialog,tr as openLeaveWorkspaceDialog,ir as openLocalContentImportDialog,or as openLocalTriageImportDialog,lr as openManagedCommentsDialog,yr as openManagedIssueDialog,Sr as openManagedShareDialog,wr as openNewWorkspaceDialog,Or as openNpmCompareAdvisoriesDialog,Ir as openObjstoreRecoveryDialog,Rr as openPasskeySetupDialog,Br as openPersistenceDegradedDialog,Jr as openReportCompareDialog,ni as openRepositoryItemDialog,oi as openRevalidationConflictsDialog,li as openStoragePersistDialog,Ar as openSyncDownloadDialog,pi as openSyncSuggestDialog,hi as openSyncUploadDialog,Si as openTriageComparisonDialog,Ti as openTriageExportDialog,ki as openWhyDialog,Mi as openWorkspaceExportDialog,Pi as openWorkspaceShareLinkDialog,Ii as openWorkspaceUnlockBundleDialog,Ri as openWorkspaceUnlockLinkDialog};
|