14 changed files with 4485 additions and 105 deletions
@ -0,0 +1,51 @@ |
|||||||
|
'use strict' |
||||||
|
|
||||||
|
const { app, BrowserWindow, shell } = require('electron') |
||||||
|
const path = require('path') |
||||||
|
|
||||||
|
/** True when running from source (`electron .`); false when packaged. */ |
||||||
|
const isDev = !app.isPackaged |
||||||
|
|
||||||
|
function createWindow() { |
||||||
|
const win = new BrowserWindow({ |
||||||
|
width: 1280, |
||||||
|
height: 840, |
||||||
|
minWidth: 400, |
||||||
|
minHeight: 500, |
||||||
|
show: false, |
||||||
|
webPreferences: { |
||||||
|
preload: path.join(__dirname, 'preload.cjs'), |
||||||
|
contextIsolation: true, |
||||||
|
nodeIntegration: false, |
||||||
|
sandbox: true |
||||||
|
} |
||||||
|
}) |
||||||
|
|
||||||
|
win.once('ready-to-show', () => win.show()) |
||||||
|
|
||||||
|
if (isDev) { |
||||||
|
const devUrl = process.env.VITE_DEV_SERVER_URL || 'http://127.0.0.1:5173' |
||||||
|
win.loadURL(devUrl) |
||||||
|
win.webContents.openDevTools({ mode: 'detach' }) |
||||||
|
} else { |
||||||
|
win.loadFile(path.join(__dirname, '..', 'dist', 'index.html')) |
||||||
|
} |
||||||
|
|
||||||
|
win.webContents.setWindowOpenHandler(({ url }) => { |
||||||
|
if (url.startsWith('http:') || url.startsWith('https:')) { |
||||||
|
void shell.openExternal(url) |
||||||
|
} |
||||||
|
return { action: 'deny' } |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
app.whenReady().then(() => { |
||||||
|
createWindow() |
||||||
|
app.on('activate', () => { |
||||||
|
if (BrowserWindow.getAllWindows().length === 0) createWindow() |
||||||
|
}) |
||||||
|
}) |
||||||
|
|
||||||
|
app.on('window-all-closed', () => { |
||||||
|
if (process.platform !== 'darwin') app.quit() |
||||||
|
}) |
||||||
@ -0,0 +1,7 @@ |
|||||||
|
'use strict' |
||||||
|
|
||||||
|
const { contextBridge } = require('electron') |
||||||
|
|
||||||
|
contextBridge.exposeInMainWorld('jumbleElectron', { |
||||||
|
isElectron: true |
||||||
|
}) |
||||||
@ -0,0 +1,43 @@ |
|||||||
|
import { Button } from '@/components/ui/button' |
||||||
|
import { DESKTOP_APP_DOWNLOAD_URL_DEFAULT } from '@/constants' |
||||||
|
import { cn } from '@/lib/utils' |
||||||
|
import { Download } from 'lucide-react' |
||||||
|
import { useTranslation } from 'react-i18next' |
||||||
|
|
||||||
|
function resolveDesktopDownloadUrl(): string | null { |
||||||
|
if (typeof window === 'undefined') return DESKTOP_APP_DOWNLOAD_URL_DEFAULT |
||||||
|
const fromConfig = window.__RUNTIME_CONFIG__?.DESKTOP_DOWNLOAD_URL |
||||||
|
if (fromConfig !== undefined) { |
||||||
|
const trimmed = fromConfig.trim() |
||||||
|
return trimmed.length > 0 ? trimmed : null |
||||||
|
} |
||||||
|
return DESKTOP_APP_DOWNLOAD_URL_DEFAULT |
||||||
|
} |
||||||
|
|
||||||
|
/** Bottom-of-sidebar link to native (Electron) builds; hidden in the packaged app and when URL is disabled. */ |
||||||
|
export default function DownloadDesktopSidebarButton() { |
||||||
|
const { t } = useTranslation() |
||||||
|
if (typeof window !== 'undefined' && window.jumbleElectron?.isElectron) { |
||||||
|
return null |
||||||
|
} |
||||||
|
const href = resolveDesktopDownloadUrl() |
||||||
|
if (!href) return null |
||||||
|
|
||||||
|
return ( |
||||||
|
<Button |
||||||
|
variant="ghost" |
||||||
|
className={cn( |
||||||
|
'flex shadow-none items-center transition-colors duration-500 bg-transparent w-12 h-12 xl:w-full xl:h-auto xl:min-w-0 p-3 m-0 xl:py-2 xl:pl-3 xl:pr-4 rounded-lg xl:justify-start gap-3 text-lg font-semibold [&_svg]:size-full xl:[&_svg]:size-4 xl:[&_svg]:shrink-0', |
||||||
|
'text-muted-foreground hover:text-foreground' |
||||||
|
)} |
||||||
|
asChild |
||||||
|
> |
||||||
|
<a href={href} target="_blank" rel="noopener noreferrer" title={t('downloadDesktopApp')}> |
||||||
|
<Download strokeWidth={2.5} aria-hidden /> |
||||||
|
<div className="max-xl:hidden min-w-0 flex-1 text-left break-words leading-snug pr-0.5"> |
||||||
|
{t('downloadDesktopApp')} |
||||||
|
</div> |
||||||
|
</a> |
||||||
|
</Button> |
||||||
|
) |
||||||
|
} |
||||||
Loading…
Reference in new issue