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
30 changes: 22 additions & 8 deletions .github/workflows/github-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -106,23 +106,32 @@ jobs:
shell: pwsh
run: |
$label = "${{ steps.meta.outputs.label }}"
$installer = Get-ChildItem -Path release -Filter "NASbook Setup $label.exe" -File -ErrorAction Stop
$installer = Get-ChildItem -Path release -Filter "NASbook-Setup-$label.exe" -File -ErrorAction Stop
if ($installer.Count -ne 1) { throw "Expected exactly one NASbook $label installer." }
"path=$($installer.FullName)" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append

$metadata = Get-ChildItem -Path release -Filter "latest.yml" -File -ErrorAction Stop
if ($metadata.Count -ne 1) { throw "Expected exactly one latest.yml update manifest." }
"metadata=$($metadata.FullName)" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append

$blockmap = Get-ChildItem -Path release -Filter "NASbook-Setup-$label.exe.blockmap" -File -ErrorAction Stop
if ($blockmap.Count -ne 1) { throw "Expected exactly one NSIS blockmap." }
"blockmap=$($blockmap.FullName)" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append

- name: Write release notes
if: steps.release-check.outputs.publish == 'true'
shell: pwsh
run: |
@"
## NASbook ${{ steps.meta.outputs.label }}

- Adds fast global search across visible note titles and content, including Arabic text.
- Guarantees queued autosaves finish before note navigation or application close.
- Separates editor HTML from Markdown through a transactional SQLite migration.
- Adds visible recovery for CRUD failures and renderer crashes.
- Reduces the default toolbar to core writing controls while preserving advanced customization.
- Upgrades Electron and resolves all reported npm audit vulnerabilities.
- Fixes section collapse while a note is unlocked and prevents viewport jumping.
- Adds automatic background update checks for installed Windows copies.
- Downloads future releases automatically and installs them on a normal safe exit.
- Preserves the existing application identity and local notes database.

V07 is the first update-enabled release. Install V07 once over V06; future
releases will then arrive through the application automatically.
"@ | Set-Content release-notes.md -Encoding utf8

- name: Publish GitHub Release
Expand All @@ -133,6 +142,8 @@ jobs:
run: |
gh release create "${{ steps.meta.outputs.tag }}" `
"${{ steps.installer.outputs.path }}" `
"${{ steps.installer.outputs.metadata }}" `
"${{ steps.installer.outputs.blockmap }}" `
--title "NASbook ${{ steps.meta.outputs.label }}" `
--notes-file release-notes.md `
--target "${{ github.sha }}"
Expand All @@ -142,6 +153,9 @@ jobs:
uses: actions/upload-artifact@v4
with:
name: NASbook-Setup-${{ steps.meta.outputs.label }}-Windows
path: release/NASbook Setup ${{ steps.meta.outputs.label }}.exe
path: |
release/NASbook-Setup-${{ steps.meta.outputs.label }}.exe
release/NASbook-Setup-${{ steps.meta.outputs.label }}.exe.blockmap
release/latest.yml
if-no-files-found: error
retention-days: 30
17 changes: 15 additions & 2 deletions .github/workflows/windows-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,17 +75,30 @@ jobs:
shell: pwsh
run: |
$label = "${{ steps.release-label.outputs.label }}"
$installer = Get-ChildItem -Path release -Filter "NASbook Setup $label.exe" -File -ErrorAction Stop
$installer = Get-ChildItem -Path release -Filter "NASbook-Setup-$label.exe" -File -ErrorAction Stop
if ($installer.Count -ne 1) {
throw "Expected exactly one NASbook $label installer."
}
Write-Host "Installer: $($installer.FullName)"
Write-Host "Size: $([math]::Round($installer.Length / 1MB, 2)) MB"

$metadata = Get-ChildItem -Path release -Filter "latest.yml" -File -ErrorAction Stop
if ($metadata.Count -ne 1) {
throw "Expected exactly one latest.yml update manifest."
}

$blockmap = Get-ChildItem -Path release -Filter "NASbook-Setup-$label.exe.blockmap" -File -ErrorAction Stop
if ($blockmap.Count -ne 1) {
throw "Expected exactly one NSIS blockmap."
}

- name: Upload installer artifact
uses: actions/upload-artifact@v4
with:
name: NASbook-Setup-${{ steps.release-label.outputs.label }}-Windows
path: release/NASbook Setup ${{ steps.release-label.outputs.label }}.exe
path: |
release/NASbook-Setup-${{ steps.release-label.outputs.label }}.exe
release/NASbook-Setup-${{ steps.release-label.outputs.label }}.exe.blockmap
release/latest.yml
if-no-files-found: error
retention-days: 30
48 changes: 48 additions & 0 deletions design-qa.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# V07 Collapse Chevron Design QA

- Source visual truth: `C:\Users\sufia\AppData\Local\Temp\codex-clipboard-86157121-96a8-47c5-ba3d-c87ad64345ec.png`
- Open implementation: `C:\Users\sufia\AppData\Local\Temp\nasbook-v07-collapse-open.png`
- Closed implementation: `C:\Users\sufia\AppData\Local\Temp\nasbook-v07-collapse-closed.png`
- Combined comparison: `C:\Users\sufia\AppData\Local\Temp\nasbook-v07-collapse-comparison.png`
- Viewport and CSS size: 1320 x 860
- Source pixels: 1320 x 860
- Implementation pixels: 1320 x 860
- Device scale factor: 1; no density normalization required
- State: light theme, RTL note, unlocked editor, same note and scroll anchor

## Findings

No actionable P0, P1, or P2 differences remain in the requested control.

- Fonts and typography: unchanged from the source; heading weight, wrapping, and line height remain intact.
- Spacing and layout rhythm: the control receives a deliberate 38 px inline slot; document width and vertical rhythm remain unchanged.
- Colors and visual tokens: the chevron and its subtle surface use the existing application accent token with improved contrast.
- Image and icon quality: the existing vector-like CSS chevron remains sharp at 1x; no branding or raster assets changed.
- Copy and content: unchanged.
- Accessibility and affordance: the 10 x 10 px stroke sits in a persistent 30 x 30 px visual target, with a stronger hover state.
- Interaction: open points down; closed RTL points left toward the text; hidden content is restored on expansion.
- Stability: heading top remained exactly 319.5 px before and after collapse.

## Full-view Comparison Evidence

The 3960 x 860 combined image places source, open implementation, and closed implementation in one comparison. The application shell, toolbar, editor measure, and note content remain visually unchanged outside the requested chevron treatment.

## Focused-region Evidence

No additional crop was required because each 1320 x 860 source capture preserves the chevron at original 1:1 density and the combined comparison makes both states readable.

## Comparison History

1. Source issue: the small unframed chevron had weak affordance and ambiguous state direction.
2. Fix: consolidated conflicting style rules, increased chevron size and contrast, added a subtle persistent surface, and split closed direction using inherited RTL/LTR writing direction.
3. Post-fix evidence: open transform is 45 degrees with visible content; closed RTL transform is 135 degrees with the next section node hidden; the heading anchor remains fixed.

## Implementation Checklist

- [x] Open state points down.
- [x] Closed state points toward text in RTL and LTR.
- [x] Chevron is prominent without changing branding.
- [x] Collapse does not move the heading anchor.
- [x] Automated tests, production build, and packaged runtime pass.

final result: passed
5 changes: 4 additions & 1 deletion electron/main/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { createGoogleAuthService } from "./googleAuthService";
import { createGoogleDriveBackupService } from "./googleDriveBackupService";
import { createGmailBackupService } from "./gmailBackupService";
import { isSafeExternalUrl } from "../../src/shared/externalUrl";
import { disposeUpdateService, initializeUpdateService } from "./updateService";

const gotTheLock = app.requestSingleInstanceLock();

Expand Down Expand Up @@ -172,7 +173,7 @@ if (!gotTheLock) {

registerIpcHandlers({
appName: app.getName(),
appVersion: "V06",
appVersion: "V07",
database: notesbookDatabase,
settingsStore,
backupService,
Expand All @@ -182,6 +183,7 @@ if (!gotTheLock) {
});

createMainWindow();
initializeUpdateService();

app.on("activate", () => {
if (BrowserWindow.getAllWindows().length === 0) {
Expand All @@ -197,6 +199,7 @@ if (!gotTheLock) {
});

app.on("before-quit", () => {
disposeUpdateService();
notesbookDatabase?.close();
notesbookDatabase = null;
});
Expand Down
75 changes: 75 additions & 0 deletions electron/main/updateService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { app, Notification } from "electron";
import * as electronUpdater from "electron-updater";

const { autoUpdater } = electronUpdater;
const INITIAL_CHECK_DELAY_MS = 10_000;
const UPDATE_CHECK_INTERVAL_MS = 6 * 60 * 60 * 1000;

let initialized = false;
let checkInProgress = false;
let initialCheckTimer: NodeJS.Timeout | null = null;
let periodicCheckTimer: NodeJS.Timeout | null = null;

async function checkForUpdates(): Promise<void> {
if (checkInProgress) return;

checkInProgress = true;
try {
await autoUpdater.checkForUpdates();
} catch (error) {
console.error("Automatic update check failed:", error);
} finally {
checkInProgress = false;
}
}

export function initializeUpdateService(): void {
if (initialized || !app.isPackaged || process.platform !== "win32") return;
initialized = true;

autoUpdater.autoDownload = true;
autoUpdater.autoInstallOnAppQuit = true;
autoUpdater.allowPrerelease = false;
autoUpdater.logger = console;

autoUpdater.on("checking-for-update", () => {
console.info("Checking for NASbook updates.");
});
autoUpdater.on("update-available", (info) => {
console.info(`NASbook update ${info.version} is available; download started.`);
});
autoUpdater.on("update-not-available", (info) => {
console.info(`NASbook ${info.version} is up to date.`);
});
autoUpdater.on("update-downloaded", (info) => {
console.info(`NASbook update ${info.version} is ready and will install on exit.`);
if (Notification.isSupported()) {
new Notification({
title: "NASbook",
body: "تم تنزيل تحديث جديد وسيتم تثبيته عند إغلاق البرنامج.",
silent: true,
}).show();
}
});
autoUpdater.on("error", (error) => {
console.error("NASbook updater error:", error);
});

initialCheckTimer = setTimeout(() => {
initialCheckTimer = null;
void checkForUpdates();
}, INITIAL_CHECK_DELAY_MS);
initialCheckTimer.unref();

periodicCheckTimer = setInterval(() => {
void checkForUpdates();
}, UPDATE_CHECK_INTERVAL_MS);
periodicCheckTimer.unref();
}

export function disposeUpdateService(): void {
if (initialCheckTimer) clearTimeout(initialCheckTimer);
if (periodicCheckTimer) clearInterval(periodicCheckTimer);
initialCheckTimer = null;
periodicCheckTimer = null;
}
Loading
Loading