diff --git a/src/core/browser.test.ts b/src/core/browser.test.ts new file mode 100644 index 0000000..8ac4574 --- /dev/null +++ b/src/core/browser.test.ts @@ -0,0 +1,21 @@ +import { describe, expect, it, vi } from 'vitest' +import { openGenerationInBrowser } from './browser.js' + +describe('openGenerationInBrowser', () => { + it('opens encoded generation URL and returns null on success', async () => { + const openUrl = vi.fn(async (_target: string) => undefined) + const error = await openGenerationInBrowser('gen/with spaces', openUrl) + + expect(error).toBeNull() + expect(openUrl).toHaveBeenCalledWith('https://www.pixelmuse.studio/g/gen%2Fwith%20spaces') + }) + + it('returns readable error when open fails', async () => { + const openUrl = vi.fn(async (_target: string) => { + throw new Error('No browser available') + }) + const error = await openGenerationInBrowser('abc123', openUrl) + + expect(error).toBe('No browser available') + }) +}) diff --git a/src/core/browser.ts b/src/core/browser.ts new file mode 100644 index 0000000..a8c2c4c --- /dev/null +++ b/src/core/browser.ts @@ -0,0 +1,16 @@ +const GALLERY_URL_BASE = 'https://www.pixelmuse.studio/g/' + +export async function openGenerationInBrowser( + generationId: string, + openUrl: (target: string) => Promise, +): Promise { + const generationUrl = `${GALLERY_URL_BASE}${encodeURIComponent(generationId)}` + + try { + await openUrl(generationUrl) + return null + } catch (err) { + if (err instanceof Error && err.message) return err.message + return 'Failed to open browser' + } +} diff --git a/src/core/index.ts b/src/core/index.ts index c1d5d55..133e8c5 100644 --- a/src/core/index.ts +++ b/src/core/index.ts @@ -38,3 +38,4 @@ export { timeAgo } from './utils.js' export { initiateDeviceAuth, pollForToken } from './device-auth.js' export { detectEditors, configureMcp, type EditorInfo } from './mcp-config.js' export { buildMacClipboardArgs } from './clipboard.js' +export { openGenerationInBrowser } from './browser.js' diff --git a/src/screens/GalleryDetail.tsx b/src/screens/GalleryDetail.tsx index 5953137..9e16798 100644 --- a/src/screens/GalleryDetail.tsx +++ b/src/screens/GalleryDetail.tsx @@ -5,6 +5,7 @@ import open from 'open' import type { PixelmuseClient } from '../core/client.js' import type { Generation } from '../core/types.js' import { imageToBuffer, autoSave } from '../core/image.js' +import { openGenerationInBrowser } from '../core/browser.js' import ImagePreview from '../components/ImagePreview.js' interface Props { @@ -49,7 +50,12 @@ export default function GalleryDetail({ client, generationId, back }: Props) { if (confirming) return if (input === 'd') setConfirming(true) if (input === 'o' && generation) { - open(`https://www.pixelmuse.studio/g/${generation.id}`) + void (async () => { + const openError = await openGenerationInBrowser(generation.id, open) + if (openError) { + setError(openError) + } + })() } })