Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 4 additions & 13 deletions frontend/electron/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -745,23 +745,14 @@ function createMenu() {
{
label: 'Report Issue',
click: () => {
shell.openExternal('https://github.com/Logikbug/prompd.app/issues')
shell.openExternal('https://github.com/Prompd/prompd-app/issues')
}
},
{ type: 'separator' },
{
label: 'Check for Updates',
click: () => {
getAutoUpdater().checkForUpdatesAndNotify()
if (mainWindow) {
dialog.showMessageBox(mainWindow, {
type: 'info',
title: 'Checking for Updates',
message: 'Checking for updates...',
detail: 'You will be notified if an update is available.',
buttons: ['OK']
})
}
getAutoUpdater().checkForUpdates()
}
},
{ type: 'separator' },
Expand Down Expand Up @@ -825,7 +816,7 @@ app.whenReady().then(() => {
// Check for updates on startup (only in production)
if (!process.env.ELECTRON_START_URL) {
console.log('[Electron] Checking for updates...')
getAutoUpdater().checkForUpdatesAndNotify()
getAutoUpdater().checkForUpdates()
}

// Initialize GA4 analytics (opt-in, anonymous)
Expand Down Expand Up @@ -2787,7 +2778,7 @@ ipcMain.handle('app:quit', async () => { app.quit() })
ipcMain.handle('app:checkForUpdates', async () => {
try {
const updater = getAutoUpdater()
updater.checkForUpdatesAndNotify()
updater.checkForUpdates()
} catch (e) {
console.warn('[Electron] Auto-updater check failed:', e.message)
}
Expand Down
3 changes: 2 additions & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@prompd/app",
"version": "0.5.0-beta.3",
"version": "0.5.0-beta.5",
"productName": "Prompd",
"description": "AI prompt editor with package-based inheritance",
"author": "Prompd LLC",
Expand Down Expand Up @@ -110,6 +110,7 @@
"category": "Development"
},
"nsis": {
"artifactName": "${productName}-Setup-${version}.${ext}",
"guid": "app.prompd",
"oneClick": false,
"allowToChangeInstallationDirectory": true,
Expand Down
2 changes: 1 addition & 1 deletion frontend/public/licenses.json
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@
"path": "C:\\git\\github\\Prompd\\prompd-app\\frontend\\node_modules\\@prompd\\scheduler\\node_modules\\@pkgjs\\parseargs",
"licenseFile": "C:\\git\\github\\Prompd\\prompd-app\\frontend\\node_modules\\@prompd\\scheduler\\node_modules\\@pkgjs\\parseargs\\LICENSE"
},
"@prompd/app@0.5.0-beta.3": {
"@prompd/app@0.5.0-beta.5": {
"licenses": "UNLICENSED",
"private": true,
"publisher": "Prompd LLC",
Expand Down
23 changes: 18 additions & 5 deletions frontend/scripts/afterPack.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
* Runs after the app is packed but BEFORE installers are created.
* Tasks:
* 1. Verify @prompd/cli is installed from npm
* 2. Embed icon into Windows executable
* 2. Embed icon and product metadata into Windows executable
* (needed because signAndEditExecutable is false — no code signing cert)
*/

const { execSync } = require('child_process')
Expand Down Expand Up @@ -93,12 +94,24 @@ module.exports = async function(context) {

console.log('[afterPack] Using rcedit:', rceditPath)

const appInfo = context.packager.appInfo
const version = appInfo.version

try {
execSync(`"${rceditPath}" "${exePath}" --set-icon "${iconPath}"`, {
stdio: 'inherit'
})
// Set icon
execSync(`"${rceditPath}" "${exePath}" --set-icon "${iconPath}"`, { stdio: 'inherit' })
console.log('[afterPack] Icon embedded successfully!')

// Set product metadata so Task Manager shows "Prompd" instead of "Electron"
execSync(`"${rceditPath}" "${exePath}" --set-version-string "ProductName" "Prompd"`, { stdio: 'inherit' })
execSync(`"${rceditPath}" "${exePath}" --set-version-string "FileDescription" "Prompd"`, { stdio: 'inherit' })
execSync(`"${rceditPath}" "${exePath}" --set-version-string "CompanyName" "Prompd LLC"`, { stdio: 'inherit' })
execSync(`"${rceditPath}" "${exePath}" --set-version-string "LegalCopyright" "Copyright Prompd LLC"`, { stdio: 'inherit' })
execSync(`"${rceditPath}" "${exePath}" --set-version-string "OriginalFilename" "Prompd.exe"`, { stdio: 'inherit' })
execSync(`"${rceditPath}" "${exePath}" --set-product-version "${version}"`, { stdio: 'inherit' })
execSync(`"${rceditPath}" "${exePath}" --set-file-version "${version}"`, { stdio: 'inherit' })
console.log('[afterPack] Product metadata embedded successfully!')
} catch (error) {
console.error('[afterPack] ERROR: Failed to embed icon:', error.message)
console.error('[afterPack] ERROR: Failed to embed exe metadata:', error.message)
}
}
22 changes: 22 additions & 0 deletions frontend/src/modules/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3757,6 +3757,28 @@ version: 1.0.0
}
}, [])

// Auto-update notifications via toasts
useEffect(() => {
const api = window.electronAPI
if (!api?.isElectron) return

const cleanups: (() => void)[] = []

cleanups.push(api.onUpdateAvailable((info) => {
addToast(`Version ${info.version} is available and downloading...`, 'update', 10000)
}))

cleanups.push(api.onUpdateDownloaded((info) => {
addToast(`Version ${info.version} is ready to install`, 'update', 0, {
label: 'Restart & Update',
onClick: () => api.installUpdate(),
})
}))

return () => cleanups.forEach(fn => fn())
}, [addToast])


// Add file to content reference field
const addToContentField = useCallback((filePath: string, field: 'system' | 'assistant' | 'context' | 'user' | 'response', currentFilePath?: string) => {
let pathToInsert = filePath
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/modules/components/TitleBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ function buildMenus(ms: MenuState): MenuDef[] {
label: 'Help',
items: [
{ label: 'Documentation', action: () => api.openExternal('https://prompd.io/docs') },
{ label: 'Report Issue', action: () => api.openExternal('https://github.com/Logikbug/prompd.app/issues') },
{ label: 'Report Issue', action: () => api.openExternal('https://github.com/Prompd/prompd-app/issues') },
{ type: 'separator' },
{ label: 'Check for Updates', action: () => api.checkForUpdates() },
{ type: 'separator' },
Expand Down
14 changes: 13 additions & 1 deletion frontend/src/modules/components/ToastContainer.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useUIStore, selectToasts } from '../../stores/uiStore'
import { X, CheckCircle, AlertCircle, AlertTriangle, Info } from 'lucide-react'
import { X, CheckCircle, AlertCircle, AlertTriangle, Info, Download } from 'lucide-react'

/**
* Toast notification container - renders all active toasts
Expand All @@ -20,8 +20,20 @@ export function ToastContainer() {
{toast.type === 'error' && <AlertCircle size={16} />}
{toast.type === 'warning' && <AlertTriangle size={16} />}
{toast.type === 'info' && <Info size={16} />}
{toast.type === 'update' && <Download size={16} />}
</span>
<span className="toast-message">{toast.message}</span>
{toast.action && (
<button
className="toast-action"
onClick={() => {
toast.action!.onClick()
removeToast(toast.id)
}}
>
{toast.action.label}
</button>
)}
<button
className="toast-close"
onClick={() => removeToast(toast.id)}
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/stores/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,9 @@ export type ModalType =
export interface Toast {
id: string
message: string
type: 'info' | 'warning' | 'error' | 'success'
type: 'info' | 'warning' | 'error' | 'success' | 'update'
duration?: number // milliseconds, 0 = persistent
action?: { label: string; onClick: () => void }
}

/**
Expand Down
6 changes: 3 additions & 3 deletions frontend/src/stores/uiStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ interface UIActions {
setSelectedEnvFile: (file: string | null) => void

// Toast notifications
addToast: (message: string, type?: Toast['type'], duration?: number) => string
addToast: (message: string, type?: Toast['type'], duration?: number, action?: Toast['action']) => string
removeToast: (id: string) => void
clearToasts: () => void

Expand Down Expand Up @@ -896,10 +896,10 @@ export const useUIStore = create<UIStore>()(
}),

// Toast notifications
addToast: (message, type = 'info', duration = 5000) => {
addToast: (message, type = 'info', duration = 5000, action) => {
const id = `toast-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`
set((state) => {
state.toasts.push({ id, message, type, duration })
state.toasts.push({ id, message, type, duration, action })
})
// Auto-remove after duration (if not persistent)
if (duration > 0) {
Expand Down
33 changes: 33 additions & 0 deletions frontend/src/styles/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -908,6 +908,22 @@ select.input {
color: var(--text);
}

.toast-action {
flex-shrink: 0;
padding: 2px 10px;
font-size: 11px;
border-radius: 4px;
border: 1px solid var(--accent);
background: rgba(99, 102, 241, 0.15);
color: var(--text);
cursor: pointer;
white-space: nowrap;
}

.toast-action:hover {
background: rgba(99, 102, 241, 0.3);
}

/* Toast type styles */
.toast-info {
border-left: 3px solid var(--accent);
Expand Down Expand Up @@ -941,6 +957,23 @@ select.input {
color: var(--error);
}

.toast-update {
border-left: 3px solid #f59e0b;
}

.toast-update .toast-icon {
color: #f59e0b;
}

.toast-update .toast-action {
border-color: #f59e0b;
background: rgba(245, 158, 11, 0.15);
}

.toast-update .toast-action:hover {
background: rgba(245, 158, 11, 0.3);
}

/* Build Output Panel - Docked Bottom Panel */
.build-output-panel-docked {
position: fixed;
Expand Down
Loading