UNPKG

vue-window-bridge

Version:

Vue composable for managing multiple windows across displays, with cross-window communication

1 lines 7.1 kB
'use strict';var vue=require('vue');const P=(u={})=>{const i=vue.ref(null),a=vue.ref(""),w=vue.ref(false),y=vue.ref(null),D=u.debug||false,f=u.preferredScreen,g=u.fullscreen!==void 0?u.fullscreen:true,n=(e,o)=>{D&&(console.group(`[MultiWindow] ${e}`),o!==void 0&&console.log(o),console.groupEnd());},R=async e=>{try{return typeof window<"u"&&"getScreenDetails"in window?(n("Using modern getScreenDetails API"),await I(e)):(n("Falling back to legacy Screen API"),C(e))}catch(o){return n("Error opening window",o),h(e)}},L=async()=>{try{if(typeof window<"u"&&"getScreenDetails"in window){const o=(await window.getScreenDetails()).screens;if(!o||o.length<=1)return 0;const t=window.screenX||window.screenLeft||0,l=window.screenY||window.screenTop||0;n("Current window position",{x:t,y:l});for(let s=0;s<o.length;s++){const r=o[s],d=r.availLeft!==void 0?{left:r.availLeft,top:r.availTop,right:r.availLeft+r.availWidth,bottom:r.availTop+r.availHeight}:r.bounds;if(t>=d.left&&t<d.right&&l>=d.top&&l<d.bottom)return n("Current window is on monitor",{index:s,screen:r}),s}return 0}else if(typeof window<"u"){const e=window.screenX||window.screenLeft||0,o=window.screen?.width||window.innerWidth,t=e>=o?1:0;return n("Legacy monitor detection",{windowX:e,screenWidth:o,monitorIndex:t}),t}return 0}catch(e){return n("Error detecting current monitor",e),0}},I=async e=>{try{if(typeof window>"u")return null;const t=(await window.getScreenDetails()).screens;if(n("Available screens",t),!t||t.length<=1)return n("Only one screen detected, using current screen"),h(e);const l=await L();n("Current monitor index",l);let s;f!==void 0&&t[f]?(s=f,n("Using preferred screen",{index:f})):(s=(l+1)%t.length,n("Using next screen in sequence",{current:l,next:s}));const r=t[s];n("Target screen",r);const{width:d,height:c,left:S,top:W}=r.availWidth?{width:r.availWidth,height:r.availHeight,left:r.availLeft,top:r.availTop}:r.bounds;n("Target screen dimensions",{width:d,height:c,left:S,top:W});const U=g?`width=${d},height=${c},left=${S},top=${W},menubar=no,toolbar=no,location=yes,status=no,directories=no,scrollbars=yes,resizable=yes`:`width=${Math.min(800,d)},height=${Math.min(600,c)},left=${S+(d-Math.min(800,d))/2},top=${W+(c-Math.min(600,c))/2},menubar=no,toolbar=no,location=yes,status=no,directories=no,scrollbars=yes,resizable=yes`;return E(e,U)}catch(o){return n("Error with getScreenDetails",o),C(e)}},C=async e=>{if(typeof window>"u"||!window.screen)return n("Screen API not available"),h(e);try{const o=await L();n("Current monitor index (legacy)",o);const t=window.screenX||window.screenLeft||0,l=window.screenY||window.screenTop||0,s=window.screen.width||window.innerWidth,r=window.screen.height||window.innerHeight;let d;o===0?d=s:d=-s,n("Legacy screen positioning",{windowX:t,windowY:l,screenWidth:s,screenHeight:r,currentMonitorIndex:o,newWindowLeft:d});const c=g?`width=${s},height=${r},left=${d},top=0,menubar=no,toolbar=no,location=no,status=no`:`width=800,height=600,left=${d+(s-800)/2},top=${(r-600)/2},menubar=no,toolbar=no,location=no,status=no`;return E(e,c)}catch(o){return n("Error with legacy screen detection",o),h(e)}},h=e=>typeof window>"u"?null:(n("Opening regular window without special positioning"),E(e,g?"menubar=no,toolbar=no,location=no,status=no,directories=no,scrollbars=yes,resizable=yes":"width=800,height=600,menubar=no,toolbar=no,location=no,status=no,directories=no,scrollbars=yes,resizable=yes")),F=e=>{if(!e||e.closed){n("Cannot request fullscreen for null or closed window");return}try{e.postMessage("REQUEST_FULLSCREEN",a.value||window.location.origin),n("Sent fullscreen request message to child window");}catch(o){n("Error sending fullscreen message",o);}},O=e=>{if(!e||e.closed){n("Cannot maximize null or closed window");return}try{e.postMessage("MAXIMIZE_WINDOW",a.value||window.location.origin),n("Sent maximize request message to child window");}catch(o){n("Error sending maximize message",o);}},b=()=>i.value&&i.value.closed?(n("Child window is closed"),w.value=false,i.value=null,true):false,p=e=>{if(a.value&&e.origin!==a.value){n("Ignored message from untrusted origin",{expected:a.value,received:e.origin});return}e.data==="READY_FOR_MAXIMIZE"&&i.value?(n("Child window is ready to be maximized"),O(i.value)):e.data==="READY_FOR_FULLSCREEN"&&i.value&&g&&(n("Child window is ready for fullscreen"),F(i.value)),n("Received data from child window",e.data),y.value=e.data,typeof window<"u"&&window.document&&window.document.dispatchEvent(new CustomEvent("multi-window-data",{detail:e.data}));},A=e=>{if(!(typeof window>"u"))try{if(window.opener){n("Sending data to parent window",e);const o=typeof vue.toRaw=="function"?vue.toRaw(e):JSON.parse(JSON.stringify(e));window.opener.postMessage(o,window.opener.location.origin),n("Data sent to parent",o);}else n("No parent window found");}catch(o){n("Error sending data to parent",o);}},$=e=>{if(!(typeof window>"u"))try{if(i.value&&!i.value.closed){n("Sending data to child window",e);const o=typeof vue.toRaw=="function"?vue.toRaw(e):JSON.parse(JSON.stringify(e));i.value.postMessage(o,a.value||"*"),n("Data sent to child window",o);}else n("No child window found or window is closed"),b();}catch(o){n("Error sending data to child window",o),b();}},M=()=>{i.value&&!i.value.closed&&(n("Closing child window"),i.value.close(),i.value=null,w.value=false);},q=()=>typeof window<"u"&&!!window.opener,x=()=>{if(!(typeof window>"u"||!window.document)){n("Attempting to make this window fullscreen");try{const e=window.document.documentElement,o=e.requestFullscreen||e.webkitRequestFullscreen||e.mozRequestFullScreen||e.msRequestFullscreen;o?(o.call(e),n("Requested fullscreen via API")):n("Fullscreen API not supported in this browser");}catch(e){n("Error requesting fullscreen",e);}}},E=(e,o)=>{if(typeof window>"u")return null;try{const t=new URL(e,window.location.origin).href;if(n("Opening window with URL",t),i.value&&!i.value.closed&&(n("Closing existing child window"),i.value.close()),i.value=window.open(t,"_blank",o),!i.value)return n("Failed to open window - likely blocked by popup blocker"),null;a.value=new URL(t).origin,n("Child window origin",a.value),typeof window.removeEventListener=="function"&&window.removeEventListener("message",p),window.addEventListener("message",p),w.value=!0;const l=()=>{b()||w.value&&setTimeout(l,1e3);};return setTimeout(l,1e3),i.value}catch(t){return n("Error opening window",t),null}},T=()=>{typeof window<"u"&&window.removeEventListener("message",p),M();};return typeof window<"u"&&window.opener&&(window.addEventListener("message",e=>{e.source===window.opener&&(e.data==="REQUEST_FULLSCREEN"?x():(n("Received data from parent window",e.data),y.value=e.data,window.document&&window.document.dispatchEvent(new CustomEvent("multi-window-data",{detail:e.data}))));}),window.addEventListener("beforeunload",()=>{try{window.removeEventListener("message",p);}catch{}})),{childWindow:i,isChildWindowOpen:w,receivedData:y,openWindowOnSecondMonitor:R,sendDataToParent:A,sendDataToChild:$,closeChildWindow:M,isChildWindow:q,requestFullscreenForSelf:x,cleanup:T}};exports.useMultiWindow=P;