Skip to content

Commit 4fcb6f6

Browse files
authored
[codex] Polish defaults and updater feedback (#15)
* Polish defaults and updater feedback * Show native update feedback from app menu * Harden release note normalization * Strip remaining tag brackets from release notes * Move release note formatting to renderer
1 parent ff191c1 commit 4fcb6f6

6 files changed

Lines changed: 144 additions & 18 deletions

File tree

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
"private": true,
77
"main": "./out/main/index.js",
88
"author": {
9-
"name": "Adi Bhanna",
9+
"name": "Adib Hanna",
1010
"email": "adibhanna@gmail.com"
1111
},
1212
"license": "MIT",
@@ -147,7 +147,7 @@
147147
"AppImage",
148148
"deb"
149149
],
150-
"maintainer": "Adi Bhanna <adibhanna@gmail.com>",
150+
"maintainer": "Adib Hanna <adibhanna@gmail.com>",
151151
"category": "Office"
152152
}
153153
}

src/main/index.ts

Lines changed: 81 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -815,7 +815,7 @@ function installAppMenu(): void {
815815
{
816816
label: 'Check for Updates…',
817817
click: () => {
818-
void checkForAppUpdates()
818+
void runMenuUpdateCheck()
819819
}
820820
},
821821
{ type: 'separator' },
@@ -878,6 +878,86 @@ function installAppMenu(): void {
878878
Menu.setApplicationMenu(Menu.buildFromTemplate(template))
879879
}
880880

881+
async function runMenuUpdateCheck(): Promise<void> {
882+
const parent = BrowserWindow.getFocusedWindow() ?? mainWindow ?? undefined
883+
const showDialog = async (
884+
options: Electron.MessageBoxOptions
885+
): Promise<Electron.MessageBoxReturnValue> => {
886+
return parent
887+
? await dialog.showMessageBox(parent, options)
888+
: await dialog.showMessageBox(options)
889+
}
890+
const state = await checkForAppUpdates()
891+
892+
if (state.phase === 'available') {
893+
const { response } = await showDialog({
894+
type: 'info',
895+
buttons: ['Download Update', 'Later'],
896+
defaultId: 0,
897+
cancelId: 1,
898+
title: 'ZenNotes Update Available',
899+
message: `ZenNotes ${state.availableVersion ?? ''} is available.`,
900+
detail: state.message
901+
})
902+
if (response === 0) {
903+
void downloadAppUpdate()
904+
await showDialog({
905+
type: 'info',
906+
buttons: ['OK'],
907+
defaultId: 0,
908+
title: 'Downloading Update',
909+
message: `ZenNotes ${state.availableVersion ?? ''} is downloading in the background.`,
910+
detail: 'Open Settings → About to track progress and install when the download finishes.'
911+
})
912+
}
913+
return
914+
}
915+
916+
if (state.phase === 'downloaded') {
917+
const { response } = await showDialog({
918+
type: 'info',
919+
buttons: ['Install and Relaunch', 'Later'],
920+
defaultId: 0,
921+
cancelId: 1,
922+
title: 'ZenNotes Update Ready',
923+
message: `ZenNotes ${state.availableVersion ?? ''} is ready to install.`,
924+
detail: state.message
925+
})
926+
if (response === 0) {
927+
installAppUpdate()
928+
}
929+
return
930+
}
931+
932+
if (state.phase === 'downloading' || state.phase === 'checking') {
933+
await showDialog({
934+
type: 'info',
935+
buttons: ['OK'],
936+
defaultId: 0,
937+
title: 'ZenNotes Updates',
938+
message: state.phase === 'checking' ? 'Checking for updates…' : 'Downloading update…',
939+
detail: state.message
940+
})
941+
return
942+
}
943+
944+
await showDialog({
945+
type: state.phase === 'error' ? 'warning' : 'info',
946+
buttons: ['OK'],
947+
defaultId: 0,
948+
title: 'ZenNotes Updates',
949+
message:
950+
state.phase === 'not-available'
951+
? 'ZenNotes is up to date.'
952+
: state.phase === 'unsupported'
953+
? 'Update checks are unavailable.'
954+
: state.phase === 'error'
955+
? 'Could not check for updates.'
956+
: 'ZenNotes Updates',
957+
detail: state.message
958+
})
959+
}
960+
881961
app.whenReady().then(async () => {
882962
protocol.handle(LOCAL_ASSET_SCHEME, async (request) => {
883963
const abs = decodeLocalAssetRequestPath(request.url)

src/renderer/src/components/FloatingNoteApp.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,8 @@ function loadFloatingPrefs(): FloatingPrefs {
105105
vimMode: true,
106106
livePreview: true,
107107
themeId: DEFAULT_THEME_ID,
108-
themeFamily: 'apple',
109-
themeMode: 'auto',
108+
themeFamily: 'gruvbox',
109+
themeMode: 'dark',
110110
editorFontSize: 16,
111111
editorLineHeight: 1.7,
112112
lineNumberMode: 'off',

src/renderer/src/components/SettingsModal.tsx

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,26 @@ function formatBytes(bytes: number | null): string | null {
126126
return `${rounded} ${unit}`
127127
}
128128

129+
function formatReleaseNotesForDisplay(notes: string | null): string | null {
130+
if (!notes) return null
131+
const trimmed = notes.trim()
132+
if (!trimmed) return null
133+
if (!/[<&]/.test(trimmed)) return trimmed
134+
135+
try {
136+
const parser = new DOMParser()
137+
const doc = parser.parseFromString(trimmed, 'text/html')
138+
const text = (doc.body.innerText || doc.body.textContent || '')
139+
.replace(/\r\n?/g, '\n')
140+
.replace(/[ \t]+\n/g, '\n')
141+
.replace(/\n{3,}/g, '\n\n')
142+
.trim()
143+
return text || trimmed
144+
} catch {
145+
return trimmed
146+
}
147+
}
148+
129149
export function SettingsModal(): JSX.Element {
130150
const setSettingsOpen = useStore((s) => s.setSettingsOpen)
131151
const vimMode = useStore((s) => s.vimMode)
@@ -243,7 +263,28 @@ export function SettingsModal(): JSX.Element {
243263
}, [])
244264

245265
const triggerUpdateCheck = useCallback(() => {
246-
void window.zen.checkForAppUpdates()
266+
void window.zen.checkForAppUpdates().then(
267+
(state) => {
268+
if (state.phase === 'available') {
269+
window.alert(
270+
`ZenNotes ${state.availableVersion ?? ''} is available. Use “Download Update” to fetch it.`
271+
)
272+
return
273+
}
274+
if (state.phase === 'not-available') {
275+
window.alert(state.message)
276+
return
277+
}
278+
if (state.phase === 'unsupported' || state.phase === 'error') {
279+
window.alert(state.message)
280+
}
281+
},
282+
(error) => {
283+
const message =
284+
error instanceof Error ? error.message : 'Could not check for updates.'
285+
window.alert(message)
286+
}
287+
)
247288
}, [])
248289

249290
const triggerUpdateDownload = useCallback(() => {
@@ -254,6 +295,11 @@ export function SettingsModal(): JSX.Element {
254295
void window.zen.installAppUpdate()
255296
}, [])
256297

298+
const displayedReleaseNotes = useMemo(
299+
() => formatReleaseNotesForDisplay(appUpdateState?.releaseNotes ?? null),
300+
[appUpdateState?.releaseNotes]
301+
)
302+
257303
// Family list — Apple is the default, followed by the other families.
258304
const familyOptions = useMemo<{ id: ThemeFamily; label: string }[]>(
259305
() => [
@@ -916,13 +962,13 @@ export function SettingsModal(): JSX.Element {
916962
</div>
917963
</div>
918964
)}
919-
{appUpdateState?.releaseNotes && (
965+
{displayedReleaseNotes && (
920966
<details className="mt-3 rounded-xl border border-paper-300/60 bg-paper-100/60 px-3 py-2.5">
921967
<summary className="cursor-pointer text-xs font-medium uppercase tracking-[0.16em] text-ink-500">
922968
Release notes
923969
</summary>
924970
<pre className="mt-2 whitespace-pre-wrap font-sans text-sm leading-6 text-ink-600">
925-
{appUpdateState.releaseNotes}
971+
{displayedReleaseNotes}
926972
</pre>
927973
</details>
928974
)}

src/renderer/src/lib/themes.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ export const THEMES: ThemeOption[] = [
9494
{ id: 'tokyo-night-storm', label: 'Storm', family: 'tokyo-night', mode: 'dark' }
9595
]
9696

97-
export const DEFAULT_THEME_ID = 'apple-light'
97+
export const DEFAULT_THEME_ID = 'dark-hard'
9898

9999
export function findTheme(id: string): ThemeOption {
100100
return THEMES.find((t) => t.id === id) ?? THEMES[1] // fallback: light-medium

src/renderer/src/store.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -183,20 +183,20 @@ const DEFAULT_PREFS: Prefs = {
183183
ripgrepBinaryPath: null,
184184
fzfBinaryPath: null,
185185
livePreview: true,
186-
tabsEnabled: false,
186+
tabsEnabled: true,
187187
themeId: DEFAULT_THEME_ID,
188-
themeFamily: 'apple',
189-
themeMode: 'auto',
188+
themeFamily: 'gruvbox',
189+
themeMode: 'dark',
190190
editorFontSize: 16,
191191
editorLineHeight: 1.7,
192192
previewMaxWidth: 920,
193193
lineNumberMode: 'off',
194-
// Ship with SF Mono everywhere — gives the app a single, consistent
195-
// typographic identity out of the box. Users can still change any of
196-
// the three slots from Settings → Fonts.
197-
interfaceFont: 'SF Mono',
198-
textFont: 'SF Mono',
199-
monoFont: 'SF Mono',
194+
// Leave all font slots on the built-in "Default" path. That lets the
195+
// shipped CSS fallbacks choose sensible system fonts on each machine
196+
// instead of forcing a specific family that may not exist.
197+
interfaceFont: null,
198+
textFont: null,
199+
monoFont: null,
200200
sidebarWidth: 232,
201201
noteListWidth: 300,
202202
noteSortOrder: 'none',

0 commit comments

Comments
 (0)