@@ -10,7 +10,7 @@ const os = require('os')
1010// ── Constants ────────────────────────────────────────────────────────────────
1111
1212const kPort = 59812
13- const kBaseURL = 'https://water.95ent.ai '
13+ const kBaseURL = 'https://water.grauxmusic.com '
1414const kUA = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) WaterMorphCompanion/1.0'
1515
1616// ── Single-instance guard ────────────────────────────────────────────────────
@@ -36,12 +36,15 @@ function createWindow() {
3636 // custom chrome, which keeps a native draggable area but lets us color it.
3737 const isMac = process . platform === 'darwin'
3838 win = new BrowserWindow ( {
39- width : 420 ,
40- height : 700 ,
41- minWidth : 340 ,
42- minHeight : 480 ,
39+ width : 400 ,
40+ height : 800 ,
41+ minWidth : 400 ,
42+ maxWidth : 400 ,
43+ minHeight : 400 ,
44+ show : false ,
4345 frame : true ,
44- titleBarStyle : isMac ? 'hidden' : 'hiddenInset' ,
46+ titleBarStyle : isMac ? 'hiddenInset' : 'hiddenInset' ,
47+ trafficLightPosition : isMac ? { x : 12 , y : 10 } : undefined ,
4548 titleBarOverlay : isMac ? false : {
4649 color : '#0a0a0a' ,
4750 symbolColor : '#8B5CF6' , // mauve — Water brand
@@ -63,11 +66,10 @@ function createWindow() {
6366 // and the web app renders the full browser UI instead of the companion UI.
6467 win . webContents . session . setUserAgent ( kUA )
6568
66- // Clear service worker + cache on every launch so stale Next.js HTML never
67- // causes React hydration mismatch (#418) between cached server HTML and new JS.
68- win . webContents . session . clearStorageData ( {
69- storages : [ 'serviceworkers' , 'cachestorage' ]
70- } ) . catch ( ( ) => { } ) . finally ( ( ) => {
69+ // Clear only serviceworkers — lets the HTTP cache speed up loads.
70+ // HTTP cache cleared = blank flash on every launch (do not add clearCache() here).
71+ win . webContents . session . clearStorageData ( { storages : [ 'serviceworkers' ] } )
72+ . catch ( ( ) => { } ) . finally ( ( ) => {
7173 win . loadURL ( `${ kBaseURL } /discover?companion=windows` , {
7274 userAgent : kUA ,
7375 extraHeaders : 'X-Water-Companion: windows\n'
@@ -85,8 +87,9 @@ function createWindow() {
8587 setTimeout ( ( ) => win ?. loadURL ( `${ kBaseURL } /discover?companion=windows` , { userAgent : kUA } ) , 3000 )
8688 } )
8789
88- // Send current DAW state and kill web chrome as soon as the page is interactive
90+ // Show window only after page is ready — eliminates the blank flash on open
8991 win . webContents . on ( 'did-finish-load' , ( ) => {
92+ win . show ( )
9093 injectCompanionCSS ( )
9194 win . webContents . send ( 'daw-state' , {
9295 connected : dawState . isConnected ,
@@ -98,6 +101,26 @@ function createWindow() {
98101 // Re-inject on SPA navigation (Next.js history.pushState)
99102 win . webContents . on ( 'did-navigate-in-page' , ( ) => { injectCompanionCSS ( ) } )
100103
104+ // Splice rule: plans/billing/auth always open in the external browser.
105+ // The companion stays on /discover — it never becomes a checkout page.
106+ const EXTERNAL_PATHS = [ '/plans' , '/billing' , '/auth' , '/start' , '/legal' , '/settings' ]
107+ win . webContents . on ( 'will-navigate' , ( event , url ) => {
108+ try {
109+ const path = new URL ( url ) . pathname
110+ if ( EXTERNAL_PATHS . some ( p => path . startsWith ( p ) ) ) {
111+ event . preventDefault ( )
112+ shell . openExternal ( url )
113+ }
114+ } catch { }
115+ } )
116+
117+ // Auto-save downloads to WATER_TMP — prevents macOS "Save As" dialog
118+ win . webContents . session . on ( 'will-download' , ( _event , item ) => {
119+ const filename = item . getFilename ( )
120+ const savePath = path . join ( WATER_TMP , filename )
121+ item . setSavePath ( savePath )
122+ } )
123+
101124 win . on ( 'close' , ( e ) => {
102125 // If we're doing a real quit (update install or tray Quit), let it through.
103126 if ( isQuitting ) { win = null ; return }
@@ -113,7 +136,7 @@ function createTray() {
113136 // Use a 16x16 transparent icon — replace icon.ico with a real icon in production
114137 const icon = nativeImage . createEmpty ( )
115138 tray = new Tray ( icon )
116- tray . setToolTip ( 'Water Morph ' )
139+ tray . setToolTip ( 'Water Studio ' )
117140 updateTrayMenu ( )
118141 tray . on ( 'click' , ( ) => {
119142 if ( win ?. isVisible ( ) ) { win . focus ( ) } else { win ?. show ( ) }
@@ -148,6 +171,15 @@ function registerAutostart() {
148171
149172// ── Companion CSS injection — kills web chrome, re-injects on SPA navigation ──
150173
174+ function injectPluginStatus ( connected , bpm , key ) {
175+ if ( ! win ?. webContents ) return
176+ const script = `(function(){
177+ const el = document.getElementById('__wm_plugin_status');
178+ if (el) { el.dataset.connected = ${ JSON . stringify ( String ( connected ) ) } ; el.dataset.bpm = ${ JSON . stringify ( String ( bpm ) ) } ; el.dataset.key = ${ JSON . stringify ( String ( key ) ) } ; }
179+ })()`
180+ win . webContents . executeJavaScript ( script ) . catch ( ( ) => { } )
181+ }
182+
151183function injectCompanionCSS ( ) {
152184 if ( ! win ?. webContents ) return
153185 const js = `
@@ -171,7 +203,7 @@ function injectCompanionCSS() {
171203 if(!document.getElementById('__wmCSS')){
172204 var s=document.createElement('style');
173205 s.id='__wmCSS';
174- s.textContent='[data-bottom-nav]{display:none!important;visibility:hidden!important;height:0!important;overflow:hidden!important;pointer-events:none!important;}[data-mobile-spacer]{display:none!important;height:0!important;}#__wm_plugin_status{display:none!important;}';
206+ s.textContent='[data-bottom-nav]{display:none!important;visibility:hidden!important;height:0!important;overflow:hidden!important;pointer-events:none!important;}[data-mobile-spacer]{display:none!important;height:0!important;}#__wm_plugin_status{display:none!important;}[data-companion-header]{-webkit-app-region:drag}[data-companion-header] button,[data-companion-header] input,[data-companion-header] a{-webkit-app-region:no-drag}[data-sidebar="sidebar"]{display:none!important;}[data-sidebar="rail"]{display:none!important;} ';
175207 document.head.appendChild(s);
176208 }
177209 var _kill=function(){
@@ -350,6 +382,11 @@ function handleLine(conn, line) {
350382
351383// ── IPC from web app ──────────────────────────────────────────────────────────
352384
385+ // Dedicated temp dir — never rely on os.tmpdir() which inherits TMPDIR from the
386+ // launching shell (e.g. Claude Code sets TMPDIR to its session folder).
387+ const WATER_TMP = path . join ( os . homedir ( ) , 'Library' , 'Caches' , 'water-studio' , 'downloads' )
388+ fs . mkdirSync ( WATER_TMP , { recursive : true } )
389+
353390// Track cache: id → absolute local path (populated by fetch interception + morphCopy)
354391const trackCache = new Map ( )
355392
@@ -457,6 +494,12 @@ function setupAutoUpdater() {
457494// ── App lifecycle ─────────────────────────────────────────────────────────────
458495
459496app . whenReady ( ) . then ( ( ) => {
497+ if ( app . dock ) app . dock . hide ( )
498+ // Standard Edit menu so Cmd+C/V/X/A work in the WebView
499+ Menu . setApplicationMenu ( Menu . buildFromTemplate ( [
500+ { role : 'appMenu' } ,
501+ { role : 'editMenu' } ,
502+ ] ) )
460503 createWindow ( )
461504 createTray ( )
462505 startTCPServer ( )
@@ -478,6 +521,21 @@ app.on('open-url', (_event, url) => {
478521function handleProtocolUrl ( url ) {
479522 if ( ! url . startsWith ( 'watermorph://' ) ) return
480523 if ( win ) { win . show ( ) ; win . focus ( ) }
524+
525+ const parsed = new URL ( url )
526+
527+ // watermorph://auth?access_token=X&refresh_token=Y
528+ // Navigates the WebView to a URL with the Supabase auth fragment.
529+ // The Supabase client on that page auto-picks up the tokens and
530+ // creates the session — same mechanism as email magic links.
531+ if ( parsed . hostname === 'auth' ) {
532+ const at = parsed . searchParams . get ( 'access_token' )
533+ const rt = parsed . searchParams . get ( 'refresh_token' )
534+ if ( at && rt && win ?. webContents ) {
535+ const authUrl = `${ kBaseURL } /api/plugin/v1/auth-callback?access_token=${ encodeURIComponent ( at ) } &refresh_token=${ encodeURIComponent ( rt ) } `
536+ win . webContents . loadURL ( authUrl , { userAgent : kUA , extraHeaders : 'X-Water-Companion: windows\n' } )
537+ }
538+ }
481539}
482540
483541app . on ( 'before-quit' , ( ) => { isQuitting = true } )
0 commit comments