diff --git a/package.json b/package.json index 0afad80b..aee2a8a0 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "private": true, "main": "./out/main/index.js", "author": { - "name": "Adi Bhanna", + "name": "Adib Hanna", "email": "adibhanna@gmail.com" }, "license": "MIT", @@ -147,7 +147,7 @@ "AppImage", "deb" ], - "maintainer": "Adi Bhanna ", + "maintainer": "Adib Hanna ", "category": "Office" } } diff --git a/src/main/index.ts b/src/main/index.ts index e7486891..91e02988 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -815,7 +815,7 @@ function installAppMenu(): void { { label: 'Check for Updates…', click: () => { - void checkForAppUpdates() + void runMenuUpdateCheck() } }, { type: 'separator' }, @@ -878,6 +878,86 @@ function installAppMenu(): void { Menu.setApplicationMenu(Menu.buildFromTemplate(template)) } +async function runMenuUpdateCheck(): Promise { + const parent = BrowserWindow.getFocusedWindow() ?? mainWindow ?? undefined + const showDialog = async ( + options: Electron.MessageBoxOptions + ): Promise => { + return parent + ? await dialog.showMessageBox(parent, options) + : await dialog.showMessageBox(options) + } + const state = await checkForAppUpdates() + + if (state.phase === 'available') { + const { response } = await showDialog({ + type: 'info', + buttons: ['Download Update', 'Later'], + defaultId: 0, + cancelId: 1, + title: 'ZenNotes Update Available', + message: `ZenNotes ${state.availableVersion ?? ''} is available.`, + detail: state.message + }) + if (response === 0) { + void downloadAppUpdate() + await showDialog({ + type: 'info', + buttons: ['OK'], + defaultId: 0, + title: 'Downloading Update', + message: `ZenNotes ${state.availableVersion ?? ''} is downloading in the background.`, + detail: 'Open Settings → About to track progress and install when the download finishes.' + }) + } + return + } + + if (state.phase === 'downloaded') { + const { response } = await showDialog({ + type: 'info', + buttons: ['Install and Relaunch', 'Later'], + defaultId: 0, + cancelId: 1, + title: 'ZenNotes Update Ready', + message: `ZenNotes ${state.availableVersion ?? ''} is ready to install.`, + detail: state.message + }) + if (response === 0) { + installAppUpdate() + } + return + } + + if (state.phase === 'downloading' || state.phase === 'checking') { + await showDialog({ + type: 'info', + buttons: ['OK'], + defaultId: 0, + title: 'ZenNotes Updates', + message: state.phase === 'checking' ? 'Checking for updates…' : 'Downloading update…', + detail: state.message + }) + return + } + + await showDialog({ + type: state.phase === 'error' ? 'warning' : 'info', + buttons: ['OK'], + defaultId: 0, + title: 'ZenNotes Updates', + message: + state.phase === 'not-available' + ? 'ZenNotes is up to date.' + : state.phase === 'unsupported' + ? 'Update checks are unavailable.' + : state.phase === 'error' + ? 'Could not check for updates.' + : 'ZenNotes Updates', + detail: state.message + }) +} + app.whenReady().then(async () => { protocol.handle(LOCAL_ASSET_SCHEME, async (request) => { const abs = decodeLocalAssetRequestPath(request.url) diff --git a/src/renderer/src/components/FloatingNoteApp.tsx b/src/renderer/src/components/FloatingNoteApp.tsx index a3f95caf..168cc70d 100644 --- a/src/renderer/src/components/FloatingNoteApp.tsx +++ b/src/renderer/src/components/FloatingNoteApp.tsx @@ -105,8 +105,8 @@ function loadFloatingPrefs(): FloatingPrefs { vimMode: true, livePreview: true, themeId: DEFAULT_THEME_ID, - themeFamily: 'apple', - themeMode: 'auto', + themeFamily: 'gruvbox', + themeMode: 'dark', editorFontSize: 16, editorLineHeight: 1.7, lineNumberMode: 'off', diff --git a/src/renderer/src/components/SettingsModal.tsx b/src/renderer/src/components/SettingsModal.tsx index 4ecfef11..d7e9fe13 100644 --- a/src/renderer/src/components/SettingsModal.tsx +++ b/src/renderer/src/components/SettingsModal.tsx @@ -126,6 +126,26 @@ function formatBytes(bytes: number | null): string | null { return `${rounded} ${unit}` } +function formatReleaseNotesForDisplay(notes: string | null): string | null { + if (!notes) return null + const trimmed = notes.trim() + if (!trimmed) return null + if (!/[<&]/.test(trimmed)) return trimmed + + try { + const parser = new DOMParser() + const doc = parser.parseFromString(trimmed, 'text/html') + const text = (doc.body.innerText || doc.body.textContent || '') + .replace(/\r\n?/g, '\n') + .replace(/[ \t]+\n/g, '\n') + .replace(/\n{3,}/g, '\n\n') + .trim() + return text || trimmed + } catch { + return trimmed + } +} + export function SettingsModal(): JSX.Element { const setSettingsOpen = useStore((s) => s.setSettingsOpen) const vimMode = useStore((s) => s.vimMode) @@ -243,7 +263,28 @@ export function SettingsModal(): JSX.Element { }, []) const triggerUpdateCheck = useCallback(() => { - void window.zen.checkForAppUpdates() + void window.zen.checkForAppUpdates().then( + (state) => { + if (state.phase === 'available') { + window.alert( + `ZenNotes ${state.availableVersion ?? ''} is available. Use “Download Update” to fetch it.` + ) + return + } + if (state.phase === 'not-available') { + window.alert(state.message) + return + } + if (state.phase === 'unsupported' || state.phase === 'error') { + window.alert(state.message) + } + }, + (error) => { + const message = + error instanceof Error ? error.message : 'Could not check for updates.' + window.alert(message) + } + ) }, []) const triggerUpdateDownload = useCallback(() => { @@ -254,6 +295,11 @@ export function SettingsModal(): JSX.Element { void window.zen.installAppUpdate() }, []) + const displayedReleaseNotes = useMemo( + () => formatReleaseNotesForDisplay(appUpdateState?.releaseNotes ?? null), + [appUpdateState?.releaseNotes] + ) + // Family list — Apple is the default, followed by the other families. const familyOptions = useMemo<{ id: ThemeFamily; label: string }[]>( () => [ @@ -916,13 +962,13 @@ export function SettingsModal(): JSX.Element { )} - {appUpdateState?.releaseNotes && ( + {displayedReleaseNotes && (
Release notes
-                      {appUpdateState.releaseNotes}
+                      {displayedReleaseNotes}
                     
)} diff --git a/src/renderer/src/lib/themes.ts b/src/renderer/src/lib/themes.ts index 8e7b6231..430ab6dc 100644 --- a/src/renderer/src/lib/themes.ts +++ b/src/renderer/src/lib/themes.ts @@ -94,7 +94,7 @@ export const THEMES: ThemeOption[] = [ { id: 'tokyo-night-storm', label: 'Storm', family: 'tokyo-night', mode: 'dark' } ] -export const DEFAULT_THEME_ID = 'apple-light' +export const DEFAULT_THEME_ID = 'dark-hard' export function findTheme(id: string): ThemeOption { return THEMES.find((t) => t.id === id) ?? THEMES[1] // fallback: light-medium diff --git a/src/renderer/src/store.ts b/src/renderer/src/store.ts index 672d8ac8..f7228a0d 100644 --- a/src/renderer/src/store.ts +++ b/src/renderer/src/store.ts @@ -183,20 +183,20 @@ const DEFAULT_PREFS: Prefs = { ripgrepBinaryPath: null, fzfBinaryPath: null, livePreview: true, - tabsEnabled: false, + tabsEnabled: true, themeId: DEFAULT_THEME_ID, - themeFamily: 'apple', - themeMode: 'auto', + themeFamily: 'gruvbox', + themeMode: 'dark', editorFontSize: 16, editorLineHeight: 1.7, previewMaxWidth: 920, lineNumberMode: 'off', - // Ship with SF Mono everywhere — gives the app a single, consistent - // typographic identity out of the box. Users can still change any of - // the three slots from Settings → Fonts. - interfaceFont: 'SF Mono', - textFont: 'SF Mono', - monoFont: 'SF Mono', + // Leave all font slots on the built-in "Default" path. That lets the + // shipped CSS fallbacks choose sensible system fonts on each machine + // instead of forcing a specific family that may not exist. + interfaceFont: null, + textFont: null, + monoFont: null, sidebarWidth: 232, noteListWidth: 300, noteSortOrder: 'none',