jbrowse-plugin-msaview 5.0.0 → 5.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunks/{LaunchMsaViewDialog-LMZOJ7T6.js → LaunchMsaViewDialog-WBXEEHJL.js} +2 -2
- package/dist/chunks/chunk-DECSTFAT.js +2 -0
- package/dist/chunks/chunk-DECSTFAT.js.map +7 -0
- package/dist/jbrowse-plugin-msaview.esm.js +2 -2
- package/dist/jbrowse-plugin-msaview.esm.js.map +1 -1
- package/dist/utils/workspaces.js +20 -8
- package/dist/utils/workspaces.test.js +22 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
- package/src/utils/workspaces.test.ts +26 -0
- package/src/utils/workspaces.ts +29 -8
- package/src/version.ts +1 -1
- package/dist/chunks/chunk-VOBS7IVV.js +0 -2
- package/dist/chunks/chunk-VOBS7IVV.js.map +0 -7
- /package/dist/chunks/{LaunchMsaViewDialog-LMZOJ7T6.js.map → LaunchMsaViewDialog-WBXEEHJL.js.map} +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import{a as Ft}from"./chunk-6YFCSBEE.js";import{D as Et,a as G,c as bt,d as de,e as pe,f as fe,g as te,h as vt,i as qe,j as Ee,k as xe,l as or,m as Mt,q as kt,r as ot,s as _e,v as Ve,x as qt}from"./chunk-MHBTV7BZ.js";import{f as ue,g as St,h as Be,j as tt,k as De,l as At,m as Oe,n as Ct,o as Lt,q as je,r as $e,s as Nt,u as Pt,v as It}from"./chunk-PJZ3XTMG.js";import{a as yt,b as Bt,c as rr}from"./chunk-YUKJI2AH.js";import"./chunk-QAC6JSDZ.js";import{a as wt,b as ke,c as xt,d as Tt}from"./chunk-
|
|
1
|
+
import{a as Ft}from"./chunk-6YFCSBEE.js";import{D as Et,a as G,c as bt,d as de,e as pe,f as fe,g as te,h as vt,i as qe,j as Ee,k as xe,l as or,m as Mt,q as kt,r as ot,s as _e,v as Ve,x as qt}from"./chunk-MHBTV7BZ.js";import{f as ue,g as St,h as Be,j as tt,k as De,l as At,m as Oe,n as Ct,o as Lt,q as je,r as $e,s as Nt,u as Pt,v as It}from"./chunk-PJZ3XTMG.js";import{a as yt,b as Bt,c as rr}from"./chunk-YUKJI2AH.js";import"./chunk-QAC6JSDZ.js";import{a as wt,b as ke,c as xt,d as Tt}from"./chunk-DECSTFAT.js";import{a as V}from"./chunk-NSASZ5FM.js";import{a as be,c as Ie,d as gt}from"./chunk-IBBQUV2I.js";import{a as tr}from"./chunk-2IWD4OO4.js";import"./chunk-73KAPCJ2.js";import{a as ve}from"./chunk-CVLB4VFP.js";import{a as we}from"./chunk-DNFD5II2.js";import{a as k}from"./chunk-BX4Z274Q.js";import{a as h}from"./chunk-J6KYLXRD.js";import{a as er,b as a}from"./chunk-FY3BTKY4.js";var Go=er((cs,Vo)=>{Vo.exports=JBrowseExports["@jbrowse/core/data_adapters/dataAdapterCache"]});var L=a(h()),Xo=a(we()),Zo=a(be()),ye=a(k());var K=a(h());var Dt=a(gt(),1),Ot=(0,Ie.createSvgIcon)((0,Dt.jsx)("path",{d:"M19.14 12.94c.04-.3.06-.61.06-.94 0-.32-.02-.64-.07-.94l2.03-1.58c.18-.14.23-.41.12-.61l-1.92-3.32c-.12-.22-.37-.29-.59-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94l-.36-2.54c-.04-.24-.24-.41-.48-.41h-3.84c-.24 0-.43.17-.47.41l-.36 2.54c-.59.24-1.13.57-1.62.94l-2.39-.96c-.22-.08-.47 0-.59.22L2.74 8.87c-.12.21-.08.47.12.61l2.03 1.58c-.05.3-.09.63-.09.94s.02.64.07.94l-2.03 1.58c-.18.14-.23.41-.12.61l1.92 3.32c.12.22.37.29.59.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h3.84c.24 0 .44-.17.47-.41l.36-2.54c.59-.24 1.13-.56 1.62-.94l2.39.96c.22.08.47 0 .59-.22l1.92-3.32c.12-.22.07-.47-.12-.61zM12 15.6c-1.98 0-3.6-1.62-3.6-3.6s1.62-3.6 3.6-3.6 3.6 1.62 3.6 3.6-1.62 3.6-3.6 3.6"}),"Settings");var Ke=a(k()),Co=a(V());var w=a(h());var U=a(k()),fo=a(ve()),ho=a(V());var rt=a(h()),jt=a(k());function nr({children:e,...t}){return rt.default.createElement("div",null,rt.default.createElement(jt.TextField,{...t},e))}var g=nr;var nt=a(h()),$t=a(we()),_t=a(k()),Vt=a(V()),ar=(0,Vt.makeStyles)()({dialogContent:{width:"80em"}});function J({error:e,children:t}){let{classes:o}=ar();return nt.default.createElement(_t.DialogContent,{className:o.dialogContent},e?nt.default.createElement($t.ErrorMessage,{error:e}):null,t)}var at=a(h()),Gt=a(we()),Qt=a(k());function se({status:e}){return e==="loading"?at.default.createElement(Gt.LoadingEllipses,{message:"Translating transcript"}):e==="missing"?at.default.createElement(Qt.Typography,{color:"textSecondary",variant:"body2"},"no coding sequence to align"):null}var me=a(h()),Ut=a(be()),le=a(k());var ie=a(h());var Ht=(0,ie.createContext)(void 0);function Wt({children:e}){let t=(0,ie.useState)(()=>ke()==="splitRight");return ie.default.createElement(Ht.Provider,{value:t},e)}function Ge(){let e=(0,ie.useContext)(Ht),t=(0,ie.useState)(()=>ke()==="splitRight");return e??t}function sr({checked:e,onChange:t}){return me.default.createElement(le.FormControlLabel,{label:"Open beside the genome view",control:me.default.createElement(le.Checkbox,{checked:e,onChange:o=>{t(o.target.checked)}})})}function Y({onSubmit:e,onCancel:t,submitDisabled:o,hint:r,submitLabel:n="Submit",cancelLabel:s="Cancel",model:i}){let[m,u]=Ge(),l=!!i&&wt((0,Ut.getSession)(i));return me.default.createElement(le.DialogActions,{sx:{flexWrap:"wrap",rowGap:1}},l?me.default.createElement(sr,{checked:m,onChange:u}):null,me.default.createElement("div",{style:{display:"flex",alignItems:"center",gap:8,marginLeft:"auto"}},r,me.default.createElement(le.Button,{sx:{flexShrink:0},color:"primary",variant:"contained",disabled:o,onClick:()=>{if(l){let p=m?"splitRight":"stack";xt(p),e(p)}else e(ke())}},n),me.default.createElement(le.Button,{sx:{flexShrink:0},color:"secondary",variant:"contained",onClick:()=>{t()}},s)))}var X=a(h()),Te=a(k()),Jt=a(V());var zt=a(h()),Kt=a(V());var ir=(0,Kt.makeStyles)()({textAreaFont:{fontFamily:"Courier New"}});function st({value:e}){let{classes:t}=ir();return zt.default.createElement(g,{variant:"outlined",multiline:!0,minRows:5,maxRows:10,fullWidth:!0,value:e,slotProps:{input:{readOnly:!0,classes:{input:t.textAreaFont}}}})}var lr=(0,Jt.makeStyles)()({flex:{display:"flex"},minWidth:{minWidth:300},centered:{alignContent:"center",marginLeft:20}});function Z({feature:e,options:t,selectedId:o,selectedTranscript:r,setSelectedId:n,proteinSequence:s,sequenceStatus:i="ready",validIds:m}){let{classes:u}=lr(),[l,p]=(0,X.useState)(!1);return X.default.createElement(X.default.Fragment,null,X.default.createElement("div",{className:u.flex},X.default.createElement(Te.TextField,{variant:"outlined",label:`Choose isoform of ${te(e)}`,helperText:"the query row, so the alignment stays linked to the genome view",select:!0,className:u.minWidth,value:o,onChange:d=>{n(d.target.value)}},t.map(d=>{let c=m?m.some(S=>pe(d,S)):!0,{len:x,mod:b}=bt(d);return X.default.createElement(Te.MenuItem,{value:de(d),key:d.id(),disabled:!c},fe(d)," (",x," aa)"," ",b?" (possible fragment)":"",m?c?" (has data)":" (no data)":"")})),X.default.createElement("div",{className:u.centered},X.default.createElement(Te.Button,{variant:"contained",color:"primary",onClick:()=>{p(!l)}},l?"Hide sequence":"Show sequence"))),l?X.default.createElement(st,{value:s?`>${fe(r)}
|
|
2
2
|
${s}`:i==="missing"?"This transcript has no coding sequence, so there is nothing to translate.":"Loading..."}):null)}var Yt=a(h()),Xt=a(be());var Me={drawNodeBubbles:!0,colWidth:10,rowHeight:12};function Q({view:e,feature:t,placement:o,...r}){return Tt((0,Xt.getSession)(e),{placement:o,connectedViewId:e.id,connectedFeature:t?.toJSON(),...r})}function oe(e){let[t,o]=(0,Yt.useState)();return{launchError:t,submit:r=>{o(void 0),r().then(e,n=>{console.error(n),o(n)})}}}var He=a(h());var Rt=a(be()),eo=a(or());var H=a(h()),Zt=e=>e==null||e===!1;function mr(e){return Zt(e)||Array.isArray(e)&&e.some(Zt)?null:JSON.stringify(e)}function W(e,t,{onSuccess:o}={}){let r=mr(e),[n,s]=(0,H.useState)(0),[i,m]=(0,H.useState)(),u=r===null?null:`${n}\0${r}`,l=i?.attempt===u,p=i?.question===r,d=(0,H.useRef)({fetcher:t,onSuccess:o});return(0,H.useEffect)(()=>{d.current={fetcher:t,onSuccess:o}}),(0,H.useEffect)(()=>{if(u===null||r===null)return;let c=new AbortController;return d.current.fetcher(c.signal).then(x=>{c.signal.aborted||(m({attempt:u,question:r,data:x}),d.current.onSuccess?.(x))}).catch(x=>{c.signal.aborted||m({attempt:u,question:r,error:x})}),()=>{c.abort()}},[u,r]),{data:p?i.data:void 0,error:l?i.error:void 0,isLoading:u!==null&&!l,mutate:(0,H.useCallback)(()=>{s(c=>c+1)},[])}}function Qe(e,t){let[o,r]=(0,H.useState)(e);return(0,H.useEffect)(()=>{let n=setTimeout(()=>{r(e)},t);return()=>{clearTimeout(n)}},[e,t]),o}function to({view:e,feature:t}){let o=e?.assemblyNames?.[0],{data:r,error:n,isLoading:s}=W(t&&o?[t.id(),o,"feature-sequence"]:null,async()=>{let{start:i,end:m,refName:u}=t.toJSON();return Mt({start:i,end:m,refName:u,assemblyName:o,session:(0,Rt.getSession)(e)})});return{proteinSequence:r&&t?(0,eo.translateTranscript)({transcript:t,...r})?.protein??"":"",sequence:r,error:n,isLoading:s}}function cr(e,t,o){if(!o?.length)return e;let r=i=>o.some(m=>pe(i,m)),n=t.find(i=>de(i)===e);if(n&&r(n))return e;let s=t.find(r);return s?de(s):e}function ur({error:e,isLoading:t,proteinSequence:o}){return e?"error":t?"loading":o?"ready":"missing"}function re({feature:e,view:t,validIds:o,preferredTranscriptId:r}){let n=(0,He.useMemo)(()=>qe(e),[e]),[s,i]=(0,He.useState)(()=>de(n.find(c=>pe(c,r??""))??n[0])),m=cr(s,n,o),u=n.find(c=>de(c)===m),{proteinSequence:l,error:p,isLoading:d}=to({view:t,feature:u});return{options:n,selectedId:m,setSelectedId:i,selectedTranscript:u,proteinSequence:l,error:p,isLoading:d,sequenceStatus:ur({error:p,isLoading:d,proteinSequence:l}),validIds:o}}var P=a(h()),ao=a(we());var oo=a(gt(),1),ro=(0,Ie.createSvgIcon)((0,oo.jsx)("path",{d:"M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6zM19 4h-3.5l-1-1h-5l-1 1H5v2h14z"}),"Delete");var q=a(k()),so=a(ve()),io=a(V());function We(e){let{data:t,error:o,isLoading:r,mutate:n}=W(`cached-blast-${e.join(",")}`,async()=>(await kt()).filter(i=>i.geneId&&e.includes(i.geneId)));return{results:t??[],error:o,isLoading:r,handleDelete:async s=>{await ot(s),n()},handleClearAll:async()=>{await Promise.all((t??[]).map(s=>ot(s.id))),n()}}}var dr=(0,io.makeStyles)()({header:{display:"flex",justifyContent:"space-between",alignItems:"center",marginBottom:8},resultList:{maxHeight:300,overflow:"auto"}});function no(e){let t=[e.geneName,e.transcriptName!==e.geneName?e.transcriptName:void 0].filter(o=>!!o);return t.length>0?t.join(" - "):e.geneId??e.transcriptId??"Unknown"}function pr(e){return[e.blastDatabase,e.searchProgram??e.blastProgram??"blastp",e.msaAlgorithm].filter(Boolean).join(" / ")}var fr=(0,so.observer)(function({model:e,handleClose:t,feature:o}){let{classes:r}=dr(),n=G(e),[s,i]=(0,P.useState)(),[m]=Ge(),u=(0,P.useMemo)(()=>xe(o),[o]),{results:l,error:p,isLoading:d,handleDelete:c,handleClearAll:x}=We(u),b=v=>{let{transcriptId:A,msa:N,tree:B,treeMetadata:F}=v;Q({view:n,feature:A?qe(o).find(C=>pe(C,A)):void 0,placement:m?"splitRight":"stack",displayName:`BLAST - ${no(v)}`,...Me,data:{msa:N,tree:B,treeMetadata:F}}).then(t,C=>{console.error(C),i(C)})},S=p??s;return S?P.default.createElement(ao.ErrorMessage,{error:S}):d?P.default.createElement(q.Typography,null,"Loading cached results..."):l.length===0?P.default.createElement(q.Typography,{color:"textSecondary"},"No cached BLAST results found for this gene. Run a BLAST query to cache results."):P.default.createElement("div",null,P.default.createElement("div",{className:r.header},P.default.createElement(q.Typography,{variant:"subtitle1"},"Cached BLAST Results (",l.length,")"),P.default.createElement(q.Button,{size:"small",color:"error",onClick:async()=>{try{i(void 0),await x()}catch(v){i(v)}}},"Clear results for this gene")),P.default.createElement(q.List,{dense:!0,className:r.resultList},l.map(v=>P.default.createElement(q.ListItem,{key:v.id,disablePadding:!0,secondaryAction:P.default.createElement(q.IconButton,{edge:"end",size:"small",onClick:async A=>{A.stopPropagation();try{i(void 0),await c(v.id)}catch(N){i(N)}}},P.default.createElement(ro,{fontSize:"small"}))},P.default.createElement(q.ListItemButton,{onClick:()=>{b(v)}},P.default.createElement(q.ListItemText,{primary:`${no(v)} - ${pr(v)}`,secondary:`${new Date(v.timestamp).toLocaleString()} - Seq: ${v.proteinSequence.slice(0,30)}...`}))))))}),lo=fr;var it=a(h()),mo=a(k());function Fe({value:e,onChange:t,className:o}){return it.default.createElement(g,{variant:"outlined",label:"MSA Algorithm",className:o,select:!0,value:e,helperText:e==="browser"?"no EBI job; rows aligned to the query":void 0,onChange:r=>{t(r.target.value)}},De.map(r=>it.default.createElement(mo.MenuItem,{value:r,key:r},At[r])))}var hr="msaView-blastSearch",gr="msaView-msaAlgorithm",co=je("blastp"),uo="clustalo";function Sr(e){let{program:t,database:o}=e??{},r=Oe.find(n=>n===t);return r&&$e(r).some(n=>n===o)?{program:r,database:o}:co}function yr(e){return De.find(t=>t===e)??uo}function po(){let[e,t]=ue(hr,co);return[Sr(e),o=>{t(o)}]}function Ue(){let[e,t]=ue(gr,uo);return[yr(e),o=>{t(o)}]}var br=(0,ho.makeStyles)()({selectField:{width:150},databaseField:{width:260},cachedResultsAccordion:{marginTop:20},infoText:{marginTop:20}}),vr=(0,fo.observer)(function({handleClose:e,feature:t,model:o,children:r,preferredTranscriptId:n}){let{classes:s}=br(),i=G(o),{launchError:m,submit:u}=oe(e),[l,p]=po(),[d,c]=Ue(),[x,b]=(0,w.useState)(String(Lt)),S=Number(x),v=Number.isInteger(S)&&S>=1,A=l.program==="phmmer",N=(0,w.useMemo)(()=>xe(t),[t]),{results:B,error:F}=We(N),C=re({feature:t,view:i,preferredTranscriptId:n}),{selectedTranscript:E,proteinSequence:D,sequenceStatus:ee}=C,ce=C.error??m??F;return w.default.createElement(w.default.Fragment,null,w.default.createElement(J,{error:ce},r,w.default.createElement(g,{variant:"outlined",label:"Search program",className:s.selectField,select:!0,value:l.program,onChange:T=>{p(je(T.target.value))}},Oe.map(T=>w.default.createElement(U.MenuItem,{value:T,key:T},Ct[T]))),w.default.createElement(g,{variant:"outlined",label:"Database",className:s.databaseField,select:!0,value:l.database,onChange:T=>{p({program:l.program,database:T.target.value})}},$e(l.program).map(T=>w.default.createElement(U.MenuItem,{value:T,key:T},Nt(T)))),A?null:w.default.createElement(Fe,{className:s.selectField,value:d,onChange:c}),w.default.createElement(g,{variant:"outlined",label:"Hits",className:s.selectField,type:"number",value:x,onChange:T=>{b(T.target.value)},error:!v,helperText:"best-scoring sequences to keep"}),w.default.createElement(Z,{feature:t,...C}),w.default.createElement(U.Typography,{variant:"body2",className:s.infoText},A?"phmmer aligns the hits as it finds them, so nothing is realigned afterwards.":"The hits come back from EBI and are then run through the chosen aligner."," ","The EBI queue is the wait, and it runs from seconds to many minutes."),B.length>0?w.default.createElement(U.Accordion,{className:s.cachedResultsAccordion},w.default.createElement(U.AccordionSummary,{expandIcon:w.default.createElement(yt,null)},w.default.createElement(U.Typography,null,"Previous BLAST Results")),w.default.createElement(U.AccordionDetails,null,w.default.createElement(lo,{model:o,handleClose:e,feature:t}))):null),w.default.createElement(Y,{model:o,hint:w.default.createElement(se,{status:ee}),submitDisabled:!D||!v,onSubmit:T=>{if(E){let $=l.program==="phmmer"?{searchProgram:"phmmer",blastDatabase:l.database,maxHits:S,selectedTranscript:E.toJSON(),proteinSequence:D}:{searchProgram:"blastp",blastDatabase:l.database,msaAlgorithm:d,maxHits:S,selectedTranscript:E.toJSON(),proteinSequence:D};u(()=>Q({view:i,feature:E,placement:T,displayName:vt(t,E),...Me,blastParams:$}))}},onCancel:e}))}),go=vr;var y=a(h()),wo=a(be()),ge=a(k()),xo=a(ve()),To=a(V());var ze=a(h());function So(e){return Ee(e).toUpperCase()}function wr(e,t){let o=Math.min(e.length,t.length);if(o===0)return 0;let r=0;for(let n=0;n<o;n++)e[n]===t[n]&&r++;return r/o}var xr=.9,Tr=.5;function yo(e,t){if(!e.trim())return{names:[]};let o;try{o=Pt(e)}catch{return{names:[]}}let r=o.getNames(),n=So(t);return{names:r,match:n?Mr(o,r,n):void 0}}function Mr(e,t,o){let r=[];for(let s of t){let i=So(It(e.getRow(s)));if(!i)continue;if(i===o)return{name:s,quality:"exact",identity:1,offset:0};let m=o.indexOf(i),u=i.indexOf(o);if(m!==-1||u!==-1){let p=Math.min(i.length,o.length)/Math.max(i.length,o.length);p>=Tr&&r.push({name:s,quality:"partial",identity:p,offset:m===-1?-u:m});continue}let l=wr(i,o);l>=xr&&r.push({name:s,quality:"similar",identity:l,offset:0})}let n=["partial","similar"];return r.sort((s,i)=>n.indexOf(s.quality)-n.indexOf(i.quality)||i.identity-s.identity)[0]}function bo(e,t){return e.querySeqName||(e.names.includes(t)?t:"")}function Ae(e,t){let[o,r]=(0,ze.useState)(),{names:n,match:s}=(0,ze.useMemo)(()=>yo(e,t),[e,t]),i=o??s?.name??"";return{detected:s,names:n,querySeqName:i,querySeqOffset:i===s?.name?s.offset:0,setQuerySeqName:r,isAutoDetected:o===void 0&&!!s}}var R=a(h()),Ce=a(k()),vo=a(V());var Ar=(0,vo.makeStyles)()({field:{marginTop:20},alert:{marginTop:10}});function Cr({identity:e,offset:t}){let o=`, covering ${Math.round(e*100)}% of it`;return t>0?`${o} from residue ${t+1}`:t<0?`${o} and running ${-t} residues past its start`:o}function he({names:e,detected:t,querySeqName:o,setQuerySeqName:r,isAutoDetected:n}){let{classes:s}=Ar();return R.default.createElement(R.default.Fragment,null,e.length>0?R.default.createElement(g,{variant:"outlined",label:"MSA row matching the selected transcript",select:!0,fullWidth:!0,className:s.field,value:e.includes(o)?o:"",onChange:i=>{r(i.target.value)}},e.map(i=>R.default.createElement(Ce.MenuItem,{value:i,key:i},i,t?.name===i?" \u2014 matches your protein":""))):R.default.createElement(g,{variant:"outlined",label:"MSA row matching the selected transcript",fullWidth:!0,className:s.field,helperText:"Paste an alignment above and this fills in on its own",value:o,onChange:i=>{r(i.target.value)}}),n&&t?t.quality==="similar"?R.default.createElement(Ce.Alert,{severity:"warning",className:s.alert},R.default.createElement("strong",null,t.name)," is"," ",Math.round(t.identity*100),"% identical to your protein but is not the same sequence. Clicking the alignment navigates the genome view, approximately: wherever the two differ by an insertion or a deletion, every residue after it lands one codon off."):R.default.createElement(Ce.Alert,{severity:"success",className:s.alert},"Matched ",R.default.createElement("strong",null,t.name)," to your protein sequence",t.quality==="exact"?"":Cr(t),". Clicking the alignment will navigate the genome view."):e.length>0&&!o?R.default.createElement(Ce.Alert,{severity:"warning",className:s.alert},"No row matched your protein sequence \u2014 pick the one for your gene above. Without it the alignment still renders, but clicking it will not navigate the genome view."):null)}var Lr=(0,To.makeStyles)()({ncbiLink:{wordBreak:"break-all"},textAreaFont:{fontFamily:"Courier New"},msaInput:{marginBottom:20},step:{marginTop:20},stepBody:{marginLeft:20,marginTop:8}}),Nr=(0,xo.observer)(function({handleClose:e,feature:t,model:o,children:r,preferredTranscriptId:n}){let{classes:s}=Lr(),i=G(o),{launchError:m,submit:u}=oe(e),[l,p]=(0,y.useState)(""),[d,c]=(0,y.useState)(""),x=re({feature:t,view:i,preferredTranscriptId:n}),{proteinSequence:b,selectedTranscript:S,error:v}=x,A=Ae(l,b),N=Ee(b),B=`${tt}?PAGE_TYPE=BlastSearch&PAGE=Proteins&PROGRAM=blastp&QUERY=${N}`,F=`${tt}?PAGE_TYPE=BlastSearch&PAGE=Proteins&PROGRAM=blastp&QUERY=${(0,wo.shorten2)(N,10)}`;return y.default.createElement(y.default.Fragment,null,y.default.createElement(J,{error:m??v},r,y.default.createElement(Z,{feature:t,...x}),y.default.createElement("div",{className:s.step},y.default.createElement(ge.Typography,{variant:"subtitle2"},"1. Run the search at NCBI"),y.default.createElement("div",{className:s.stepBody},b?y.default.createElement("div",{className:s.ncbiLink},y.default.createElement(Ft,{href:B},F)):y.default.createElement(ge.Alert,{severity:"info"},"Pick a transcript above to get a link carrying its protein sequence."))),y.default.createElement("div",{className:s.step},y.default.createElement(ge.Typography,{variant:"subtitle2"},"2. Align the hits"),y.default.createElement("div",{className:s.stepBody},y.default.createElement(ge.Typography,null,`On the results page click "Multiple Alignment" to run COBALT, NCBI's aligner. Download the alignment (.aln) and, if you want the tree drawn, the Newick tree (.nh).`))),y.default.createElement("div",{className:s.step},y.default.createElement(ge.Typography,{variant:"subtitle2"},"3. Paste the results back here"),y.default.createElement("div",{className:s.stepBody},y.default.createElement(g,{variant:"outlined",label:"Alignment",multiline:!0,minRows:5,maxRows:10,fullWidth:!0,className:s.msaInput,slotProps:{input:{className:s.textAreaFont}},placeholder:"Paste the .aln contents here",value:l,onChange:C=>{p(C.target.value)}}),y.default.createElement(g,{variant:"outlined",label:"Tree (optional)",multiline:!0,minRows:3,maxRows:10,fullWidth:!0,slotProps:{input:{className:s.textAreaFont}},placeholder:"Paste the .nh Newick tree here",value:d,onChange:C=>{c(C.target.value)}}),y.default.createElement(he,{...A})))),y.default.createElement(Y,{model:o,submitDisabled:!S||!l.trim(),onSubmit:C=>{S&&u(()=>Q({view:i,feature:S,placement:C,displayName:te(S),querySeqName:A.querySeqName,querySeqOffset:A.querySeqOffset,data:{msa:l,tree:d}}))},onCancel:e}))}),Mo=Nr;var Se=a(h()),ne=a(k());function lt({lookupMethod:e,setLookupMethod:t}){return Se.default.createElement(ne.FormControl,{component:"fieldset"},Se.default.createElement(ne.RadioGroup,{row:!0,value:e,onChange:o=>{t(o.target.value)}},Se.default.createElement(ne.FormControlLabel,{value:"automatic",control:Se.default.createElement(ne.Radio,null),label:"Automatic"}),Se.default.createElement(ne.FormControlLabel,{value:"manual",control:Se.default.createElement(ne.Radio,null),label:"Manual"})))}var z=a(h()),O=a(k()),Ao=a(V());var kr=(0,Ao.makeStyles)()({field:{minWidth:300},help:{marginBottom:8}});function mt({handleClose:e,ebiEmail:t}){let{classes:o}=kr(),[r,n]=(0,z.useState)(t);return z.default.createElement(O.Dialog,{open:!0,maxWidth:"lg",onClose:()=>{e()}},z.default.createElement(O.DialogTitle,null,"EBI settings"),z.default.createElement(O.DialogContent,null,z.default.createElement(O.Typography,{variant:"subtitle2",className:o.help},"Every search this tab runs is submitted to EBI with this address, which is how they reach whoever is generating the load. If your site sends real volume, use your own."),z.default.createElement(g,{autoFocus:!0,margin:"dense",label:"EBI contact email",fullWidth:!0,variant:"outlined",value:r,className:o.field,onChange:s=>{n(s.target.value)}}),z.default.createElement(O.Button,{variant:"contained",onClick:()=>{n(Be)}},"Reset")),z.default.createElement(O.DialogActions,null,z.default.createElement(O.Button,{variant:"contained",color:"secondary",onClick:()=>{e()}},"Cancel"),z.default.createElement(O.Button,{color:"primary",variant:"contained",onClick:()=>{e({ebiEmail:r})}},"Save")))}var Fr=(0,Co.makeStyles)()({settingsButton:{float:"right"}}),Pr={automatic:go,manual:Mo};function ct({handleClose:e,model:t,feature:o,preferredTranscriptId:r}){let[n,s]=(0,K.useState)("automatic"),[i,m]=ue(St,Be),[u,l]=(0,K.useState)(!1),{classes:p}=Fr(),d=Pr[n];return K.default.createElement(K.default.Fragment,null,K.default.createElement(Ke.Tooltip,{title:`EBI settings \u2014 jobs are submitted with ${i}`},K.default.createElement(Ke.IconButton,{className:p.settingsButton,"aria-label":"EBI settings",size:"small",onClick:()=>{l(!0)}},K.default.createElement(Ot,null))),K.default.createElement(d,{model:t,feature:o,handleClose:e,preferredTranscriptId:r},K.default.createElement(lt,{lookupMethod:n,setLookupMethod:s})),u?K.default.createElement(mt,{ebiEmail:i,handleClose:c=>{c&&m(c.ebiEmail),l(!1)}}):null)}var j=a(h());var Je=a(k()),Ir=(0,j.lazy)(()=>import("./HelpDialog-DF4Y536X.js"));function ut(){let[e,t]=(0,j.useState)(!1);return j.default.createElement(j.default.Fragment,null,j.default.createElement(Je.Tooltip,{title:"What each tab does"},j.default.createElement(Je.IconButton,{"aria-label":"Help",onClick:()=>{t(!0)}},j.default.createElement(Bt,null))),e?j.default.createElement(j.Suspense,{fallback:null},j.default.createElement(Ir,{handleClose:()=>{t(!1)}})):null)}var f=a(h()),dt=a(we()),Lo=a(rr()),ae=a(k()),No=a(ve()),ko=a(V());function Br(e){let t=Qe(e,500),{data:o}=W(t?[JSON.stringify(t),"msa-file-text"]:null,()=>(0,Lo.openLocation)(t).readFile("utf8"));return o??""}var qr=(0,ko.makeStyles)()({textAreaFont:{fontFamily:"Courier New"},inputContainer:{marginBottom:30},fileContainer:{maxWidth:500},msaInput:{marginBottom:20}}),Er=(0,No.observer)(function({model:t,feature:o,handleClose:r,preferredTranscriptId:n}){let s=G(t),{classes:i}=qr(),{launchError:m,submit:u}=oe(r),[l,p]=(0,f.useState)("file"),[d,c]=(0,f.useState)(""),[x,b]=(0,f.useState)(""),[S,v]=(0,f.useState)(),[A,N]=(0,f.useState)(),B=re({feature:o,view:s,preferredTranscriptId:n}),{selectedTranscript:F,proteinSequence:C,error:E,sequenceStatus:D}=B,ee=Br(l==="file"?S:void 0),ce=Ae(l==="file"?ee:d,C);return f.default.createElement(f.default.Fragment,null,f.default.createElement(J,{error:m??E},f.default.createElement(ae.FormControl,{component:"fieldset"},f.default.createElement(ae.RadioGroup,{row:!0,value:l,onChange:$=>{p($.target.value)}},f.default.createElement(ae.FormControlLabel,{value:"file",control:f.default.createElement(ae.Radio,null),label:"Open files"}),f.default.createElement(ae.FormControlLabel,{value:"text",control:f.default.createElement(ae.Radio,null),label:"Paste text"}))),f.default.createElement("div",{className:i.inputContainer},l==="file"?f.default.createElement("div",{className:i.fileContainer},f.default.createElement(dt.FileSelector,{name:"MSA File .aln (Clustal), .fa/.mfa (aligned FASTA), .stock (Stockholm), etc)",inline:!0,location:S,setLocation:v}),f.default.createElement(dt.FileSelector,{name:"Tree file .nh (Newick) or .asn (NCBI COBALT ASN.1)",inline:!0,location:A,setLocation:N})):f.default.createElement(f.default.Fragment,null,f.default.createElement(g,{variant:"outlined",name:"MSA",multiline:!0,minRows:5,className:i.msaInput,maxRows:10,fullWidth:!0,placeholder:"Paste MSA here",value:d,onChange:$=>{c($.target.value)}}),f.default.createElement(g,{variant:"outlined",name:"Tree",multiline:!0,minRows:5,maxRows:10,fullWidth:!0,placeholder:"Paste newick tree (optional)",value:x,onChange:$=>{b($.target.value)}}))),f.default.createElement(Z,{feature:o,...B}),f.default.createElement(he,{...ce})),f.default.createElement(Y,{model:t,hint:f.default.createElement(se,{status:D}),submitDisabled:!F||l==="file"&&!S||l==="text"&&!d.trim(),onSubmit:$=>{F&&u(()=>Q({view:s,feature:F,placement:$,displayName:te(F),querySeqName:ce.querySeqName,querySeqOffset:ce.querySeqOffset,...l==="file"?{msaFilehandle:S,treeFilehandle:A}:{data:{msa:d,tree:x}}}))},onCancel:r}))}),Fo=Er;var I=a(h()),Oo=a(k()),jo=a(ve()),$o=a(V());var pt=a(h()),Po=a(k());var Dr="msaview-ortholog-source",Io={ncbi:"NCBI orthologs",panther:"PANTHER",uniref:"UniRef cluster"},Bo=Object.keys(Io);function Or(e){return Bo.find(t=>t===e)??"ncbi"}function qo(){let[e,t]=ue(Dr,"ncbi");return[Or(e),o=>{t(o)}]}var jr={ncbi:"vertebrates and insects",panther:"also yeast, worm, fly and plants",uniref:"all of UniProtKB within 50% identity"};function ft({value:e,onChange:t,className:o}){return pt.default.createElement(g,{variant:"outlined",label:"Source",className:o,select:!0,value:e,helperText:jr[e],onChange:r=>{t(r.target.value)}},Bo.map(r=>pt.default.createElement(Po.MenuItem,{value:r,key:r},Io[r])))}var Ye=a(h());async function Eo(e,t){let o=e.trim();if(!o)return;if(/^\d+$/.test(o))return Number(o);let n=(await Ve(_e("esearch",{db:"taxonomy",term:o,retmax:"1",retmode:"json"}),{signal:t})).esearchresult?.idlist?.[0];return n?Number(n):void 0}async function Do(e,t){let o=e.trim();if(!o)return;let n=(await Ve(_e("esearch",{db:"assembly",term:o,retmax:"1",retmode:"json"}),{signal:t})).esearchresult?.idlist?.[0];if(!n)return;let i=(await Ve(_e("esummary",{db:"assembly",id:n,retmode:"json"}),{signal:t})).result?.[n],m=Number(i?.speciestaxid);return i?.speciesname&&m?{taxId:m,speciesName:i.speciesname}:void 0}async function $r(e,t){let o=await Eo(e,t);if(!o)throw new Error(`No NCBI taxon matches "${e}"`);let r=(await Et([o],t)).get(o),n=[r?.sciname,r?.commonName&&`(${r.commonName})`].filter(Boolean).join(" ");return{taxId:o,label:n||`taxon ${o}`}}function ht({value:e,assemblyName:t,onChange:o,className:r}){let[n,s]=(0,Ye.useState)(),i=Qe(n,400),{data:m}=W(t&&n===void 0?[t,"assembly-species"]:null,c=>Do(t,c),{onSuccess:c=>{c&&o(c.taxId)}}),{data:u,error:l}=W(i?.trim()?[i.trim(),"taxon"]:null,c=>$r(i,c),{onSuccess:({taxId:c})=>{o(c)}}),p=n??m?.speciesName??"human",d=n===void 0?m?.speciesName:u?.label;return Ye.default.createElement(g,{variant:"outlined",label:"Query species",className:r,value:p,onChange:c=>{s(c.target.value)},error:!!l,helperText:l?`${l}`:d??`the species this gene is from (taxon ${e})`})}var _r=(0,$o.makeStyles)()({selectField:{width:180}}),Vr={ncbi:e=>`the ${e} closest species NCBI has`,panther:e=>`the ${e} closest species PANTHER has`,uniref:e=>`${e} rows, one per species, reviewed entries first`},Gr=(0,jo.observer)(function({handleClose:e,feature:t,model:o,preferredTranscriptId:r}){let{classes:n}=_r(),s=G(o),{launchError:i,submit:m}=oe(e),[u,l]=(0,I.useState)(9606),[p,d]=qo(),[c,x]=Ue(),[b,S]=(0,I.useState)(String(qt)),v=(0,I.useMemo)(()=>xe(t),[t]),A=re({feature:t,view:s,preferredTranscriptId:r}),{selectedTranscript:N,proteinSequence:B,sequenceStatus:F}=A,C=A.error??i,E=Number(b),D=Number.isInteger(E)&&E>=2;return I.default.createElement(I.default.Fragment,null,I.default.createElement(J,{error:C},I.default.createElement(Oo.Typography,{variant:"body2"},"Precomputed orthologs, one gene per species, looked up rather than searched for. No BLAST job to queue, and with the in-browser aligner no job at all."),I.default.createElement("div",null,I.default.createElement(ft,{className:n.selectField,value:p,onChange:d}),I.default.createElement(ht,{className:n.selectField,value:u,assemblyName:s.assemblyNames[0],onChange:l}),I.default.createElement(Fe,{className:n.selectField,value:c,onChange:x}),I.default.createElement(g,{variant:"outlined",label:"Rows to align",className:n.selectField,type:"number",value:b,onChange:ee=>{S(ee.target.value)},error:!D,helperText:D?Vr[p](E):"a whole number, 2 or more"})),I.default.createElement(Z,{feature:t,...A})),I.default.createElement(Y,{model:o,hint:I.default.createElement(se,{status:F}),submitDisabled:!B||!D,onSubmit:ee=>{N&&m(()=>Q({view:s,feature:N,placement:ee,displayName:`Orthologs - ${te(t)} - ${fe(N)}`,...Me,orthologParams:{taxId:u,source:p,maxSpecies:E,geneCandidates:v,msaAlgorithm:c,selectedTranscript:N.toJSON(),proteinSequence:B}}))},onCancel:e}))}),_o=Gr;var M=a(h()),Le=a(we()),Ze=a(be()),Re=a(k()),Ko=a(ve()),Jo=a(V());var Qo=a(Go());async function Ho(e,t){return(await(0,Qo.getAdapter)(e,"msa",t)).dataAdapter}async function Wo({config:e,pluginManager:t}){return(await Ho(t,e)).getMSAList()}async function Uo({config:e,pluginManager:t,msaId:o}){return(await Ho(t,e)).getMSA(o)}var zo=a(tr());function Xe(e){return(0,zo.readConfObject)(e,["msa","datasets"])}var Qr=(0,Jo.makeStyles)()({selectedContainer:{marginTop:50}}),Hr=(0,Ko.observer)(function({model:e,feature:t,handleClose:o,preferredTranscriptId:r}){let n=(0,Ze.getSession)(e),s=G(e),{classes:i}=Qr(),{pluginManager:m}=(0,Ze.getEnv)(e),{assemblyNames:u}=s,{launchError:l,submit:p}=oe(o),d=Xe(n.jbrowse),[c,x]=(0,M.useState)(d?.[0]?.datasetId),b=d?.find(_=>_.datasetId===c),{data:S,isLoading:v,error:A}=W(b?`${b.datasetId}-msa-list`:null,()=>Wo({config:b.adapter,pluginManager:m})),N=re({feature:t,view:s,validIds:S,preferredTranscriptId:r}),{selectedId:B,selectedTranscript:F,proteinSequence:C,sequenceStatus:E}=N,{data:D,isLoading:ee,error:ce}=W(B&&b&&S?`${b.datasetId}-${B}-msa`:null,()=>Uo({msaId:B,config:b.adapter,pluginManager:m})),T=(0,M.useMemo)(()=>D?.map(_=>`>${_.get("refName")}
|
|
3
3
|
${_.get("seq")}`).join(`
|
|
4
4
|
`)??"",[D]),$=Ae(T,C),Pe=bo($,`${B}_${u[0]??""}`),Ro=A??ce??N.error??l;return M.default.createElement(M.default.Fragment,null,M.default.createElement(J,{error:Ro},M.default.createElement(g,{select:!0,label:"Select MSA dataset",value:c,onChange:_=>{x(_.target.value)}},d?.map(_=>M.default.createElement(Re.MenuItem,{key:_.datasetId,value:_.datasetId},_.name))),b?M.default.createElement("div",{className:i.selectedContainer},!v&&ee?M.default.createElement(Le.LoadingEllipses,{variant:"h6",message:`Loading MSA for ${fe(F)||B}`}):null,v?M.default.createElement(Le.LoadingEllipses,{variant:"h6",message:`Loading available MSAs for (${b.name})`}):null,S?M.default.createElement("div",null,M.default.createElement(Le.SanitizedHTML,{html:b.description}),M.default.createElement(Z,{feature:t,...N}),T?M.default.createElement(he,{...$,querySeqName:Pe}):null):null):null),M.default.createElement(Y,{model:e,hint:ee?M.default.createElement(Le.LoadingEllipses,{message:"Loading alignment"}):D?.length&&!Pe?M.default.createElement(Re.Typography,{color:"textSecondary",variant:"body2"},"no row matches this transcript"):M.default.createElement(se,{status:E}),submitDisabled:!F||!D?.length||!Pe,onSubmit:_=>{F&&T&&p(()=>Q({view:s,feature:F,placement:_,displayName:te(F),treeAreaWidth:200,treeWidth:100,drawNodeBubbles:!1,labelsAlignRight:!0,showBranchLen:!1,colWidth:10,rowHeight:12,colorSchemeName:"percent_identity_dynamic",querySeqName:Pe,querySeqOffset:$.querySeqOffset,data:{msa:T}}))},onCancel:o}))}),Yo=Hr;var et=a(h());function Ne({children:e,value:t,index:o,...r}){let n=t===o,[s,i]=(0,et.useState)(n);return n&&!s&&i(!0),et.default.createElement("div",{role:"tabpanel",hidden:!n,...r},s||n?e:null)}function Wr({handleClose:e,feature:t,model:o,preferredTranscriptId:r}){let n=(0,Zo.getSession)(o),i=!!Xe(n.jbrowse)?.length,[m,u]=(0,L.useState)("orthologs");return L.default.createElement(Xo.Dialog,{maxWidth:"xl",title:"Launch MSA view",open:!0,onClose:e},L.default.createElement(Wt,null,L.default.createElement("div",{style:{display:"flex",alignItems:"center"}},L.default.createElement(ye.Tabs,{value:m,onChange:(l,p)=>{u(p)}},L.default.createElement(ye.Tab,{label:"Orthologs",value:"orthologs"}),L.default.createElement(ye.Tab,{label:"BLAST query",value:"ncbi_blast"}),i?L.default.createElement(ye.Tab,{label:"Pre-loaded MSA datasets",value:"preloaded_msa"}):null,L.default.createElement(ye.Tab,{label:"Manual upload",value:"manual_msa"})),L.default.createElement(ut,null)),L.default.createElement(Ne,{value:m,index:"orthologs"},L.default.createElement(_o,{handleClose:e,feature:t,model:o,preferredTranscriptId:r})),L.default.createElement(Ne,{value:m,index:"ncbi_blast"},L.default.createElement(ct,{handleClose:e,feature:t,model:o,preferredTranscriptId:r})),i?L.default.createElement(Ne,{value:m,index:"preloaded_msa"},L.default.createElement(Yo,{model:o,feature:t,handleClose:e,preferredTranscriptId:r})):null,L.default.createElement(Ne,{value:m,index:"manual_msa"},L.default.createElement(Fo,{model:o,feature:t,handleClose:e,preferredTranscriptId:r}))))}export{Wr as default};
|
|
5
|
-
//# sourceMappingURL=LaunchMsaViewDialog-
|
|
5
|
+
//# sourceMappingURL=LaunchMsaViewDialog-WBXEEHJL.js.map
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var w=["stack","splitRight","newTab"],i="splitRight",r="msaView-launchPlacement";function o(e,t){return t in e&&typeof e[t]=="function"}var a=!1;function M(e){return c(e)||o(e,"setUseWorkspaces")&&o(e,"setPendingMove")}function c(e){return o(e,"moveViewToNewTab")&&o(e,"moveViewToSplit")}function p(e){let t=o(e,"setUseWorkspaces"),s=o(e,"setPendingMove");return t!==s&&!a&&(a=!0,console.warn(`jbrowse-plugin-msaview: this session supports workspaces but not ${s?"setUseWorkspaces":"setPendingMove"}, so the MSA view was stacked instead of tiled: the session API moved and the plugin needs updating to match.`)),t&&s}function l(e,t,s){s!=="stack"&&(c(e)?s==="newTab"?e.moveViewToNewTab(t):e.moveViewToSplit(t,"row"):p(e)&&(e.setPendingMove({type:s,viewId:t}),e.setUseWorkspaces(!0)))}function u(e){return w.includes(e)}function m(){try{let e=globalThis.localStorage.getItem(r);return u(e)?e:i}catch(e){return console.error(e),i}}function d(e){try{globalThis.localStorage.setItem(r,e)}catch(t){console.error(t)}}async function P(e,{placement:t="stack",...s}){let n=await e.launchView("MsaView",{type:"MsaView",...s});return l(e,n.id,t),n}export{M as a,m as b,d as c,P as d};
|
|
2
|
+
//# sourceMappingURL=chunk-DECSTFAT.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/utils/workspaces.ts", "../../src/utils/launchMsaView.ts"],
|
|
4
|
+
"sourcesContent": ["import type { AbstractSessionModel } from '@jbrowse/core/util'\n\n/**\n * Where a launched MSA view lands. Every launch names one of these and stops\n * there, so a host that arranges views differently is one function to teach.\n *\n * stack append below whatever is on screen\n * splitRight its own cell to the right, beside the view it is connected to\n * newTab its own tab in the current cell\n */\nexport type MsaViewPlacement = 'stack' | 'splitRight' | 'newTab'\n\nconst PLACEMENTS: MsaViewPlacement[] = ['stack', 'splitRight', 'newTab']\n\n/**\n * What the dialog does unasked. Side-by-side, because a launch from a gene\n * feature sets `connectedViewId`: the pair shares a hover and a highlight, and\n * reads as a split. A session spec defaults to `stack` instead.\n */\nexport const DEFAULT_LAUNCH_PLACEMENT: MsaViewPlacement = 'splitRight'\n\nexport const LAUNCH_PLACEMENT_KEY = 'msaView-launchPlacement'\n\n/**\n * The session actions that place a view in a tiled workspace. Only jbrowse-web\n * and desktop have them, so feature-detect rather than import.\n */\ninterface SessionWithMoves {\n moveViewToNewTab: (viewId: string) => unknown\n moveViewToSplit: (viewId: string, direction: 'row' | 'column') => unknown\n}\n\n// a host from before the workspace was always on\ninterface SessionWithWorkspaces {\n setUseWorkspaces: (useWorkspaces: boolean) => void\n setPendingMove: (move: {\n type: 'newTab' | 'splitRight'\n viewId: string\n }) => void\n}\n\nfunction hasAction(session: AbstractSessionModel, name: string) {\n return (\n name in session &&\n typeof (session as unknown as Record<string, unknown>)[name] === 'function'\n )\n}\n\n// Warned at most once. This is a property of the host, so the answer is the\n// same on every launch and a dialog the user reopens should not stack up noise.\nlet warnedPartial = false\n\nexport function resetWorkspacesWarning() {\n warnedPartial = false\n}\n\n/**\n * Whether this host can honor anything other than `stack`. Silent: the dialog\n * asks on every render, and only a launch is worth warning about.\n */\nexport function sessionSupportsPlacement(session: AbstractSessionModel) {\n return (\n isSessionWithMoves(session) ||\n (hasAction(session, 'setUseWorkspaces') &&\n hasAction(session, 'setPendingMove'))\n )\n}\n\nfunction isSessionWithMoves(\n session: AbstractSessionModel,\n): session is AbstractSessionModel & SessionWithMoves {\n return (\n hasAction(session, 'moveViewToNewTab') &&\n hasAction(session, 'moveViewToSplit')\n )\n}\n\nfunction isSessionWithWorkspaces(\n session: AbstractSessionModel,\n): session is AbstractSessionModel & SessionWithWorkspaces {\n const canEnable = hasAction(session, 'setUseWorkspaces')\n const canPlace = hasAction(session, 'setPendingMove')\n\n // Missing BOTH is an embedded session, with nothing to ask for. Missing ONE\n // is a host that moved the action out from under us: protein3d once stopped\n // tiling silently for weeks when jbrowse-web folded `setPendingMove` into its\n // layout `init`.\n if (canEnable !== canPlace && !warnedPartial) {\n warnedPartial = true\n console.warn(\n `jbrowse-plugin-msaview: this session supports workspaces but not ` +\n `${canPlace ? 'setUseWorkspaces' : 'setPendingMove'}, so the MSA view ` +\n `was stacked instead of tiled: the session API moved and the plugin ` +\n `needs updating to match.`,\n )\n }\n return canEnable && canPlace\n}\n\n/**\n * Put a freshly added view where the launch said to. `stack` is a placement\n * rather than the absence of one, so no caller has to ask what host it is on.\n */\nexport function placeMsaView(\n session: AbstractSessionModel,\n viewId: string,\n placement: MsaViewPlacement,\n) {\n if (placement === 'stack') {\n return\n }\n if (isSessionWithMoves(session)) {\n if (placement === 'newTab') {\n session.moveViewToNewTab(viewId)\n } else {\n session.moveViewToSplit(viewId, 'row')\n }\n } else if (isSessionWithWorkspaces(session)) {\n session.setPendingMove({ type: placement, viewId })\n session.setUseWorkspaces(true)\n }\n}\n\nfunction isPlacement(value: unknown): value is MsaViewPlacement {\n return PLACEMENTS.includes(value as MsaViewPlacement)\n}\n\n/**\n * The dialog's own remembered choice \u2014 not the host's preferences system, which\n * records whether the user likes workspaces and does not exist everywhere.\n */\nexport function readLaunchPlacement(): MsaViewPlacement {\n try {\n const stored = globalThis.localStorage.getItem(LAUNCH_PLACEMENT_KEY)\n return isPlacement(stored) ? stored : DEFAULT_LAUNCH_PLACEMENT\n } catch (error) {\n console.error(error)\n return DEFAULT_LAUNCH_PLACEMENT\n }\n}\n\nexport function writeLaunchPlacement(placement: MsaViewPlacement) {\n try {\n globalThis.localStorage.setItem(LAUNCH_PLACEMENT_KEY, placement)\n } catch (error) {\n console.error(error)\n }\n}\n", "import { placeMsaView } from './workspaces'\n\nimport type { MsaViewPlacement } from './workspaces'\nimport type { AbstractSessionModel } from '@jbrowse/core/util'\n\n/**\n * A launch, stated: what the view is, and where it goes. Everything but\n * `placement` is a react-msaview or plugin-model snapshot property, passed\n * through untouched so this never becomes a list that has to grow.\n */\nexport interface MsaViewLaunchSpec extends Record<string, unknown> {\n /** default `stack`, the only thing an embedded session can do */\n placement?: MsaViewPlacement\n}\n\n/**\n * The one place a launch adds an MSA view -- the dialog's four tabs, the Add\n * menu, and the `LaunchView-MsaView` extension point a session spec arrives on\n * all come through here. Each of them used to run its own `addView` and none\n * placed the result, which is how a launch from a gene feature landed stacked\n * under the very genome view it was connected to.\n */\nexport async function launchMsaView(\n session: AbstractSessionModel,\n { placement = 'stack', ...snapshot }: MsaViewLaunchSpec,\n) {\n const view = await session.launchView('MsaView', {\n type: 'MsaView',\n ...snapshot,\n })\n placeMsaView(session, view.id, placement)\n return view\n}\n"],
|
|
5
|
+
"mappings": "AAYA,IAAMA,EAAiC,CAAC,QAAS,aAAc,QAAQ,EAO1DC,EAA6C,aAE7CC,EAAuB,0BAoBpC,SAASC,EAAUC,EAA+BC,EAAc,CAC9D,OACEA,KAAQD,GACR,OAAQA,EAA+CC,CAAI,GAAM,UAErE,CAIA,IAAIC,EAAgB,GAUb,SAASC,EAAyBC,EAA+B,CACtE,OACEC,EAAmBD,CAAO,GACzBE,EAAUF,EAAS,kBAAkB,GACpCE,EAAUF,EAAS,gBAAgB,CAEzC,CAEA,SAASC,EACPD,EACoD,CACpD,OACEE,EAAUF,EAAS,kBAAkB,GACrCE,EAAUF,EAAS,iBAAiB,CAExC,CAEA,SAASG,EACPH,EACyD,CACzD,IAAMI,EAAYF,EAAUF,EAAS,kBAAkB,EACjDK,EAAWH,EAAUF,EAAS,gBAAgB,EAMpD,OAAII,IAAcC,GAAY,CAACC,IAC7BA,EAAgB,GAChB,QAAQ,KACN,oEACKD,EAAW,mBAAqB,gBAAgB,+GAGvD,GAEKD,GAAaC,CACtB,CAMO,SAASE,EACdP,EACAQ,EACAC,EACA,CACIA,IAAc,UAGdR,EAAmBD,CAAO,EACxBS,IAAc,SAChBT,EAAQ,iBAAiBQ,CAAM,EAE/BR,EAAQ,gBAAgBQ,EAAQ,KAAK,EAE9BL,EAAwBH,CAAO,IACxCA,EAAQ,eAAe,CAAE,KAAMS,EAAW,OAAAD,CAAO,CAAC,EAClDR,EAAQ,iBAAiB,EAAI,GAEjC,CAEA,SAASU,EAAYC,EAA2C,CAC9D,OAAOC,EAAW,SAASD,CAAyB,CACtD,CAMO,SAASE,GAAwC,CACtD,GAAI,CACF,IAAMC,EAAS,WAAW,aAAa,QAAQC,CAAoB,EACnE,OAAOL,EAAYI,CAAM,EAAIA,EAASE,CACxC,OAASC,EAAO,CACd,eAAQ,MAAMA,CAAK,EACZD,CACT,CACF,CAEO,SAASE,EAAqBT,EAA6B,CAChE,GAAI,CACF,WAAW,aAAa,QAAQM,EAAsBN,CAAS,CACjE,OAASQ,EAAO,CACd,QAAQ,MAAMA,CAAK,CACrB,CACF,CC7HA,eAAsBE,EACpBC,EACA,CAAE,UAAAC,EAAY,QAAS,GAAGC,CAAS,EACnC,CACA,IAAMC,EAAO,MAAMH,EAAQ,WAAW,UAAW,CAC/C,KAAM,UACN,GAAGE,CACL,CAAC,EACD,OAAAE,EAAaJ,EAASG,EAAK,GAAIF,CAAS,EACjCE,CACT",
|
|
6
|
+
"names": ["PLACEMENTS", "DEFAULT_LAUNCH_PLACEMENT", "LAUNCH_PLACEMENT_KEY", "hasAction", "session", "name", "warnedPartial", "sessionSupportsPlacement", "session", "isSessionWithMoves", "hasAction", "isSessionWithWorkspaces", "canEnable", "canPlace", "warnedPartial", "placeMsaView", "viewId", "placement", "isPlacement", "value", "PLACEMENTS", "readLaunchPlacement", "stored", "LAUNCH_PLACEMENT_KEY", "DEFAULT_LAUNCH_PLACEMENT", "error", "writeLaunchPlacement", "launchMsaView", "session", "placement", "snapshot", "view", "placeMsaView"]
|
|
7
|
+
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{f as p,h as ye}from"./chunks/chunk-TITP6LCV.js";import{a as Me}from"./chunks/chunk-FCWXYIFC.js";import{d as h}from"./chunks/chunk-
|
|
2
|
-
`)?e:void 0,o=t?.trimStart().startsWith("(")?t:void 0,s=n||o?{...r.data,...n?{msa:n}:{},...o?{tree:o}:{}}:r.data;return{...r,data:s,...e&&!n?{msaFileLocation:{uri:e}}:{},...t&&!o?{treeFileLocation:{uri:t}}:{},querySeqName:r.querySeqName??r.query}}function C(e){e.addToExtensionPoint("LaunchView-MsaView",async t=>{let{session:r,query:n,data:o,msaFileLocation:s,msaIndexedLocation:a,msaName:m,treeFileLocation:l,querySeqName:b,searchParams:x,...N}=He(t);if(!o&&!s&&!a&&!N.orthologParams&&!x)throw new Error("No MSA data, file location, orthologParams or searchParams provided when launching MSA view");let E={msaUrl:s?.uri,msaIndexedLocation:a,msaName:m,querySeqName:b},{expandSpec:fe}=await import("./chunks/dist-2T65ZS4E.js");return await h(r,{...fe({...N,query:n}),...x?{blastParams:x}:{},data:o,...l?{treeFilehandle:{...l,locationType:"UriLocation"}}:{},...Object.values(E).some(he=>he!==void 0)?{init:E}:{}}),t})}var ue=i(c()),ce=i(le()),je=(0,ue.lazy)(()=>import("./chunks/MsaViewPanel-YYP4CYYO.js"));function H(e){e.addViewType(()=>new ce.default({name:"MsaView",stateModel:()=>import("./chunks/model-7VBTOIJX.js").then(t=>t.default()),ReactComponent:je}))}var pe="5.
|
|
1
|
+
import{f as p,h as ye}from"./chunks/chunk-TITP6LCV.js";import{a as Me}from"./chunks/chunk-FCWXYIFC.js";import{d as h}from"./chunks/chunk-DECSTFAT.js";import{a as we}from"./chunks/chunk-NSASZ5FM.js";import{a as u,c as f,d as B}from"./chunks/chunk-IBBQUV2I.js";import{a as q}from"./chunks/chunk-2IWD4OO4.js";import{a as P}from"./chunks/chunk-CVLB4VFP.js";import{a as c}from"./chunks/chunk-J6KYLXRD.js";import{a as z,b as i}from"./chunks/chunk-FY3BTKY4.js";var R=z((Ge,D)=>{D.exports=JBrowseExports["@jbrowse/core/Plugin"]});var le=z((pt,me)=>{me.exports=JBrowseExports["@jbrowse/core/pluggableElementTypes/ViewType"]});var de=i(R()),j=i(q()),ge=i(u()),G=i(Me());var $=i(B(),1),O=(0,f.createSvgIcon)((0,$.jsx)("path",{d:"M20 2H4c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2M8 20H4v-4h4zm0-6H4v-4h4zm0-6H4V4h4zm6 12h-4v-4h4zm0-6h-4v-4h4zm0-6h-4V4h4zm6 12h-4v-4h4zm0-6h-4v-4h4zm0-6h-4V4h4z"}),"GridOn");var Z=i(c()),ee=i(u());function M(e){return e.type==="MsaView"}var g=i(c()),X=i(P());var V=i(c()),J=i(u()),v=i(P());var _=i(we()),w=(0,_.makeStyles)()({highlight:{height:"100%",background:"rgba(255,255,0,0.2)",border:"1px solid rgba(50,50,0,0.2)",position:"absolute",zIndex:99,textAlign:"center",pointerEvents:"none",overflow:"hidden"}});var be=(0,v.observer)(function({model:e}){let{hovered:t,views:r}=(0,J.getSession)(e);return r.some(o=>M(o)&&o.connectedViewId===e.id)&&p(t)?V.default.createElement(xe,{model:e,hovered:t}):null}),xe=(0,v.observer)(function({model:e,hovered:t}){let{classes:r}=w(),{offsetPx:n}=e,{coord:o,refName:s}=t.hoverPosition,a=e.bpToPx({refName:s,coord:o-1}),m=e.bpToPx({refName:s,coord:o});if(a&&m){let l=Math.max(Math.abs(m.offsetPx-a.offsetPx),4),b=Math.min(a.offsetPx,m.offsetPx)-n;return V.default.createElement("div",{className:r.highlight,style:{left:b,width:l}})}return null}),W=be;var d=i(c()),K=i(u()),T=i(P());function U(e,t,r){let n=e.filter(Pe).filter(o=>o.connectedViewId===t).flatMap(o=>[...o.connectedClickHighlights,...r?[]:o.connectedHoverHighlights]);return[...new Map(n.map(o=>[`${o.refName}:${o.start}-${o.end}`,o])).values()]}function Pe(e){return e.type==="MsaView"}var Ve=(0,T.observer)(function({model:t}){let{views:r,hovered:n}=(0,K.getSession)(t),o=U(r,t.id,p(n));return o.length?d.default.createElement(ve,{model:t,highlights:o}):null}),ve=(0,T.observer)(function({model:e,highlights:t}){let{classes:r}=w(),{offsetPx:n}=e;return d.default.createElement(d.default.Fragment,null,t.map(o=>{let s=e.bpToPx({refName:o.refName,coord:o.start}),a=e.bpToPx({refName:o.refName,coord:o.end});if(s&&a){let m=Math.max(Math.abs(a.offsetPx-s.offsetPx),4),l=Math.min(s.offsetPx,a.offsetPx)-n;return d.default.createElement("div",{key:`${o.refName}-${o.start}-${o.end}`,className:r.highlight,style:{left:l,width:m}})}return null}))}),Q=Ve;var Te=(0,X.observer)(function({model:t}){return g.default.createElement(g.default.Fragment,null,g.default.createElement(Q,{model:t}),g.default.createElement(W,{model:t}))}),Y=Te;function S(e){e.contributeToExtensionPoint("LinearGenomeView-TracksContainerComponent",({model:t})=>{let{views:r}=(0,ee.getSession)(t);return r.some(n=>M(n)&&n.connectedViewId===t.id)?Z.default.createElement(Y,{key:"highlight_protein_viewer_msaview",model:t}):void 0})}var oe=i(ye());var te=i(q());function F(e){let t=e.getAdapterType("BgzipFastaAdapter");return(0,te.ConfigurationSchema)("BgzipFastaMsaAdapter",{msaRegex:{type:"string",defaultValue:"_"}},{baseConfiguration:t.configSchema})}function L(e){return e.addAdapterType(()=>new oe.AdapterType({name:"BgzipFastaMsaAdapter",configSchema:F(e),adapterMetadata:{hiddenFromGUI:!0},getAdapterClass:()=>import("./chunks/BgzipFastaMsaAdapter-ZMUARSUQ.js").then(t=>t.default)}))}var se=i(c()),y=i(u());var re=i(B(),1),ne=(0,f.createSvgIcon)((0,re.jsx)("path",{d:"M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6z"}),"Add");function Se(e){return e.get("type")?.toLowerCase()==="cds"}function I(e){return Se(e)||!!e.get("subfeatures")?.some(I)}var ie=i(u());function ae(e){let t=e.contextMenuInfo;return t&&(0,ie.isGeneLikeType)(t.item.type)?{fetchFeature:()=>e.fetchFullFeature(t.item.featureId,t.displayedRegionIndex),preferredTranscriptId:t.subfeature?.featureId}:void 0}var Fe=(0,se.lazy)(()=>import("./chunks/LaunchMsaViewDialog-WBXEEHJL.js"));function Le(e){return e.name==="LinearBasicDisplay"}function Ie(e){return e.get("name")??e.get("id")??"This feature"}function Ae(e,t){let r=(0,y.getContainingTrack)(e),n=(0,y.getSession)(r),{preferredTranscriptId:o}=t,s=a=>{n.queueDialog(m=>[Fe,{model:r,handleClose:m,feature:a,preferredTranscriptId:o}])};t.fetchFeature().then(a=>{a?I(a)?s(a):n.notify(`${Ie(a)} has no coding sequence, so there is no protein to align`,"info"):n.notify("Could not load feature for MSA view","warning")}).catch(a=>{n.notifyError(`${a}`,a)})}function Ce(e){return e.views(t=>{let r=t.contextMenuItems;return{contextMenuItems(){let n=ae(t);return[...r.call(t),...n?[{label:"Launch MSA view",icon:ne,onClick:()=>{Ae(t,n)}}]:[]]}}})}function A(e){e.addToExtensionPoint("Core-extendPluggableElement",t=>(Le(t)&&(t.stateModel=Ce(t.stateModel)),t))}function He({msa:e,tree:t,...r}){let n=e?.includes(`
|
|
2
|
+
`)?e:void 0,o=t?.trimStart().startsWith("(")?t:void 0,s=n||o?{...r.data,...n?{msa:n}:{},...o?{tree:o}:{}}:r.data;return{...r,data:s,...e&&!n?{msaFileLocation:{uri:e}}:{},...t&&!o?{treeFileLocation:{uri:t}}:{},querySeqName:r.querySeqName??r.query}}function C(e){e.addToExtensionPoint("LaunchView-MsaView",async t=>{let{session:r,query:n,data:o,msaFileLocation:s,msaIndexedLocation:a,msaName:m,treeFileLocation:l,querySeqName:b,searchParams:x,...N}=He(t);if(!o&&!s&&!a&&!N.orthologParams&&!x)throw new Error("No MSA data, file location, orthologParams or searchParams provided when launching MSA view");let E={msaUrl:s?.uri,msaIndexedLocation:a,msaName:m,querySeqName:b},{expandSpec:fe}=await import("./chunks/dist-2T65ZS4E.js");return await h(r,{...fe({...N,query:n}),...x?{blastParams:x}:{},data:o,...l?{treeFilehandle:{...l,locationType:"UriLocation"}}:{},...Object.values(E).some(he=>he!==void 0)?{init:E}:{}}),t})}var ue=i(c()),ce=i(le()),je=(0,ue.lazy)(()=>import("./chunks/MsaViewPanel-YYP4CYYO.js"));function H(e){e.addViewType(()=>new ce.default({name:"MsaView",stateModel:()=>import("./chunks/model-7VBTOIJX.js").then(t=>t.default()),ReactComponent:je}))}var pe="5.1.0";var k=class extends de.default{constructor(){super(...arguments);this.name="MsaViewPlugin";this.version=pe;this.rootConfigurationSchema=r=>({msa:(0,j.ConfigurationSchema)("MSA",{datasets:G.types.maybe(G.types.array((0,j.ConfigurationSchema)("MSAEntry",{datasetId:{type:"string",defaultValue:""},description:{type:"string",defaultValue:""},name:{type:"string",defaultValue:""},adapter:r.pluggableConfigSchemaType("adapter")})))})})}install(r){H(r),A(r),C(r),S(r),L(r)}configure(r){try{(0,ge.isAbstractMenuManager)(r.rootModel)&&r.rootModel.appendToSubMenu(["Add"],{label:"Multiple sequence alignment view",icon:O,onClick:n=>{h(n,{}).catch(o=>{console.error(o),n.notifyError(`${o}`,o)})}})}catch(n){console.error("jbrowse-plugin-msaview: could not add the Add menu item",n)}}};export{k as default};
|
|
3
3
|
//# sourceMappingURL=jbrowse-plugin-msaview.esm.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["global-externals:@jbrowse/core/Plugin", "global-externals:@jbrowse/core/pluggableElementTypes/ViewType", "../src/index.ts", "../node_modules/.pnpm/@mui+icons-material@9.4.0_@mui+material@9.4.0_@emotion+react@11.14.0_@types+react@19.3._3766ec55733e6b150ee672ad270cc236/node_modules/@mui/icons-material/GridOn.mjs", "../src/AddHighlightModel/index.tsx", "../src/MsaViewPanel/isMsaView.ts", "../src/AddHighlightModel/HighlightComponents.tsx", "../src/AddHighlightModel/GenomeMouseoverHighlight.tsx", "../src/AddHighlightModel/util.ts", "../src/AddHighlightModel/MsaToGenomeHighlight.tsx", "../src/AddHighlightModel/connectedHighlights.ts", "../src/BgzipFastaMsaAdapter/index.ts", "../src/BgzipFastaMsaAdapter/configSchema.ts", "../src/LaunchMsaView/index.ts", "../node_modules/.pnpm/@mui+icons-material@9.4.0_@mui+material@9.4.0_@emotion+react@11.14.0_@types+react@19.3._3766ec55733e6b150ee672ad270cc236/node_modules/@mui/icons-material/Add.mjs", "../src/LaunchMsaView/codingFeature.ts", "../src/LaunchMsaView/launchTarget.ts", "../src/LaunchMsaViewExtensionPoint/index.ts", "../src/MsaViewPanel/index.ts", "../src/version.ts"],
|
|
4
|
-
"sourcesContent": ["module.exports = JBrowseExports[\"@jbrowse/core/Plugin\"];", "module.exports = JBrowseExports[\"@jbrowse/core/pluggableElementTypes/ViewType\"];", "import Plugin from '@jbrowse/core/Plugin'\nimport { ConfigurationSchema } from '@jbrowse/core/configuration'\nimport { isAbstractMenuManager } from '@jbrowse/core/util'\nimport { types } from '@jbrowse/mobx-state-tree'\nimport GridOn from '@mui/icons-material/GridOn'\n\nimport AddHighlightModelF from './AddHighlightModel'\nimport BgzipFastaMsaAdapterF from './BgzipFastaMsaAdapter'\nimport LaunchMsaViewF from './LaunchMsaView'\nimport LaunchMsaViewExtensionPointF from './LaunchMsaViewExtensionPoint'\nimport MsaViewF from './MsaViewPanel'\nimport { launchMsaView } from './utils/launchMsaView'\nimport { version } from './version'\n\nimport type PluginManager from '@jbrowse/core/PluginManager'\nimport type { AbstractSessionModel } from '@jbrowse/core/util'\n\nexport default class MsaViewPlugin extends Plugin {\n name = 'MsaViewPlugin'\n version = version\n\n install(pluginManager: PluginManager) {\n MsaViewF(pluginManager)\n LaunchMsaViewF(pluginManager)\n LaunchMsaViewExtensionPointF(pluginManager)\n AddHighlightModelF(pluginManager)\n BgzipFastaMsaAdapterF(pluginManager)\n }\n\n configure(pluginManager: PluginManager) {\n // a throw here error-pages the whole app, and a menu entry is not worth a\n // session\n try {\n if (isAbstractMenuManager(pluginManager.rootModel)) {\n pluginManager.rootModel.appendToSubMenu(['Add'], {\n label: 'Multiple sequence alignment view',\n icon: GridOn,\n onClick: (session: AbstractSessionModel) => {\n // stacked, by default: nothing was launched from, so there is no\n // connected view for it to sit beside\n launchMsaView(session, {}).catch((e: unknown) => {\n console.error(e)\n session.notifyError(`${e}`, e)\n })\n },\n })\n }\n } catch (e) {\n console.error(\n 'jbrowse-plugin-msaview: could not add the Add menu item',\n e,\n )\n }\n }\n\n rootConfigurationSchema = (pluginManager: PluginManager) => {\n return {\n msa: ConfigurationSchema('MSA', {\n datasets: types.maybe(\n types.array(\n ConfigurationSchema('MSAEntry', {\n datasetId: {\n type: 'string',\n defaultValue: '',\n },\n description: {\n type: 'string',\n defaultValue: '',\n },\n name: {\n type: 'string',\n defaultValue: '',\n },\n adapter: pluginManager.pluggableConfigSchemaType('adapter'),\n }),\n ),\n ),\n }),\n }\n }\n}\n", "\"use client\";\n\nimport createSvgIcon from \"./utils/createSvgIcon.mjs\";\nimport { jsx as _jsx } from \"react/jsx-runtime\";\nexport default createSvgIcon(/*#__PURE__*/_jsx(\"path\", {\n d: \"M20 2H4c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2M8 20H4v-4h4zm0-6H4v-4h4zm0-6H4V4h4zm6 12h-4v-4h4zm0-6h-4v-4h4zm0-6h-4V4h4zm6 12h-4v-4h4zm0-6h-4v-4h4zm0-6h-4V4h4z\"\n}), 'GridOn');", "import React from 'react'\n\nimport { getSession } from '@jbrowse/core/util'\n\nimport { isMsaView } from '../MsaViewPanel/isMsaView'\nimport HighlightComponents from './HighlightComponents'\n\nimport type PluginManager from '@jbrowse/core/PluginManager'\n\nexport default function AddHighlightComponentsModelF(\n pluginManager: PluginManager,\n) {\n pluginManager.contributeToExtensionPoint(\n 'LinearGenomeView-TracksContainerComponent',\n ({ model }) => {\n const { views } = getSession(model)\n return views.some(v => isMsaView(v) && v.connectedViewId === model.id) ? (\n <HighlightComponents\n key=\"highlight_protein_viewer_msaview\"\n model={model}\n />\n ) : undefined\n },\n )\n}\n", "import type { JBrowsePluginMsaViewModel } from './model'\n\nexport function isMsaView(view: {\n type: string\n}): view is JBrowsePluginMsaViewModel {\n return view.type === 'MsaView'\n}\n", "import React from 'react'\n\nimport { observer } from 'mobx-react'\n\nimport GenomeMouseoverHighlight from './GenomeMouseoverHighlight'\nimport MsaToGenomeHighlight from './MsaToGenomeHighlight'\n\nimport type { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'\n\ntype LGV = LinearGenomeViewModel\n\nconst HighlightComponents = observer(function HighlightComponents2({\n model,\n}: {\n model: LGV\n}) {\n return (\n <>\n <MsaToGenomeHighlight model={model} />\n <GenomeMouseoverHighlight model={model} />\n </>\n )\n})\n\nexport default HighlightComponents\n", "import React from 'react'\n\nimport { getSession } from '@jbrowse/core/util'\nimport { observer } from 'mobx-react'\n\nimport { isMsaView } from '../MsaViewPanel/isMsaView'\nimport { hasHoverPosition, useStyles } from './util'\n\nimport type { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'\n\nconst GenomeMouseoverHighlight = observer(function ({\n model,\n}: {\n model: LinearGenomeViewModel\n}) {\n const { hovered, views } = getSession(model)\n const hasMsaView = views.some(\n s => isMsaView(s) && s.connectedViewId === model.id,\n )\n return hasMsaView && hasHoverPosition(hovered) ? (\n <GenomeMouseoverHighlightRenderer model={model} hovered={hovered} />\n ) : null\n})\n\nconst GenomeMouseoverHighlightRenderer = observer(function ({\n model,\n hovered,\n}: {\n model: LinearGenomeViewModel\n hovered: { hoverPosition: { coord: number; refName: string } }\n}) {\n const { classes } = useStyles()\n const { offsetPx } = model\n const { coord, refName } = hovered.hoverPosition\n\n const s = model.bpToPx({ refName, coord: coord - 1 })\n const e = model.bpToPx({ refName, coord })\n\n if (s && e) {\n const width = Math.max(Math.abs(e.offsetPx - s.offsetPx), 4)\n const left = Math.min(s.offsetPx, e.offsetPx) - offsetPx\n return <div className={classes.highlight} style={{ left, width }} />\n }\n\n return null\n})\n\nexport default GenomeMouseoverHighlight\n", "import { makeStyles } from 'tss-react/mui'\n\nexport { hasHoverPosition } from '../MsaViewPanel/util'\n\nexport const useStyles = makeStyles()({\n highlight: {\n height: '100%',\n background: 'rgba(255,255,0,0.2)',\n border: '1px solid rgba(50,50,0,0.2)',\n position: 'absolute',\n zIndex: 99,\n textAlign: 'center',\n pointerEvents: 'none',\n overflow: 'hidden',\n },\n})\n", "import React from 'react'\n\nimport { getSession } from '@jbrowse/core/util'\nimport { observer } from 'mobx-react'\n\nimport { connectedHighlights } from './connectedHighlights'\nimport { hasHoverPosition, useStyles } from './util'\n\nimport type { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'\n\ntype LGV = LinearGenomeViewModel\n\nconst MsaToGenomeHighlight = observer(function MsaToGenomeHighlight2({\n model,\n}: {\n model: LGV\n}) {\n const { views, hovered } = getSession(model)\n const highlights = connectedHighlights(\n views,\n model.id,\n hasHoverPosition(hovered),\n )\n\n return highlights.length ? (\n <MsaToGenomeHighlightRenderer model={model} highlights={highlights} />\n ) : null\n})\n\nconst MsaToGenomeHighlightRenderer = observer(function ({\n model,\n highlights,\n}: {\n model: LGV\n highlights: { refName: string; start: number; end: number }[]\n}) {\n const { classes } = useStyles()\n const { offsetPx } = model\n\n return (\n <>\n {highlights.map(r => {\n // bpToPx matches refNames exactly, so canonicalizing \"chr17\" to \"17\"\n // would miss a view whose regions say \"chr17\"\n const s = model.bpToPx({ refName: r.refName, coord: r.start })\n const e = model.bpToPx({ refName: r.refName, coord: r.end })\n if (s && e) {\n const width = Math.max(Math.abs(e.offsetPx - s.offsetPx), 4)\n const left = Math.min(s.offsetPx, e.offsetPx) - offsetPx\n return (\n <div\n key={`${r.refName}-${r.start}-${r.end}`}\n className={classes.highlight}\n style={{ left, width }}\n />\n )\n }\n return null\n })}\n </>\n )\n})\n\nexport default MsaToGenomeHighlight\n", "interface Region {\n refName: string\n start: number\n end: number\n}\n\ninterface LinkedView {\n type: string\n connectedViewId?: string\n connectedClickHighlights: Region[]\n connectedHoverHighlights: Region[]\n}\n\nexport function connectedHighlights(\n views: readonly { type: string }[],\n genomeViewId: string,\n genomeHovered: boolean,\n) {\n const regions = views\n .filter(isLinkedMsaView)\n .filter(v => v.connectedViewId === genomeViewId)\n .flatMap(v => [\n ...v.connectedClickHighlights,\n ...(genomeHovered ? [] : v.connectedHoverHighlights),\n ])\n return [\n ...new Map(\n regions.map(r => [`${r.refName}:${r.start}-${r.end}`, r]),\n ).values(),\n ]\n}\n\nfunction isLinkedMsaView(view: { type: string }): view is LinkedView {\n return view.type === 'MsaView'\n}\n", "import { AdapterType } from '@jbrowse/core/pluggableElementTypes'\n\nimport configSchemaF from './configSchema'\n\nimport type PluginManager from '@jbrowse/core/PluginManager'\n\nexport default function BgzipFastaMsaAdapterF(pluginManager: PluginManager) {\n return pluginManager.addAdapterType(() => {\n return new AdapterType({\n name: 'BgzipFastaMsaAdapter',\n configSchema: configSchemaF(pluginManager),\n adapterMetadata: {\n hiddenFromGUI: true,\n },\n getAdapterClass: () =>\n import('./BgzipFastaMsaAdapter').then(t => t.default),\n })\n })\n}\n", "import { ConfigurationSchema } from '@jbrowse/core/configuration'\n\nimport type PluginManager from '@jbrowse/core/PluginManager'\n\nexport default function configSchemaF(pluginManager: PluginManager) {\n const base = pluginManager.getAdapterType('BgzipFastaAdapter')\n return ConfigurationSchema(\n 'BgzipFastaMsaAdapter',\n {\n msaRegex: {\n type: 'string',\n defaultValue: '_',\n },\n },\n {\n baseConfiguration: base.configSchema,\n },\n )\n}\n", "import { lazy } from 'react'\n\nimport { getContainingTrack, getSession } from '@jbrowse/core/util'\nimport AddIcon from '@mui/icons-material/Add'\n\nimport { isCodingFeature } from './codingFeature'\nimport { launchTarget } from './launchTarget'\n\nimport type { DisplayModel, MenuTarget } from './launchTarget'\nimport type PluginManager from '@jbrowse/core/PluginManager'\nimport type { PluggableElementType } from '@jbrowse/core/pluggableElementTypes'\nimport type DisplayType from '@jbrowse/core/pluggableElementTypes/DisplayType'\nimport type { Feature } from '@jbrowse/core/util'\nimport type { IAnyModelType } from '@jbrowse/mobx-state-tree'\n\nconst LaunchMsaViewDialog = lazy(\n () => import('./components/LaunchMsaViewDialog'),\n)\n\nfunction isDisplay(elt: { name: string }): elt is DisplayType {\n return elt.name === 'LinearBasicDisplay'\n}\n\n// Walking to the track and the session at click time, not while the menu is\n// built: contextMenuItems runs on every right-click and, on a host whose base\n// method reads `this`, is the one place a plugin can take the whole menu down.\n// Keeping it to a pure read of the display is also what lets a test call it.\nfunction featureName(feature: Feature) {\n return feature.get('name') ?? feature.get('id') ?? 'This feature'\n}\n\nfunction openDialog(self: DisplayModel, target: MenuTarget) {\n const track = getContainingTrack(self)\n const session = getSession(track)\n const { preferredTranscriptId } = target\n const open = (feature: Feature) => {\n session.queueDialog(handleClose => [\n LaunchMsaViewDialog,\n { model: track, handleClose, feature, preferredTranscriptId },\n ])\n }\n target\n .fetchFeature()\n .then(feature => {\n if (!feature) {\n session.notify('Could not load feature for MSA view', 'warning')\n } else if (!isCodingFeature(feature)) {\n // the canvas hit test carries a type and nothing else, so this is the\n // first point at which the CDS can be looked for. Saying so beats\n // opening a dialog whose Submit never leaves grey.\n session.notify(\n `${featureName(feature)} has no coding sequence, so there is no protein to align`,\n 'info',\n )\n } else {\n open(feature)\n }\n })\n .catch((e: unknown) => {\n session.notifyError(`${e}`, e)\n })\n}\n\nexport function extendStateModel(stateModel: IAnyModelType) {\n return stateModel.views((self: DisplayModel) => {\n const superContextMenuItems = self.contextMenuItems\n return {\n contextMenuItems() {\n const target = launchTarget(self)\n return [\n // a detached call leaves `this` undefined for a host method that\n // reads it, and the throw takes the host's own rows with it\n ...superContextMenuItems.call(self),\n ...(target\n ? [\n {\n label: 'Launch MSA view',\n icon: AddIcon,\n onClick: () => {\n openDialog(self, target)\n },\n },\n ]\n : []),\n ]\n },\n }\n })\n}\n\nexport default function LaunchMsaViewF(pluginManager: PluginManager) {\n pluginManager.addToExtensionPoint(\n 'Core-extendPluggableElement',\n (elt: PluggableElementType) => {\n if (isDisplay(elt)) {\n elt.stateModel = extendStateModel(elt.stateModel)\n }\n return elt\n },\n )\n}\n", "\"use client\";\n\nimport createSvgIcon from \"./utils/createSvgIcon.mjs\";\nimport { jsx as _jsx } from \"react/jsx-runtime\";\nexport default createSvgIcon(/*#__PURE__*/_jsx(\"path\", {\n d: \"M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6z\"\n}), 'Add');", "import type { Feature } from '@jbrowse/core/util'\n\nfunction isCDS(feature: Feature) {\n return feature.get('type')?.toLowerCase() === 'cds'\n}\n\n// The feature itself counts: a viral polyprotein hangs its cleavage products\n// off its CDS rather than off further CDSs.\nexport function isCodingFeature(feature: Feature): boolean {\n return isCDS(feature) || !!feature.get('subfeatures')?.some(isCodingFeature)\n}\n", "import { isGeneLikeType } from '@jbrowse/core/util'\n\nimport type { MenuItem } from '@jbrowse/core/ui'\nimport type { Feature } from '@jbrowse/core/util'\n\nexport interface ContextMenuInfo {\n item: { featureId: string; type?: string }\n // the isoform under the pointer when the click landed on a gene's child\n subfeature?: { featureId: string }\n displayedRegionIndex: number\n}\n\nexport interface DisplayModel {\n contextMenuItems: () => MenuItem[]\n contextMenuInfo?: ContextMenuInfo\n fetchFullFeature: (\n featureId: string,\n displayedRegionIndex: number,\n ) => Promise<Feature | undefined>\n}\n\n/**\n * What the menu item launches on. The hit test carries a type and an id, so\n * whether the feature codes for anything can only be answered after the fetch,\n * and the caller answers it there.\n */\nexport interface MenuTarget {\n fetchFeature: () => Promise<Feature | undefined>\n /**\n * The isoform the click actually landed on, when the dialog opens on its\n * gene. Climbing to the gene is what puts every isoform in the picker, and it\n * threw away which one the user pointed at: the picker then opened on the\n * longest transcript.\n */\n preferredTranscriptId?: string\n}\n\n// Read off the clicked item rather than off the display: LinearBasicDisplay's\n// `isGeneLike` getter was inlined into its own `contextMenuItems`\n// (jbrowse-components 684142b3), and gating on it silently took the item off\n// every gene track.\nexport function launchTarget(self: DisplayModel): MenuTarget | undefined {\n const info = self.contextMenuInfo\n return info && isGeneLikeType(info.item.type)\n ? {\n fetchFeature: () =>\n self.fetchFullFeature(info.item.featureId, info.displayedRegionIndex),\n preferredTranscriptId: info.subfeature?.featureId,\n }\n : undefined\n}\n", "import { launchMsaView } from '../utils/launchMsaView'\n\nimport type { BlastParams, OrthologParams } from '../MsaViewPanel/model'\nimport type { MsaViewPlacement } from '../utils/workspaces'\nimport type PluginManager from '@jbrowse/core/PluginManager'\nimport type { AbstractSessionModel } from '@jbrowse/core/util'\nimport type {\n Clade,\n ColumnTrackShorthand,\n Encoding,\n HighlightShorthand,\n RegionShorthand,\n ResidueMapping,\n RowPanelSpec,\n} from 'react-msaview'\n\ninterface LaunchMsaViewArgs {\n session: AbstractSessionModel\n /**\n * Shorthand, see react-msaview's docs/layers.md: `msa` and `tree` take a\n * url or the text itself, and `query` names the row the spec is about, the\n * `querySeqName` and the `row` of every highlight, region and column track\n * that names none\n */\n msa?: string\n tree?: string\n query?: string\n data?: { msa: string; tree?: string }\n msaFileLocation?: { uri: string }\n msaIndexedLocation?: { uri: string }\n msaName?: string\n treeFileLocation?: { uri: string }\n connectedViewId?: string\n connectedFeature?: Record<string, unknown>\n /**\n * The short form of `connectedFeature`: a transcript id looked up in the\n * connected genome view's open tracks at launch, and translated to become\n * the query row of a `searchParams` or `orthologParams` launch that names\n * no `proteinSequence` of its own.\n */\n connectedTranscript?: string\n displayName?: string\n colorSchemeName?: string\n colWidth?: number\n rowHeight?: number\n treeAreaWidth?: number\n treeWidth?: number\n drawNodeBubbles?: boolean\n labelsAlignRight?: boolean\n showBranchLen?: boolean\n querySeqName?: string\n /**\n * Transcript residues before the query row's first residue, for a row that\n * carries only part of the protein -- what BLAST reports when it trims the\n * query to the aligned region. Genome navigation is off by this much without\n * it.\n */\n querySeqOffset?: number\n highlightColumns?: number[]\n /**\n * Labeled highlights in 1-based inclusive coordinates: `{row, start, end}`\n * for residues of that row, `{start, end}` for alignment columns, `{rows}`\n * for whole rows. Prefer this over `highlightColumns`: a residue range stays\n * put when `allowedGappyness` changes the visible columns.\n */\n highlights?: HighlightShorthand[]\n /** per-column tracks supplied as data, see react-msaview's docs/layers.md */\n columnTracks?: ColumnTrackShorthand[]\n /**\n * Where the view opens, in `highlights` coordinates: `{row, start, end}`\n * zooms onto residues of that row, `{start, end}` onto alignment columns.\n * Applied once the alignment has loaded, then dropped from the session.\n */\n region?: RegionShorthand\n /**\n * Passed through untouched as react-msaview snapshot properties; see its\n * docs/layers.md. A bare `{ uri }` is enough for the two filehandles.\n */\n clades?: Clade[]\n encodings?: Encoding[]\n rowPanels?: RowPanelSpec[]\n relativeTo?: string\n residueMappings?: ResidueMapping[]\n trackHeights?: Record<string, number>\n turnedOffFeatures?: Record<string, boolean>\n gffFilehandle?: { uri: string }\n treeMetadataFilehandle?: { uri: string }\n /**\n * Build the alignment from NCBI orthologs at launch time instead of naming a\n * file: `{ taxId, geneCandidates }` is enough, and `taxa` and\n * `proteinSequence` both default (see OrthologParams). This is the launch\n * dialog's Orthologs tab, reachable from a session spec \u2014 so a link can say\n * \"NLRP1 across species\" and the view builds it.\n */\n orthologParams?: OrthologParams\n /**\n * Search for the alignment at launch: the dialog's BLAST tab reached\n * declaratively. `{ searchProgram: 'phmmer', blastDatabase: 'rp15',\n * accession: 'P04637' }` is a whole request. The query comes from\n * `proteinSequence`, else fetched for `accession`, else translated from the\n * view's `connectedTranscript`. Stored on the model as `blastParams`, the\n * name the dialog has always used.\n */\n searchParams?: BlastParams\n /**\n * Hide any column gappier than this percentage, 100 being \"hide nothing\".\n * A native react-msaview property, named here because it is the one setting\n * that decides WHICH columns a freshly launched view opens on: proteins that\n * differ in length put one row's private N-terminal extension at column 0,\n * and everything else is gap there. Anything else react-msaview takes as a\n * snapshot property passes through the same way.\n */\n allowedGappyness?: number\n /**\n * Where the view lands: `stack` (default), `splitRight` or `newTab`. The\n * declarative half of the launch \u2014 a link states the arrangement it wants\n * instead of the reader dragging the view into place.\n *\n * The default is `stack` and has to stay `stack`: it is what every link\n * written before this key existed already does, and the only thing an\n * embedded session can do. The launch DIALOG defaults to `splitRight`\n * instead (see `DEFAULT_LAUNCH_PLACEMENT`), because a launch from a gene\n * feature is a connected pair and reads as a split.\n *\n * A spec that arranges more than these two views should use the host's own\n * `layout` key instead, which states the whole tree and is applied after\n * every view in the spec has launched. Both may be given; `layout` wins,\n * being the later and more specific statement.\n */\n placement?: MsaViewPlacement\n}\n\n/**\n * `msa` and `tree` onto the plugin's own sources, so a url still goes through\n * `init.msaUrl` and its format sniffing, and `query` onto `querySeqName`\n */\nfunction withLongSources({ msa, tree, ...args }: LaunchMsaViewArgs) {\n const inlineMsa = msa?.includes('\\n') ? msa : undefined\n const inlineTree = tree?.trimStart().startsWith('(') ? tree : undefined\n const data =\n inlineMsa || inlineTree\n ? {\n ...args.data,\n ...(inlineMsa ? { msa: inlineMsa } : {}),\n ...(inlineTree ? { tree: inlineTree } : {}),\n }\n : args.data\n return {\n ...args,\n data: data as LaunchMsaViewArgs['data'],\n ...(msa && !inlineMsa ? { msaFileLocation: { uri: msa } } : {}),\n ...(tree && !inlineTree ? { treeFileLocation: { uri: tree } } : {}),\n querySeqName: args.querySeqName ?? args.query,\n }\n}\n\nexport default function LaunchMsaViewExtensionPointF(\n pluginManager: PluginManager,\n) {\n pluginManager.addToExtensionPoint(\n 'LaunchView-MsaView',\n async (args: LaunchMsaViewArgs) => {\n const {\n session,\n query,\n data,\n msaFileLocation,\n msaIndexedLocation,\n msaName,\n treeFileLocation,\n querySeqName,\n searchParams,\n ...rest\n } = withLongSources(args)\n\n // `orthologParams` and `searchParams` name no alignment at all \u2014 the\n // view builds one at launch, which is the dialog's Orthologs and BLAST\n // tabs reached declaratively.\n if (\n !data &&\n !msaFileLocation &&\n !msaIndexedLocation &&\n !rest.orthologParams &&\n !searchParams\n ) {\n throw new Error(\n 'No MSA data, file location, orthologParams or searchParams provided when launching MSA view',\n )\n }\n\n // inline data and the tree URL are native react-msaview snapshot props, set\n // directly, and so is orthologParams (the model's own autorun picks it up).\n // Only sources needing launch-time resolution go through `init`: msaUrl\n // (AlphaFold sniff) and the name-indexed bgzip block (no native loader).\n //\n // An init whose every field is undefined is still a truthy object, and\n // MsaViewPanel reads any init as a launch in flight, so an inline-data\n // launch flashed \"Loading alignment\" until processInit cleared it.\n const init = {\n msaUrl: msaFileLocation?.uri,\n msaIndexedLocation,\n msaName,\n querySeqName,\n }\n const { expandSpec } = await import('react-msaview')\n await launchMsaView(session, {\n ...expandSpec({ ...rest, query }),\n ...(searchParams ? { blastParams: searchParams } : {}),\n data,\n ...(treeFileLocation\n ? {\n treeFilehandle: {\n ...treeFileLocation,\n locationType: 'UriLocation',\n },\n }\n : {}),\n ...(Object.values(init).some(v => v !== undefined) ? { init } : {}),\n })\n\n return args\n },\n )\n}\n", "import { lazy } from 'react'\n\nimport ViewType from '@jbrowse/core/pluggableElementTypes/ViewType'\n\nimport type PluginManager from '@jbrowse/core/PluginManager'\n\nconst MsaViewPanel = lazy(() => import('./components/MsaViewPanel'))\n\nexport default function MsaViewF(pluginManager: PluginManager) {\n pluginManager.addViewType(() => {\n return new ViewType({\n name: 'MsaView',\n // a thunk, so react-msaview loads with the first view rather than with\n // the plugin; addView refuses the type until then, hence launchView\n stateModel: () => import('./model').then(m => m.default()),\n ReactComponent: MsaViewPanel,\n })\n })\n}\n", "export const version = '5.0.0'\n"],
|
|
4
|
+
"sourcesContent": ["module.exports = JBrowseExports[\"@jbrowse/core/Plugin\"];", "module.exports = JBrowseExports[\"@jbrowse/core/pluggableElementTypes/ViewType\"];", "import Plugin from '@jbrowse/core/Plugin'\nimport { ConfigurationSchema } from '@jbrowse/core/configuration'\nimport { isAbstractMenuManager } from '@jbrowse/core/util'\nimport { types } from '@jbrowse/mobx-state-tree'\nimport GridOn from '@mui/icons-material/GridOn'\n\nimport AddHighlightModelF from './AddHighlightModel'\nimport BgzipFastaMsaAdapterF from './BgzipFastaMsaAdapter'\nimport LaunchMsaViewF from './LaunchMsaView'\nimport LaunchMsaViewExtensionPointF from './LaunchMsaViewExtensionPoint'\nimport MsaViewF from './MsaViewPanel'\nimport { launchMsaView } from './utils/launchMsaView'\nimport { version } from './version'\n\nimport type PluginManager from '@jbrowse/core/PluginManager'\nimport type { AbstractSessionModel } from '@jbrowse/core/util'\n\nexport default class MsaViewPlugin extends Plugin {\n name = 'MsaViewPlugin'\n version = version\n\n install(pluginManager: PluginManager) {\n MsaViewF(pluginManager)\n LaunchMsaViewF(pluginManager)\n LaunchMsaViewExtensionPointF(pluginManager)\n AddHighlightModelF(pluginManager)\n BgzipFastaMsaAdapterF(pluginManager)\n }\n\n configure(pluginManager: PluginManager) {\n // a throw here error-pages the whole app, and a menu entry is not worth a\n // session\n try {\n if (isAbstractMenuManager(pluginManager.rootModel)) {\n pluginManager.rootModel.appendToSubMenu(['Add'], {\n label: 'Multiple sequence alignment view',\n icon: GridOn,\n onClick: (session: AbstractSessionModel) => {\n // stacked, by default: nothing was launched from, so there is no\n // connected view for it to sit beside\n launchMsaView(session, {}).catch((e: unknown) => {\n console.error(e)\n session.notifyError(`${e}`, e)\n })\n },\n })\n }\n } catch (e) {\n console.error(\n 'jbrowse-plugin-msaview: could not add the Add menu item',\n e,\n )\n }\n }\n\n rootConfigurationSchema = (pluginManager: PluginManager) => {\n return {\n msa: ConfigurationSchema('MSA', {\n datasets: types.maybe(\n types.array(\n ConfigurationSchema('MSAEntry', {\n datasetId: {\n type: 'string',\n defaultValue: '',\n },\n description: {\n type: 'string',\n defaultValue: '',\n },\n name: {\n type: 'string',\n defaultValue: '',\n },\n adapter: pluginManager.pluggableConfigSchemaType('adapter'),\n }),\n ),\n ),\n }),\n }\n }\n}\n", "\"use client\";\n\nimport createSvgIcon from \"./utils/createSvgIcon.mjs\";\nimport { jsx as _jsx } from \"react/jsx-runtime\";\nexport default createSvgIcon(/*#__PURE__*/_jsx(\"path\", {\n d: \"M20 2H4c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2M8 20H4v-4h4zm0-6H4v-4h4zm0-6H4V4h4zm6 12h-4v-4h4zm0-6h-4v-4h4zm0-6h-4V4h4zm6 12h-4v-4h4zm0-6h-4v-4h4zm0-6h-4V4h4z\"\n}), 'GridOn');", "import React from 'react'\n\nimport { getSession } from '@jbrowse/core/util'\n\nimport { isMsaView } from '../MsaViewPanel/isMsaView'\nimport HighlightComponents from './HighlightComponents'\n\nimport type PluginManager from '@jbrowse/core/PluginManager'\n\nexport default function AddHighlightComponentsModelF(\n pluginManager: PluginManager,\n) {\n pluginManager.contributeToExtensionPoint(\n 'LinearGenomeView-TracksContainerComponent',\n ({ model }) => {\n const { views } = getSession(model)\n return views.some(v => isMsaView(v) && v.connectedViewId === model.id) ? (\n <HighlightComponents\n key=\"highlight_protein_viewer_msaview\"\n model={model}\n />\n ) : undefined\n },\n )\n}\n", "import type { JBrowsePluginMsaViewModel } from './model'\n\nexport function isMsaView(view: {\n type: string\n}): view is JBrowsePluginMsaViewModel {\n return view.type === 'MsaView'\n}\n", "import React from 'react'\n\nimport { observer } from 'mobx-react'\n\nimport GenomeMouseoverHighlight from './GenomeMouseoverHighlight'\nimport MsaToGenomeHighlight from './MsaToGenomeHighlight'\n\nimport type { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'\n\ntype LGV = LinearGenomeViewModel\n\nconst HighlightComponents = observer(function HighlightComponents2({\n model,\n}: {\n model: LGV\n}) {\n return (\n <>\n <MsaToGenomeHighlight model={model} />\n <GenomeMouseoverHighlight model={model} />\n </>\n )\n})\n\nexport default HighlightComponents\n", "import React from 'react'\n\nimport { getSession } from '@jbrowse/core/util'\nimport { observer } from 'mobx-react'\n\nimport { isMsaView } from '../MsaViewPanel/isMsaView'\nimport { hasHoverPosition, useStyles } from './util'\n\nimport type { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'\n\nconst GenomeMouseoverHighlight = observer(function ({\n model,\n}: {\n model: LinearGenomeViewModel\n}) {\n const { hovered, views } = getSession(model)\n const hasMsaView = views.some(\n s => isMsaView(s) && s.connectedViewId === model.id,\n )\n return hasMsaView && hasHoverPosition(hovered) ? (\n <GenomeMouseoverHighlightRenderer model={model} hovered={hovered} />\n ) : null\n})\n\nconst GenomeMouseoverHighlightRenderer = observer(function ({\n model,\n hovered,\n}: {\n model: LinearGenomeViewModel\n hovered: { hoverPosition: { coord: number; refName: string } }\n}) {\n const { classes } = useStyles()\n const { offsetPx } = model\n const { coord, refName } = hovered.hoverPosition\n\n const s = model.bpToPx({ refName, coord: coord - 1 })\n const e = model.bpToPx({ refName, coord })\n\n if (s && e) {\n const width = Math.max(Math.abs(e.offsetPx - s.offsetPx), 4)\n const left = Math.min(s.offsetPx, e.offsetPx) - offsetPx\n return <div className={classes.highlight} style={{ left, width }} />\n }\n\n return null\n})\n\nexport default GenomeMouseoverHighlight\n", "import { makeStyles } from 'tss-react/mui'\n\nexport { hasHoverPosition } from '../MsaViewPanel/util'\n\nexport const useStyles = makeStyles()({\n highlight: {\n height: '100%',\n background: 'rgba(255,255,0,0.2)',\n border: '1px solid rgba(50,50,0,0.2)',\n position: 'absolute',\n zIndex: 99,\n textAlign: 'center',\n pointerEvents: 'none',\n overflow: 'hidden',\n },\n})\n", "import React from 'react'\n\nimport { getSession } from '@jbrowse/core/util'\nimport { observer } from 'mobx-react'\n\nimport { connectedHighlights } from './connectedHighlights'\nimport { hasHoverPosition, useStyles } from './util'\n\nimport type { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'\n\ntype LGV = LinearGenomeViewModel\n\nconst MsaToGenomeHighlight = observer(function MsaToGenomeHighlight2({\n model,\n}: {\n model: LGV\n}) {\n const { views, hovered } = getSession(model)\n const highlights = connectedHighlights(\n views,\n model.id,\n hasHoverPosition(hovered),\n )\n\n return highlights.length ? (\n <MsaToGenomeHighlightRenderer model={model} highlights={highlights} />\n ) : null\n})\n\nconst MsaToGenomeHighlightRenderer = observer(function ({\n model,\n highlights,\n}: {\n model: LGV\n highlights: { refName: string; start: number; end: number }[]\n}) {\n const { classes } = useStyles()\n const { offsetPx } = model\n\n return (\n <>\n {highlights.map(r => {\n // bpToPx matches refNames exactly, so canonicalizing \"chr17\" to \"17\"\n // would miss a view whose regions say \"chr17\"\n const s = model.bpToPx({ refName: r.refName, coord: r.start })\n const e = model.bpToPx({ refName: r.refName, coord: r.end })\n if (s && e) {\n const width = Math.max(Math.abs(e.offsetPx - s.offsetPx), 4)\n const left = Math.min(s.offsetPx, e.offsetPx) - offsetPx\n return (\n <div\n key={`${r.refName}-${r.start}-${r.end}`}\n className={classes.highlight}\n style={{ left, width }}\n />\n )\n }\n return null\n })}\n </>\n )\n})\n\nexport default MsaToGenomeHighlight\n", "interface Region {\n refName: string\n start: number\n end: number\n}\n\ninterface LinkedView {\n type: string\n connectedViewId?: string\n connectedClickHighlights: Region[]\n connectedHoverHighlights: Region[]\n}\n\nexport function connectedHighlights(\n views: readonly { type: string }[],\n genomeViewId: string,\n genomeHovered: boolean,\n) {\n const regions = views\n .filter(isLinkedMsaView)\n .filter(v => v.connectedViewId === genomeViewId)\n .flatMap(v => [\n ...v.connectedClickHighlights,\n ...(genomeHovered ? [] : v.connectedHoverHighlights),\n ])\n return [\n ...new Map(\n regions.map(r => [`${r.refName}:${r.start}-${r.end}`, r]),\n ).values(),\n ]\n}\n\nfunction isLinkedMsaView(view: { type: string }): view is LinkedView {\n return view.type === 'MsaView'\n}\n", "import { AdapterType } from '@jbrowse/core/pluggableElementTypes'\n\nimport configSchemaF from './configSchema'\n\nimport type PluginManager from '@jbrowse/core/PluginManager'\n\nexport default function BgzipFastaMsaAdapterF(pluginManager: PluginManager) {\n return pluginManager.addAdapterType(() => {\n return new AdapterType({\n name: 'BgzipFastaMsaAdapter',\n configSchema: configSchemaF(pluginManager),\n adapterMetadata: {\n hiddenFromGUI: true,\n },\n getAdapterClass: () =>\n import('./BgzipFastaMsaAdapter').then(t => t.default),\n })\n })\n}\n", "import { ConfigurationSchema } from '@jbrowse/core/configuration'\n\nimport type PluginManager from '@jbrowse/core/PluginManager'\n\nexport default function configSchemaF(pluginManager: PluginManager) {\n const base = pluginManager.getAdapterType('BgzipFastaAdapter')\n return ConfigurationSchema(\n 'BgzipFastaMsaAdapter',\n {\n msaRegex: {\n type: 'string',\n defaultValue: '_',\n },\n },\n {\n baseConfiguration: base.configSchema,\n },\n )\n}\n", "import { lazy } from 'react'\n\nimport { getContainingTrack, getSession } from '@jbrowse/core/util'\nimport AddIcon from '@mui/icons-material/Add'\n\nimport { isCodingFeature } from './codingFeature'\nimport { launchTarget } from './launchTarget'\n\nimport type { DisplayModel, MenuTarget } from './launchTarget'\nimport type PluginManager from '@jbrowse/core/PluginManager'\nimport type { PluggableElementType } from '@jbrowse/core/pluggableElementTypes'\nimport type DisplayType from '@jbrowse/core/pluggableElementTypes/DisplayType'\nimport type { Feature } from '@jbrowse/core/util'\nimport type { IAnyModelType } from '@jbrowse/mobx-state-tree'\n\nconst LaunchMsaViewDialog = lazy(\n () => import('./components/LaunchMsaViewDialog'),\n)\n\nfunction isDisplay(elt: { name: string }): elt is DisplayType {\n return elt.name === 'LinearBasicDisplay'\n}\n\n// Walking to the track and the session at click time, not while the menu is\n// built: contextMenuItems runs on every right-click and, on a host whose base\n// method reads `this`, is the one place a plugin can take the whole menu down.\n// Keeping it to a pure read of the display is also what lets a test call it.\nfunction featureName(feature: Feature) {\n return feature.get('name') ?? feature.get('id') ?? 'This feature'\n}\n\nfunction openDialog(self: DisplayModel, target: MenuTarget) {\n const track = getContainingTrack(self)\n const session = getSession(track)\n const { preferredTranscriptId } = target\n const open = (feature: Feature) => {\n session.queueDialog(handleClose => [\n LaunchMsaViewDialog,\n { model: track, handleClose, feature, preferredTranscriptId },\n ])\n }\n target\n .fetchFeature()\n .then(feature => {\n if (!feature) {\n session.notify('Could not load feature for MSA view', 'warning')\n } else if (!isCodingFeature(feature)) {\n // the canvas hit test carries a type and nothing else, so this is the\n // first point at which the CDS can be looked for. Saying so beats\n // opening a dialog whose Submit never leaves grey.\n session.notify(\n `${featureName(feature)} has no coding sequence, so there is no protein to align`,\n 'info',\n )\n } else {\n open(feature)\n }\n })\n .catch((e: unknown) => {\n session.notifyError(`${e}`, e)\n })\n}\n\nexport function extendStateModel(stateModel: IAnyModelType) {\n return stateModel.views((self: DisplayModel) => {\n const superContextMenuItems = self.contextMenuItems\n return {\n contextMenuItems() {\n const target = launchTarget(self)\n return [\n // a detached call leaves `this` undefined for a host method that\n // reads it, and the throw takes the host's own rows with it\n ...superContextMenuItems.call(self),\n ...(target\n ? [\n {\n label: 'Launch MSA view',\n icon: AddIcon,\n onClick: () => {\n openDialog(self, target)\n },\n },\n ]\n : []),\n ]\n },\n }\n })\n}\n\nexport default function LaunchMsaViewF(pluginManager: PluginManager) {\n pluginManager.addToExtensionPoint(\n 'Core-extendPluggableElement',\n (elt: PluggableElementType) => {\n if (isDisplay(elt)) {\n elt.stateModel = extendStateModel(elt.stateModel)\n }\n return elt\n },\n )\n}\n", "\"use client\";\n\nimport createSvgIcon from \"./utils/createSvgIcon.mjs\";\nimport { jsx as _jsx } from \"react/jsx-runtime\";\nexport default createSvgIcon(/*#__PURE__*/_jsx(\"path\", {\n d: \"M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6z\"\n}), 'Add');", "import type { Feature } from '@jbrowse/core/util'\n\nfunction isCDS(feature: Feature) {\n return feature.get('type')?.toLowerCase() === 'cds'\n}\n\n// The feature itself counts: a viral polyprotein hangs its cleavage products\n// off its CDS rather than off further CDSs.\nexport function isCodingFeature(feature: Feature): boolean {\n return isCDS(feature) || !!feature.get('subfeatures')?.some(isCodingFeature)\n}\n", "import { isGeneLikeType } from '@jbrowse/core/util'\n\nimport type { MenuItem } from '@jbrowse/core/ui'\nimport type { Feature } from '@jbrowse/core/util'\n\nexport interface ContextMenuInfo {\n item: { featureId: string; type?: string }\n // the isoform under the pointer when the click landed on a gene's child\n subfeature?: { featureId: string }\n displayedRegionIndex: number\n}\n\nexport interface DisplayModel {\n contextMenuItems: () => MenuItem[]\n contextMenuInfo?: ContextMenuInfo\n fetchFullFeature: (\n featureId: string,\n displayedRegionIndex: number,\n ) => Promise<Feature | undefined>\n}\n\n/**\n * What the menu item launches on. The hit test carries a type and an id, so\n * whether the feature codes for anything can only be answered after the fetch,\n * and the caller answers it there.\n */\nexport interface MenuTarget {\n fetchFeature: () => Promise<Feature | undefined>\n /**\n * The isoform the click actually landed on, when the dialog opens on its\n * gene. Climbing to the gene is what puts every isoform in the picker, and it\n * threw away which one the user pointed at: the picker then opened on the\n * longest transcript.\n */\n preferredTranscriptId?: string\n}\n\n// Read off the clicked item rather than off the display: LinearBasicDisplay's\n// `isGeneLike` getter was inlined into its own `contextMenuItems`\n// (jbrowse-components 684142b3), and gating on it silently took the item off\n// every gene track.\nexport function launchTarget(self: DisplayModel): MenuTarget | undefined {\n const info = self.contextMenuInfo\n return info && isGeneLikeType(info.item.type)\n ? {\n fetchFeature: () =>\n self.fetchFullFeature(info.item.featureId, info.displayedRegionIndex),\n preferredTranscriptId: info.subfeature?.featureId,\n }\n : undefined\n}\n", "import { launchMsaView } from '../utils/launchMsaView'\n\nimport type { BlastParams, OrthologParams } from '../MsaViewPanel/model'\nimport type { MsaViewPlacement } from '../utils/workspaces'\nimport type PluginManager from '@jbrowse/core/PluginManager'\nimport type { AbstractSessionModel } from '@jbrowse/core/util'\nimport type {\n Clade,\n ColumnTrackShorthand,\n Encoding,\n HighlightShorthand,\n RegionShorthand,\n ResidueMapping,\n RowPanelSpec,\n} from 'react-msaview'\n\ninterface LaunchMsaViewArgs {\n session: AbstractSessionModel\n /**\n * Shorthand, see react-msaview's docs/layers.md: `msa` and `tree` take a\n * url or the text itself, and `query` names the row the spec is about, the\n * `querySeqName` and the `row` of every highlight, region and column track\n * that names none\n */\n msa?: string\n tree?: string\n query?: string\n data?: { msa: string; tree?: string }\n msaFileLocation?: { uri: string }\n msaIndexedLocation?: { uri: string }\n msaName?: string\n treeFileLocation?: { uri: string }\n connectedViewId?: string\n connectedFeature?: Record<string, unknown>\n /**\n * The short form of `connectedFeature`: a transcript id looked up in the\n * connected genome view's open tracks at launch, and translated to become\n * the query row of a `searchParams` or `orthologParams` launch that names\n * no `proteinSequence` of its own.\n */\n connectedTranscript?: string\n displayName?: string\n colorSchemeName?: string\n colWidth?: number\n rowHeight?: number\n treeAreaWidth?: number\n treeWidth?: number\n drawNodeBubbles?: boolean\n labelsAlignRight?: boolean\n showBranchLen?: boolean\n querySeqName?: string\n /**\n * Transcript residues before the query row's first residue, for a row that\n * carries only part of the protein -- what BLAST reports when it trims the\n * query to the aligned region. Genome navigation is off by this much without\n * it.\n */\n querySeqOffset?: number\n highlightColumns?: number[]\n /**\n * Labeled highlights in 1-based inclusive coordinates: `{row, start, end}`\n * for residues of that row, `{start, end}` for alignment columns, `{rows}`\n * for whole rows. Prefer this over `highlightColumns`: a residue range stays\n * put when `allowedGappyness` changes the visible columns.\n */\n highlights?: HighlightShorthand[]\n /** per-column tracks supplied as data, see react-msaview's docs/layers.md */\n columnTracks?: ColumnTrackShorthand[]\n /**\n * Where the view opens, in `highlights` coordinates: `{row, start, end}`\n * zooms onto residues of that row, `{start, end}` onto alignment columns.\n * Applied once the alignment has loaded, then dropped from the session.\n */\n region?: RegionShorthand\n /**\n * Passed through untouched as react-msaview snapshot properties; see its\n * docs/layers.md. A bare `{ uri }` is enough for the two filehandles.\n */\n clades?: Clade[]\n encodings?: Encoding[]\n rowPanels?: RowPanelSpec[]\n relativeTo?: string\n residueMappings?: ResidueMapping[]\n trackHeights?: Record<string, number>\n turnedOffFeatures?: Record<string, boolean>\n gffFilehandle?: { uri: string }\n treeMetadataFilehandle?: { uri: string }\n /**\n * Build the alignment from NCBI orthologs at launch time instead of naming a\n * file: `{ taxId, geneCandidates }` is enough, and `taxa` and\n * `proteinSequence` both default (see OrthologParams). This is the launch\n * dialog's Orthologs tab, reachable from a session spec \u2014 so a link can say\n * \"NLRP1 across species\" and the view builds it.\n */\n orthologParams?: OrthologParams\n /**\n * Search for the alignment at launch: the dialog's BLAST tab reached\n * declaratively. `{ searchProgram: 'phmmer', blastDatabase: 'rp15',\n * accession: 'P04637' }` is a whole request. The query comes from\n * `proteinSequence`, else fetched for `accession`, else translated from the\n * view's `connectedTranscript`. Stored on the model as `blastParams`, the\n * name the dialog has always used.\n */\n searchParams?: BlastParams\n /**\n * Hide any column gappier than this percentage, 100 being \"hide nothing\".\n * A native react-msaview property, named here because it is the one setting\n * that decides WHICH columns a freshly launched view opens on: proteins that\n * differ in length put one row's private N-terminal extension at column 0,\n * and everything else is gap there. Anything else react-msaview takes as a\n * snapshot property passes through the same way.\n */\n allowedGappyness?: number\n /**\n * Where the view lands: `stack` (default), `splitRight` or `newTab`. The\n * declarative half of the launch \u2014 a link states the arrangement it wants\n * instead of the reader dragging the view into place.\n *\n * The default is `stack` and has to stay `stack`: it is what every link\n * written before this key existed already does, and the only thing an\n * embedded session can do. The launch DIALOG defaults to `splitRight`\n * instead (see `DEFAULT_LAUNCH_PLACEMENT`), because a launch from a gene\n * feature is a connected pair and reads as a split.\n *\n * A spec that arranges more than these two views should use the host's own\n * `layout` key instead, which states the whole tree and is applied after\n * every view in the spec has launched. Both may be given; `layout` wins,\n * being the later and more specific statement.\n */\n placement?: MsaViewPlacement\n}\n\n/**\n * `msa` and `tree` onto the plugin's own sources, so a url still goes through\n * `init.msaUrl` and its format sniffing, and `query` onto `querySeqName`\n */\nfunction withLongSources({ msa, tree, ...args }: LaunchMsaViewArgs) {\n const inlineMsa = msa?.includes('\\n') ? msa : undefined\n const inlineTree = tree?.trimStart().startsWith('(') ? tree : undefined\n const data =\n inlineMsa || inlineTree\n ? {\n ...args.data,\n ...(inlineMsa ? { msa: inlineMsa } : {}),\n ...(inlineTree ? { tree: inlineTree } : {}),\n }\n : args.data\n return {\n ...args,\n data: data as LaunchMsaViewArgs['data'],\n ...(msa && !inlineMsa ? { msaFileLocation: { uri: msa } } : {}),\n ...(tree && !inlineTree ? { treeFileLocation: { uri: tree } } : {}),\n querySeqName: args.querySeqName ?? args.query,\n }\n}\n\nexport default function LaunchMsaViewExtensionPointF(\n pluginManager: PluginManager,\n) {\n pluginManager.addToExtensionPoint(\n 'LaunchView-MsaView',\n async (args: LaunchMsaViewArgs) => {\n const {\n session,\n query,\n data,\n msaFileLocation,\n msaIndexedLocation,\n msaName,\n treeFileLocation,\n querySeqName,\n searchParams,\n ...rest\n } = withLongSources(args)\n\n // `orthologParams` and `searchParams` name no alignment at all \u2014 the\n // view builds one at launch, which is the dialog's Orthologs and BLAST\n // tabs reached declaratively.\n if (\n !data &&\n !msaFileLocation &&\n !msaIndexedLocation &&\n !rest.orthologParams &&\n !searchParams\n ) {\n throw new Error(\n 'No MSA data, file location, orthologParams or searchParams provided when launching MSA view',\n )\n }\n\n // inline data and the tree URL are native react-msaview snapshot props, set\n // directly, and so is orthologParams (the model's own autorun picks it up).\n // Only sources needing launch-time resolution go through `init`: msaUrl\n // (AlphaFold sniff) and the name-indexed bgzip block (no native loader).\n //\n // An init whose every field is undefined is still a truthy object, and\n // MsaViewPanel reads any init as a launch in flight, so an inline-data\n // launch flashed \"Loading alignment\" until processInit cleared it.\n const init = {\n msaUrl: msaFileLocation?.uri,\n msaIndexedLocation,\n msaName,\n querySeqName,\n }\n const { expandSpec } = await import('react-msaview')\n await launchMsaView(session, {\n ...expandSpec({ ...rest, query }),\n ...(searchParams ? { blastParams: searchParams } : {}),\n data,\n ...(treeFileLocation\n ? {\n treeFilehandle: {\n ...treeFileLocation,\n locationType: 'UriLocation',\n },\n }\n : {}),\n ...(Object.values(init).some(v => v !== undefined) ? { init } : {}),\n })\n\n return args\n },\n )\n}\n", "import { lazy } from 'react'\n\nimport ViewType from '@jbrowse/core/pluggableElementTypes/ViewType'\n\nimport type PluginManager from '@jbrowse/core/PluginManager'\n\nconst MsaViewPanel = lazy(() => import('./components/MsaViewPanel'))\n\nexport default function MsaViewF(pluginManager: PluginManager) {\n pluginManager.addViewType(() => {\n return new ViewType({\n name: 'MsaView',\n // a thunk, so react-msaview loads with the first view rather than with\n // the plugin; addView refuses the type until then, hence launchView\n stateModel: () => import('./model').then(m => m.default()),\n ReactComponent: MsaViewPanel,\n })\n })\n}\n", "export const version = '5.1.0'\n"],
|
|
5
5
|
"mappings": "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",
|
|
6
6
|
"names": ["require_Plugin", "__commonJSMin", "exports", "module", "require_ViewType", "__commonJSMin", "exports", "module", "import_Plugin", "import_configuration", "import_util", "import_mobx_state_tree", "import_jsx_runtime", "GridOn_default", "_jsx", "import_react", "import_util", "isMsaView", "view", "import_react", "import_mobx_react", "import_react", "import_util", "import_mobx_react", "import_mui", "useStyles", "GenomeMouseoverHighlight", "model", "hovered", "views", "s", "isMsaView", "hasHoverPosition", "React", "GenomeMouseoverHighlightRenderer", "classes", "useStyles", "offsetPx", "coord", "refName", "e", "width", "left", "GenomeMouseoverHighlight_default", "import_react", "import_util", "import_mobx_react", "connectedHighlights", "views", "genomeViewId", "genomeHovered", "regions", "isLinkedMsaView", "v", "r", "view", "MsaToGenomeHighlight", "model", "views", "hovered", "highlights", "connectedHighlights", "hasHoverPosition", "React", "MsaToGenomeHighlightRenderer", "classes", "useStyles", "offsetPx", "r", "e", "width", "left", "MsaToGenomeHighlight_default", "HighlightComponents", "model", "React", "MsaToGenomeHighlight_default", "GenomeMouseoverHighlight_default", "HighlightComponents_default", "AddHighlightComponentsModelF", "pluginManager", "model", "views", "v", "isMsaView", "React", "HighlightComponents_default", "import_pluggableElementTypes", "import_configuration", "configSchemaF", "pluginManager", "base", "BgzipFastaMsaAdapterF", "pluginManager", "configSchemaF", "import_react", "import_util", "import_jsx_runtime", "Add_default", "_jsx", "isCDS", "feature", "isCodingFeature", "import_util", "launchTarget", "self", "info", "LaunchMsaViewDialog", "isDisplay", "elt", "featureName", "feature", "openDialog", "self", "target", "track", "session", "preferredTranscriptId", "open", "handleClose", "isCodingFeature", "e", "extendStateModel", "stateModel", "superContextMenuItems", "launchTarget", "Add_default", "LaunchMsaViewF", "pluginManager", "withLongSources", "msa", "tree", "args", "inlineMsa", "inlineTree", "data", "LaunchMsaViewExtensionPointF", "pluginManager", "session", "query", "msaFileLocation", "msaIndexedLocation", "msaName", "treeFileLocation", "querySeqName", "searchParams", "rest", "init", "expandSpec", "launchMsaView", "v", "import_react", "import_ViewType", "MsaViewPanel", "MsaViewF", "pluginManager", "ViewType", "m", "version", "MsaViewPlugin", "Plugin", "version", "pluginManager", "MsaViewF", "LaunchMsaViewF", "LaunchMsaViewExtensionPointF", "AddHighlightComponentsModelF", "BgzipFastaMsaAdapterF", "GridOn_default", "session", "launchMsaView", "e"]
|
|
7
7
|
}
|
package/dist/utils/workspaces.js
CHANGED
|
@@ -21,8 +21,13 @@ export function resetWorkspacesWarning() {
|
|
|
21
21
|
* asks on every render, and only a launch is worth warning about.
|
|
22
22
|
*/
|
|
23
23
|
export function sessionSupportsPlacement(session) {
|
|
24
|
-
return (
|
|
25
|
-
hasAction(session, '
|
|
24
|
+
return (isSessionWithMoves(session) ||
|
|
25
|
+
(hasAction(session, 'setUseWorkspaces') &&
|
|
26
|
+
hasAction(session, 'setPendingMove')));
|
|
27
|
+
}
|
|
28
|
+
function isSessionWithMoves(session) {
|
|
29
|
+
return (hasAction(session, 'moveViewToNewTab') &&
|
|
30
|
+
hasAction(session, 'moveViewToSplit'));
|
|
26
31
|
}
|
|
27
32
|
function isSessionWithWorkspaces(session) {
|
|
28
33
|
const canEnable = hasAction(session, 'setUseWorkspaces');
|
|
@@ -45,14 +50,21 @@ function isSessionWithWorkspaces(session) {
|
|
|
45
50
|
* rather than the absence of one, so no caller has to ask what host it is on.
|
|
46
51
|
*/
|
|
47
52
|
export function placeMsaView(session, viewId, placement) {
|
|
48
|
-
if (placement === 'stack'
|
|
53
|
+
if (placement === 'stack') {
|
|
49
54
|
return;
|
|
50
55
|
}
|
|
51
|
-
session
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
+
if (isSessionWithMoves(session)) {
|
|
57
|
+
if (placement === 'newTab') {
|
|
58
|
+
session.moveViewToNewTab(viewId);
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
session.moveViewToSplit(viewId, 'row');
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
else if (isSessionWithWorkspaces(session)) {
|
|
65
|
+
session.setPendingMove({ type: placement, viewId });
|
|
66
|
+
session.setUseWorkspaces(true);
|
|
67
|
+
}
|
|
56
68
|
}
|
|
57
69
|
function isPlacement(value) {
|
|
58
70
|
return PLACEMENTS.includes(value);
|
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
import { afterEach, beforeEach, expect, test, vi } from 'vitest';
|
|
2
2
|
import { launchMsaView } from './launchMsaView';
|
|
3
3
|
import { DEFAULT_LAUNCH_PLACEMENT, LAUNCH_PLACEMENT_KEY, placeMsaView, readLaunchPlacement, resetWorkspacesWarning, sessionSupportsPlacement, writeLaunchPlacement, } from './workspaces';
|
|
4
|
+
// a host whose workspace is always on, with the move actions the View menu uses
|
|
5
|
+
function makeMovesSession() {
|
|
6
|
+
const recorded = { moves: [], workspaces: [], added: [] };
|
|
7
|
+
const session = {
|
|
8
|
+
moveViewToNewTab: (viewId) => recorded.moves.push({ to: 'tab', viewId }),
|
|
9
|
+
moveViewToSplit: (viewId, direction) => recorded.moves.push({ to: direction, viewId }),
|
|
10
|
+
setPendingMove: (move) => recorded.moves.push({ pending: move }),
|
|
11
|
+
setUseWorkspaces: (on) => recorded.workspaces.push(on),
|
|
12
|
+
};
|
|
13
|
+
return { session, recorded };
|
|
14
|
+
}
|
|
4
15
|
function makeSession({ canPlace = true, canEnable = true, } = {}) {
|
|
5
16
|
const recorded = { moves: [], workspaces: [], added: [] };
|
|
6
17
|
const session = {
|
|
@@ -34,6 +45,17 @@ afterEach(() => {
|
|
|
34
45
|
vi.restoreAllMocks();
|
|
35
46
|
vi.unstubAllGlobals();
|
|
36
47
|
});
|
|
48
|
+
test('a host with the move actions gets them, not setPendingMove', () => {
|
|
49
|
+
const { session, recorded } = makeMovesSession();
|
|
50
|
+
placeMsaView(session, 'view-1', 'splitRight');
|
|
51
|
+
placeMsaView(session, 'view-2', 'newTab');
|
|
52
|
+
expect(recorded.moves).toEqual([
|
|
53
|
+
{ to: 'row', viewId: 'view-1' },
|
|
54
|
+
{ to: 'tab', viewId: 'view-2' },
|
|
55
|
+
]);
|
|
56
|
+
expect(recorded.workspaces).toEqual([]);
|
|
57
|
+
expect(sessionSupportsPlacement(session)).toBe(true);
|
|
58
|
+
});
|
|
37
59
|
test('stack places nothing, on a host that could tile', () => {
|
|
38
60
|
const { session, recorded } = makeSession();
|
|
39
61
|
placeMsaView(session, 'view-1', 'stack');
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const version = "5.
|
|
1
|
+
export declare const version = "5.1.0";
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = '5.
|
|
1
|
+
export const version = '5.1.0';
|
package/package.json
CHANGED
|
@@ -19,6 +19,20 @@ interface Recorded {
|
|
|
19
19
|
added: { type: string; snapshot: Record<string, unknown> }[]
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
// a host whose workspace is always on, with the move actions the View menu uses
|
|
23
|
+
function makeMovesSession() {
|
|
24
|
+
const recorded: Recorded = { moves: [], workspaces: [], added: [] }
|
|
25
|
+
const session = {
|
|
26
|
+
moveViewToNewTab: (viewId: string) =>
|
|
27
|
+
recorded.moves.push({ to: 'tab', viewId }),
|
|
28
|
+
moveViewToSplit: (viewId: string, direction: string) =>
|
|
29
|
+
recorded.moves.push({ to: direction, viewId }),
|
|
30
|
+
setPendingMove: (move: unknown) => recorded.moves.push({ pending: move }),
|
|
31
|
+
setUseWorkspaces: (on: boolean) => recorded.workspaces.push(on),
|
|
32
|
+
} as unknown as AbstractSessionModel
|
|
33
|
+
return { session, recorded }
|
|
34
|
+
}
|
|
35
|
+
|
|
22
36
|
function makeSession({
|
|
23
37
|
canPlace = true,
|
|
24
38
|
canEnable = true,
|
|
@@ -59,6 +73,18 @@ afterEach(() => {
|
|
|
59
73
|
vi.unstubAllGlobals()
|
|
60
74
|
})
|
|
61
75
|
|
|
76
|
+
test('a host with the move actions gets them, not setPendingMove', () => {
|
|
77
|
+
const { session, recorded } = makeMovesSession()
|
|
78
|
+
placeMsaView(session, 'view-1', 'splitRight')
|
|
79
|
+
placeMsaView(session, 'view-2', 'newTab')
|
|
80
|
+
expect(recorded.moves).toEqual([
|
|
81
|
+
{ to: 'row', viewId: 'view-1' },
|
|
82
|
+
{ to: 'tab', viewId: 'view-2' },
|
|
83
|
+
])
|
|
84
|
+
expect(recorded.workspaces).toEqual([])
|
|
85
|
+
expect(sessionSupportsPlacement(session)).toBe(true)
|
|
86
|
+
})
|
|
87
|
+
|
|
62
88
|
test('stack places nothing, on a host that could tile', () => {
|
|
63
89
|
const { session, recorded } = makeSession()
|
|
64
90
|
placeMsaView(session, 'view-1', 'stack')
|
package/src/utils/workspaces.ts
CHANGED
|
@@ -25,6 +25,12 @@ export const LAUNCH_PLACEMENT_KEY = 'msaView-launchPlacement'
|
|
|
25
25
|
* The session actions that place a view in a tiled workspace. Only jbrowse-web
|
|
26
26
|
* and desktop have them, so feature-detect rather than import.
|
|
27
27
|
*/
|
|
28
|
+
interface SessionWithMoves {
|
|
29
|
+
moveViewToNewTab: (viewId: string) => unknown
|
|
30
|
+
moveViewToSplit: (viewId: string, direction: 'row' | 'column') => unknown
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// a host from before the workspace was always on
|
|
28
34
|
interface SessionWithWorkspaces {
|
|
29
35
|
setUseWorkspaces: (useWorkspaces: boolean) => void
|
|
30
36
|
setPendingMove: (move: {
|
|
@@ -54,8 +60,18 @@ export function resetWorkspacesWarning() {
|
|
|
54
60
|
*/
|
|
55
61
|
export function sessionSupportsPlacement(session: AbstractSessionModel) {
|
|
56
62
|
return (
|
|
57
|
-
|
|
58
|
-
hasAction(session, '
|
|
63
|
+
isSessionWithMoves(session) ||
|
|
64
|
+
(hasAction(session, 'setUseWorkspaces') &&
|
|
65
|
+
hasAction(session, 'setPendingMove'))
|
|
66
|
+
)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function isSessionWithMoves(
|
|
70
|
+
session: AbstractSessionModel,
|
|
71
|
+
): session is AbstractSessionModel & SessionWithMoves {
|
|
72
|
+
return (
|
|
73
|
+
hasAction(session, 'moveViewToNewTab') &&
|
|
74
|
+
hasAction(session, 'moveViewToSplit')
|
|
59
75
|
)
|
|
60
76
|
}
|
|
61
77
|
|
|
@@ -90,14 +106,19 @@ export function placeMsaView(
|
|
|
90
106
|
viewId: string,
|
|
91
107
|
placement: MsaViewPlacement,
|
|
92
108
|
) {
|
|
93
|
-
if (placement === 'stack'
|
|
109
|
+
if (placement === 'stack') {
|
|
94
110
|
return
|
|
95
111
|
}
|
|
96
|
-
session
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
112
|
+
if (isSessionWithMoves(session)) {
|
|
113
|
+
if (placement === 'newTab') {
|
|
114
|
+
session.moveViewToNewTab(viewId)
|
|
115
|
+
} else {
|
|
116
|
+
session.moveViewToSplit(viewId, 'row')
|
|
117
|
+
}
|
|
118
|
+
} else if (isSessionWithWorkspaces(session)) {
|
|
119
|
+
session.setPendingMove({ type: placement, viewId })
|
|
120
|
+
session.setUseWorkspaces(true)
|
|
121
|
+
}
|
|
101
122
|
}
|
|
102
123
|
|
|
103
124
|
function isPlacement(value: unknown): value is MsaViewPlacement {
|
package/src/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = '5.
|
|
1
|
+
export const version = '5.1.0'
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
var l=["stack","splitRight","newTab"],a="splitRight",i="msaView-launchPlacement";function n(e,t){return t in e&&typeof e[t]=="function"}var r=!1;function w(e){return n(e,"setUseWorkspaces")&&n(e,"setPendingMove")}function p(e){let t=n(e,"setUseWorkspaces"),s=n(e,"setPendingMove");return t!==s&&!r&&(r=!0,console.warn(`jbrowse-plugin-msaview: this session supports workspaces but not ${s?"setUseWorkspaces":"setPendingMove"}, so the MSA view was stacked instead of tiled: the session API moved and the plugin needs updating to match.`)),t&&s}function c(e,t,s){s==="stack"||!p(e)||(e.setPendingMove({type:s,viewId:t}),e.setUseWorkspaces(!0))}function u(e){return l.includes(e)}function M(){try{let e=globalThis.localStorage.getItem(i);return u(e)?e:a}catch(e){return console.error(e),a}}function d(e){try{globalThis.localStorage.setItem(i,e)}catch(t){console.error(t)}}async function h(e,{placement:t="stack",...s}){let o=await e.launchView("MsaView",{type:"MsaView",...s});return c(e,o.id,t),o}export{w as a,M as b,d as c,h as d};
|
|
2
|
-
//# sourceMappingURL=chunk-VOBS7IVV.js.map
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../src/utils/workspaces.ts", "../../src/utils/launchMsaView.ts"],
|
|
4
|
-
"sourcesContent": ["import type { AbstractSessionModel } from '@jbrowse/core/util'\n\n/**\n * Where a launched MSA view lands. Every launch names one of these and stops\n * there, so a host that arranges views differently is one function to teach.\n *\n * stack append below whatever is on screen\n * splitRight its own cell to the right, beside the view it is connected to\n * newTab its own tab in the current cell\n */\nexport type MsaViewPlacement = 'stack' | 'splitRight' | 'newTab'\n\nconst PLACEMENTS: MsaViewPlacement[] = ['stack', 'splitRight', 'newTab']\n\n/**\n * What the dialog does unasked. Side-by-side, because a launch from a gene\n * feature sets `connectedViewId`: the pair shares a hover and a highlight, and\n * reads as a split. A session spec defaults to `stack` instead.\n */\nexport const DEFAULT_LAUNCH_PLACEMENT: MsaViewPlacement = 'splitRight'\n\nexport const LAUNCH_PLACEMENT_KEY = 'msaView-launchPlacement'\n\n/**\n * The session actions that place a view in a tiled workspace. Only jbrowse-web\n * and desktop have them, so feature-detect rather than import.\n */\ninterface SessionWithWorkspaces {\n setUseWorkspaces: (useWorkspaces: boolean) => void\n setPendingMove: (move: {\n type: 'newTab' | 'splitRight'\n viewId: string\n }) => void\n}\n\nfunction hasAction(session: AbstractSessionModel, name: string) {\n return (\n name in session &&\n typeof (session as unknown as Record<string, unknown>)[name] === 'function'\n )\n}\n\n// Warned at most once. This is a property of the host, so the answer is the\n// same on every launch and a dialog the user reopens should not stack up noise.\nlet warnedPartial = false\n\nexport function resetWorkspacesWarning() {\n warnedPartial = false\n}\n\n/**\n * Whether this host can honor anything other than `stack`. Silent: the dialog\n * asks on every render, and only a launch is worth warning about.\n */\nexport function sessionSupportsPlacement(session: AbstractSessionModel) {\n return (\n hasAction(session, 'setUseWorkspaces') &&\n hasAction(session, 'setPendingMove')\n )\n}\n\nfunction isSessionWithWorkspaces(\n session: AbstractSessionModel,\n): session is AbstractSessionModel & SessionWithWorkspaces {\n const canEnable = hasAction(session, 'setUseWorkspaces')\n const canPlace = hasAction(session, 'setPendingMove')\n\n // Missing BOTH is an embedded session, with nothing to ask for. Missing ONE\n // is a host that moved the action out from under us: protein3d once stopped\n // tiling silently for weeks when jbrowse-web folded `setPendingMove` into its\n // layout `init`.\n if (canEnable !== canPlace && !warnedPartial) {\n warnedPartial = true\n console.warn(\n `jbrowse-plugin-msaview: this session supports workspaces but not ` +\n `${canPlace ? 'setUseWorkspaces' : 'setPendingMove'}, so the MSA view ` +\n `was stacked instead of tiled: the session API moved and the plugin ` +\n `needs updating to match.`,\n )\n }\n return canEnable && canPlace\n}\n\n/**\n * Put a freshly added view where the launch said to. `stack` is a placement\n * rather than the absence of one, so no caller has to ask what host it is on.\n */\nexport function placeMsaView(\n session: AbstractSessionModel,\n viewId: string,\n placement: MsaViewPlacement,\n) {\n if (placement === 'stack' || !isSessionWithWorkspaces(session)) {\n return\n }\n session.setPendingMove({ type: placement, viewId })\n // Session-scoped: turning workspaces on for this session leaves the user's\n // own default alone, which is what `setUseWorkspaces` (as against\n // `setUseWorkspacesPreference`) is for.\n session.setUseWorkspaces(true)\n}\n\nfunction isPlacement(value: unknown): value is MsaViewPlacement {\n return PLACEMENTS.includes(value as MsaViewPlacement)\n}\n\n/**\n * The dialog's own remembered choice \u2014 not the host's preferences system, which\n * records whether the user likes workspaces and does not exist everywhere.\n */\nexport function readLaunchPlacement(): MsaViewPlacement {\n try {\n const stored = globalThis.localStorage.getItem(LAUNCH_PLACEMENT_KEY)\n return isPlacement(stored) ? stored : DEFAULT_LAUNCH_PLACEMENT\n } catch (error) {\n console.error(error)\n return DEFAULT_LAUNCH_PLACEMENT\n }\n}\n\nexport function writeLaunchPlacement(placement: MsaViewPlacement) {\n try {\n globalThis.localStorage.setItem(LAUNCH_PLACEMENT_KEY, placement)\n } catch (error) {\n console.error(error)\n }\n}\n", "import { placeMsaView } from './workspaces'\n\nimport type { MsaViewPlacement } from './workspaces'\nimport type { AbstractSessionModel } from '@jbrowse/core/util'\n\n/**\n * A launch, stated: what the view is, and where it goes. Everything but\n * `placement` is a react-msaview or plugin-model snapshot property, passed\n * through untouched so this never becomes a list that has to grow.\n */\nexport interface MsaViewLaunchSpec extends Record<string, unknown> {\n /** default `stack`, the only thing an embedded session can do */\n placement?: MsaViewPlacement\n}\n\n/**\n * The one place a launch adds an MSA view -- the dialog's four tabs, the Add\n * menu, and the `LaunchView-MsaView` extension point a session spec arrives on\n * all come through here. Each of them used to run its own `addView` and none\n * placed the result, which is how a launch from a gene feature landed stacked\n * under the very genome view it was connected to.\n */\nexport async function launchMsaView(\n session: AbstractSessionModel,\n { placement = 'stack', ...snapshot }: MsaViewLaunchSpec,\n) {\n const view = await session.launchView('MsaView', {\n type: 'MsaView',\n ...snapshot,\n })\n placeMsaView(session, view.id, placement)\n return view\n}\n"],
|
|
5
|
-
"mappings": "AAYA,IAAMA,EAAiC,CAAC,QAAS,aAAc,QAAQ,EAO1DC,EAA6C,aAE7CC,EAAuB,0BAcpC,SAASC,EAAUC,EAA+BC,EAAc,CAC9D,OACEA,KAAQD,GACR,OAAQA,EAA+CC,CAAI,GAAM,UAErE,CAIA,IAAIC,EAAgB,GAUb,SAASC,EAAyBC,EAA+B,CACtE,OACEC,EAAUD,EAAS,kBAAkB,GACrCC,EAAUD,EAAS,gBAAgB,CAEvC,CAEA,SAASE,EACPF,EACyD,CACzD,IAAMG,EAAYF,EAAUD,EAAS,kBAAkB,EACjDI,EAAWH,EAAUD,EAAS,gBAAgB,EAMpD,OAAIG,IAAcC,GAAY,CAACC,IAC7BA,EAAgB,GAChB,QAAQ,KACN,oEACKD,EAAW,mBAAqB,gBAAgB,+GAGvD,GAEKD,GAAaC,CACtB,CAMO,SAASE,EACdN,EACAO,EACAC,EACA,CACIA,IAAc,SAAW,CAACN,EAAwBF,CAAO,IAG7DA,EAAQ,eAAe,CAAE,KAAMQ,EAAW,OAAAD,CAAO,CAAC,EAIlDP,EAAQ,iBAAiB,EAAI,EAC/B,CAEA,SAASS,EAAYC,EAA2C,CAC9D,OAAOC,EAAW,SAASD,CAAyB,CACtD,CAMO,SAASE,GAAwC,CACtD,GAAI,CACF,IAAMC,EAAS,WAAW,aAAa,QAAQC,CAAoB,EACnE,OAAOL,EAAYI,CAAM,EAAIA,EAASE,CACxC,OAASC,EAAO,CACd,eAAQ,MAAMA,CAAK,EACZD,CACT,CACF,CAEO,SAASE,EAAqBT,EAA6B,CAChE,GAAI,CACF,WAAW,aAAa,QAAQM,EAAsBN,CAAS,CACjE,OAASQ,EAAO,CACd,QAAQ,MAAMA,CAAK,CACrB,CACF,CCxGA,eAAsBE,EACpBC,EACA,CAAE,UAAAC,EAAY,QAAS,GAAGC,CAAS,EACnC,CACA,IAAMC,EAAO,MAAMH,EAAQ,WAAW,UAAW,CAC/C,KAAM,UACN,GAAGE,CACL,CAAC,EACD,OAAAE,EAAaJ,EAASG,EAAK,GAAIF,CAAS,EACjCE,CACT",
|
|
6
|
-
"names": ["PLACEMENTS", "DEFAULT_LAUNCH_PLACEMENT", "LAUNCH_PLACEMENT_KEY", "hasAction", "session", "name", "warnedPartial", "sessionSupportsPlacement", "session", "hasAction", "isSessionWithWorkspaces", "canEnable", "canPlace", "warnedPartial", "placeMsaView", "viewId", "placement", "isPlacement", "value", "PLACEMENTS", "readLaunchPlacement", "stored", "LAUNCH_PLACEMENT_KEY", "DEFAULT_LAUNCH_PLACEMENT", "error", "writeLaunchPlacement", "launchMsaView", "session", "placement", "snapshot", "view", "placeMsaView"]
|
|
7
|
-
}
|
/package/dist/chunks/{LaunchMsaViewDialog-LMZOJ7T6.js.map → LaunchMsaViewDialog-WBXEEHJL.js.map}
RENAMED
|
File without changes
|