Skip to content

Commit ee85331

Browse files
committed
Fixes
1 parent bfe157c commit ee85331

File tree

8 files changed

+78
-41
lines changed

8 files changed

+78
-41
lines changed

.github/workflows/push.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ on:
44
push:
55
branches:
66
- '*'
7-
- '!winelectron'
87
tags:
98
- '*'
109

.github/workflows/windows-electron.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ name: Windows Electron Tests
33
on:
44
push:
55
branches:
6-
- winelectron
6+
- main
77

88
permissions:
99
contents: read

packages/core/src/util/tracks.ts

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -181,12 +181,24 @@ export function getFileName(track: FileLocation) {
181181
const uri = 'uri' in track ? track.uri : undefined
182182
const localPath = 'localPath' in track ? track.localPath : undefined
183183
const blob = 'blobId' in track ? track : undefined
184-
return (
185-
blob?.name ||
186-
uri?.slice(uri.lastIndexOf('/') + 1) ||
187-
localPath?.slice(localPath.replace(/\\/g, '/').lastIndexOf('/') + 1) ||
188-
''
189-
)
184+
185+
if (blob?.name) {
186+
return blob.name
187+
}
188+
189+
if (uri) {
190+
// Normalize path separators and find the last one
191+
// This handles both forward slashes and Windows backslashes in file:// URLs
192+
const normalized = uri.replace(/\\/g, '/')
193+
return normalized.slice(normalized.lastIndexOf('/') + 1)
194+
}
195+
196+
if (localPath) {
197+
const normalized = localPath.replace(/\\/g, '/')
198+
return normalized.slice(normalized.lastIndexOf('/') + 1)
199+
}
200+
201+
return ''
190202
}
191203

192204
export function guessAdapter(

packages/text-indexing/src/TextIndexing.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { checkStopToken } from '@jbrowse/core/util/stopToken'
77
import { ixIxxStream } from 'ixixx'
88

99
// misc
10-
import { generateMeta } from './types/common.ts'
10+
import { generateMeta, sanitizeForFilename } from './types/common.ts'
1111
import { indexGff3 } from './types/gff3Adapter.ts'
1212
import { indexVcf } from './types/vcfAdapter.ts'
1313

@@ -290,7 +290,8 @@ function getLoc(attr: string, config: Track) {
290290
}
291291

292292
function runIxIxx(readStream: Readable, idxLocation: string, name: string) {
293-
const ixFilename = path.join(idxLocation, 'trix', `${name}.ix`)
294-
const ixxFilename = path.join(idxLocation, 'trix', `${name}.ixx`)
293+
const safeName = sanitizeForFilename(name)
294+
const ixFilename = path.join(idxLocation, 'trix', `${safeName}.ix`)
295+
const ixxFilename = path.join(idxLocation, 'trix', `${safeName}.ixx`)
295296
return ixIxxStream(readStream, ixFilename, ixxFilename)
296297
}

packages/text-indexing/src/types/common.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,12 @@ export function guessAdapterFromFileName(filePath: string): Track {
153153
}
154154
}
155155

156+
// Sanitize a string to be safe for use in filenames on all platforms
157+
// Replaces characters that are invalid in Windows filenames: \ / : * ? " < > |
158+
export function sanitizeForFilename(name: string) {
159+
return name.replace(/[\\/:*?"<>|]/g, '_')
160+
}
161+
156162
/**
157163
* Generates metadata of index given a filename (trackId or assembly)
158164
*/
@@ -171,8 +177,9 @@ export async function generateMeta({
171177
featureTypesToExclude: string[]
172178
assemblyNames: string[]
173179
}) {
180+
const safeName = sanitizeForFilename(name)
174181
fs.writeFileSync(
175-
path.join(outDir, 'trix', `${name}_meta.json`),
182+
path.join(outDir, 'trix', `${safeName}_meta.json`),
176183
JSON.stringify(
177184
{
178185
dateCreated: new Date().toISOString(),

products/jbrowse-desktop/electron/autoUpdater.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,16 @@ export function setupAutoUpdater(
1919
autoUpdater.autoDownload = false
2020

2121
autoUpdater.on('error', (error: Error) => {
22+
const errorMessage = `Error in auto-updater: ${error}`
23+
sendStatusToWindow(getMainWindow(), errorMessage)
24+
2225
// Skip dialogs in CI environments to avoid blocking tests
2326
if (process.env.CI) {
2427
console.error('Auto-updater error (CI mode, skipping dialog):', error)
2528
return
2629
}
2730
dialog.showErrorBox(
28-
'Error: ',
31+
'Update Error',
2932
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
3033
error == null ? 'unknown' : (error.stack || error).toString(),
3134
)
@@ -34,6 +37,7 @@ export function setupAutoUpdater(
3437
autoUpdater.on('update-available', async () => {
3538
// Skip dialogs in CI environments
3639
if (process.env.CI) {
40+
// eslint-disable-next-line no-console
3741
console.log('Update available (CI mode, skipping dialog)')
3842
return
3943
}
@@ -56,13 +60,10 @@ export function setupAutoUpdater(
5660
sendStatusToWindow(getMainWindow(), 'Checking for update...')
5761
})
5862

59-
autoUpdater.on('error', (err: Error) => {
60-
sendStatusToWindow(getMainWindow(), `Error in auto-updater: ${err}`)
61-
})
62-
6363
autoUpdater.on('update-downloaded', () => {
6464
// Skip dialogs in CI environments
6565
if (process.env.CI) {
66+
// eslint-disable-next-line no-console
6667
console.log('Update downloaded (CI mode, skipping dialog)')
6768
return
6869
}

products/jbrowse-desktop/electron/electron.ts

Lines changed: 39 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { app, ipcMain } from 'electron'
1+
import { app, dialog, ipcMain } from 'electron'
22
import contextMenu from 'electron-context-menu'
33
import debug from 'electron-debug'
44
import pkg from 'electron-updater'
@@ -11,6 +11,7 @@ import { registerSessionHandlers } from './ipc/sessionHandlers.ts'
1111
import { initializePaths } from './paths.ts'
1212
import { createAuthWindow, createMainWindow } from './window.ts'
1313

14+
import type { AppPaths } from './paths.ts'
1415
import type { BrowserWindow } from 'electron'
1516

1617
const { autoUpdater } = pkg
@@ -22,31 +23,41 @@ debug({ showDevTools: false, isEnabled: true })
2223
// Environment variables
2324
const DEV_SERVER_URL = process.env.DEV_SERVER_URL
2425

25-
// Initialize paths once
26-
const paths = initializePaths()
27-
2826
// Main window reference
2927
let mainWindow: BrowserWindow | null = null
3028

29+
// Paths - initialized after app is ready
30+
let paths: AppPaths
31+
3132
function getMainWindow() {
3233
return mainWindow
3334
}
3435

36+
/**
37+
* Shows an error dialog to the user and optionally quits the app
38+
*/
39+
function showFatalError(title: string, error: unknown, shouldQuit = true) {
40+
const message = error instanceof Error ? error.message : String(error)
41+
const detail = error instanceof Error ? error.stack : undefined
42+
console.error(`${title}:`, error)
43+
44+
dialog.showErrorBox(title, detail ? `${message}\n\n${detail}` : message)
45+
46+
if (shouldQuit) {
47+
app.quit()
48+
}
49+
}
50+
3551
/**
3652
* Creates the main window and initializes the application
3753
*/
3854
async function initialize() {
39-
try {
40-
await initializeFileSystem(paths)
41-
mainWindow = await createMainWindow(autoUpdater, DEV_SERVER_URL)
55+
await initializeFileSystem(paths)
56+
mainWindow = await createMainWindow(autoUpdater, DEV_SERVER_URL)
4257

43-
mainWindow.on('closed', () => {
44-
mainWindow = null
45-
})
46-
} catch (error) {
47-
console.error('Failed to initialize application:', error)
48-
throw error
49-
}
58+
mainWindow.on('closed', () => {
59+
mainWindow = null
60+
})
5061
}
5162

5263
/**
@@ -72,15 +83,19 @@ function registerIpcHandlers() {
7283
)
7384
}
7485

75-
// Setup auto-updater
86+
// Setup auto-updater (just registers event listeners, safe before ready)
7687
setupAutoUpdater(autoUpdater, getMainWindow)
7788

78-
// Register IPC handlers
79-
registerIpcHandlers()
80-
8189
// App lifecycle handlers
8290
app.on('ready', async () => {
83-
await initialize()
91+
try {
92+
// Initialize paths after app is ready to ensure app.getPath() works reliably
93+
paths = initializePaths()
94+
registerIpcHandlers()
95+
await initialize()
96+
} catch (error) {
97+
showFatalError('Failed to initialize application', error)
98+
}
8499
})
85100

86101
app.on('window-all-closed', () => {
@@ -91,6 +106,10 @@ app.on('window-all-closed', () => {
91106

92107
app.on('activate', async () => {
93108
if (mainWindow === null) {
94-
await initialize()
109+
try {
110+
await initialize()
111+
} catch (error) {
112+
showFatalError('Failed to create window', error)
113+
}
95114
}
96115
})

products/jbrowse-desktop/electron/window.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import path from 'path'
2-
import url from 'url'
2+
import url, { pathToFileURL } from 'url'
33

44
import electron, { BrowserWindow, Menu, app, shell } from 'electron'
55
import windowStateKeeper from 'electron-window-state'
@@ -15,9 +15,7 @@ const DEFAULT_DEV_SERVER_URL = 'http://localhost:3000'
1515

1616
function getAppUrl(devServerUrl: URL): URL {
1717
if (app.isPackaged) {
18-
return new URL(
19-
`file://${path.join(app.getAppPath(), 'build', 'index.html')}`,
20-
)
18+
return pathToFileURL(path.join(app.getAppPath(), 'build', 'index.html'))
2119
}
2220
return devServerUrl
2321
}

0 commit comments

Comments
 (0)