14 changed files with 4485 additions and 105 deletions
@ -0,0 +1,51 @@
@@ -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 @@
@@ -0,0 +1,7 @@
|
||||
'use strict' |
||||
|
||||
const { contextBridge } = require('electron') |
||||
|
||||
contextBridge.exposeInMainWorld('jumbleElectron', { |
||||
isElectron: true |
||||
}) |
||||
@ -0,0 +1,43 @@
@@ -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