v1.0.2 - Update banner, smart updater, install type detection

This commit is contained in:
Yuri Karamian
2026-02-10 01:31:54 +01:00
parent c105b83570
commit 6a40f553f1
12 changed files with 110 additions and 13 deletions
+6 -5
View File
@@ -6,11 +6,12 @@ Your notes are stored as standard Markdown files on your local filesystem. No cl
## Download
| Platform | Download |
|----------|----------|
| Linux | [HelixNotes_1.0.1_amd64.AppImage](https://codeberg.org/ArkHost/HelixNotes/releases/download/v1.0.1/HelixNotes_1.0.1_amd64.AppImage) |
| Windows | [HelixNotes_1.0.1_x64-setup.exe](https://codeberg.org/ArkHost/HelixNotes/releases/download/v1.0.1/HelixNotes_1.0.1_x64-setup.exe) |
| macOS | Coming soon |
| Platform | Download | Notes |
|----------|----------|-------|
| Linux (Arch/rolling) | [HelixNotes_1.0.2_amd64.AppImage](https://codeberg.org/ArkHost/HelixNotes/releases/download/v1.0.2/HelixNotes_1.0.2_amd64.AppImage) | Best for Arch, Fedora, openSUSE |
| Linux (Debian/Ubuntu/Mint) | [HelixNotes_1.0.2_amd64.deb](https://codeberg.org/ArkHost/HelixNotes/releases/download/v1.0.2/HelixNotes_1.0.2_amd64.deb) | Ubuntu 22.04+ |
| Windows | [HelixNotes_1.0.2_x64-setup.exe](https://codeberg.org/ArkHost/HelixNotes/releases/download/v1.0.2/HelixNotes_1.0.2_x64-setup.exe) | Windows 10/11 |
| macOS | Coming soon | |
See all releases: [codeberg.org/ArkHost/HelixNotes/releases](https://codeberg.org/ArkHost/HelixNotes/releases)
+1 -1
View File
@@ -2,7 +2,7 @@
"name": "helixnotes",
"private": true,
"license": "AGPL-3.0-or-later",
"version": "1.0.1",
"version": "1.0.2",
"type": "module",
"scripts": {
"dev": "vite dev",
+1 -1
View File
@@ -1778,7 +1778,7 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "helixnotes"
version = "1.0.1"
version = "1.0.2"
dependencies = [
"chrono",
"dirs",
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "helixnotes"
version = "1.0.1"
version = "1.0.2"
description = "Local-first markdown note-taking app"
authors = ["HelixNotes"]
license = "AGPL-3.0-or-later"
+13
View File
@@ -1341,3 +1341,16 @@ fn save_app_config(config: &AppConfig) -> Result<(), String> {
std::fs::write(path, data).map_err(|e| e.to_string())?;
Ok(())
}
// ── Install Type Detection ──
#[tauri::command]
pub fn get_install_type() -> String {
if cfg!(target_os = "windows") {
"windows".to_string()
} else if std::env::var("APPIMAGE").is_ok() {
"appimage".to_string()
} else {
"native".to_string()
}
}
+1
View File
@@ -94,6 +94,7 @@ pub fn run() {
commands::set_ai_settings,
commands::test_ai_connection,
commands::ai_ask,
commands::get_install_type,
]);
if close_to_tray {
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "../node_modules/@tauri-apps/cli/config.schema.json",
"productName": "HelixNotes",
"version": "1.0.1",
"version": "1.0.2",
"identifier": "com.helixnotes.app",
"build": {
"frontendDist": "../build",
+4
View File
@@ -309,3 +309,7 @@ export async function aiAsk(
): Promise<void> {
return invoke("ai_ask", { action, text, customPrompt, requestId });
}
export async function getInstallType(): Promise<string> {
return invoke("get_install_type");
}
+28 -1
View File
@@ -1,5 +1,5 @@
<script lang="ts">
import { showSettings, theme, appConfig } from '$lib/stores/app';
import { showSettings, theme, appConfig, updateAvailable as globalUpdateAvailable, installType, settingsTab } from '$lib/stores/app';
import { setTheme, setAccentColor, setFontSize, setFontFamily, setGeneralSettings, importObsidian, createBackup, listBackups, restoreBackup, deleteBackup, setBackupSettings, setAiSettings, testAiConnection } from '$lib/api';
import { open as openDialog } from '@tauri-apps/plugin-dialog';
import { listen } from '@tauri-apps/api/event';
@@ -24,6 +24,23 @@
}
loadAppVersion();
// Switch to requested tab if set externally (e.g. from update badge)
$effect(() => {
const tab = $settingsTab;
if (tab) {
activeTab = tab as Tab;
$settingsTab = null;
}
});
// Pre-populate from global store if update was already detected at startup
$effect(() => {
const global = $globalUpdateAvailable;
if (global && !updateAvailable) {
updateAvailable = { version: global.version, body: global.body };
}
});
async function handleCheckUpdate() {
updateChecking = true;
updateMessage = null;
@@ -33,6 +50,7 @@
if (update) {
updateObj = update;
updateAvailable = { version: update.version, body: update.body, date: update.date };
globalUpdateAvailable.set({ version: update.version, body: update.body });
updateMessage = { type: 'info', text: `Version ${update.version} is available!` };
} else {
updateMessage = { type: 'success', text: 'You are on the latest version.' };
@@ -1034,6 +1052,7 @@
<div class="update-notes">{updateAvailable.body}</div>
{/if}
</div>
{#if $installType === 'appimage' || $installType === 'windows'}
<button class="update-install-btn" onclick={handleDownloadAndInstall} disabled={updateDownloading}>
{#if updateDownloading}
<svg class="spinner-icon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10" opacity="0.25" /><path d="M12 2a10 10 0 019.95 9" /></svg>
@@ -1050,6 +1069,14 @@
<div class="update-progress-fill" style="width: {updateProgress}%"></div>
</div>
{/if}
{:else}
<a class="update-install-btn" href="https://codeberg.org/ArkHost/HelixNotes/releases" target="_blank" rel="noopener">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M18 13v6a2 2 0 01-2 2H5a2 2 0 01-2-2V8a2 2 0 012-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/>
</svg>
Download from Codeberg
</a>
{/if}
</div>
{/if}
+26 -1
View File
@@ -1,6 +1,6 @@
<script lang="ts">
import { getCurrentWindow } from '@tauri-apps/api/window';
import { vaultReady, focusMode } from '$lib/stores/app';
import { vaultReady, focusMode, updateAvailable, showSettings, settingsTab } from '$lib/stores/app';
let { onNewNote = () => {} }: {
onNewNote?: () => void;
@@ -71,6 +71,12 @@
<line x1="29" y1="18" x2="19" y2="30" stroke="white" stroke-width="2" stroke-linecap="round" opacity="0.7" />
</svg>
<span class="titlebar-title">HelixNotes</span>
{#if $updateAvailable}
<!-- svelte-ignore a11y_no_static_element_interactions -->
<button class="update-badge" onmousedown={(e) => e.stopPropagation()} onclick={() => { $settingsTab = 'updates'; $showSettings = true; }}>
v{$updateAvailable.version} available
</button>
{/if}
</div>
<div class="titlebar-actions">
<button class="switch-vault-btn" onclick={() => ($vaultReady = false)} title="Switch Vault">
@@ -144,6 +150,25 @@
color: var(--text-tertiary);
}
.update-badge {
font-size: 10px;
font-weight: 600;
color: var(--accent);
background: color-mix(in srgb, var(--accent) 12%, transparent);
border: 1px solid color-mix(in srgb, var(--accent) 25%, transparent);
border-radius: 10px;
padding: 1px 8px;
cursor: pointer;
pointer-events: auto;
-webkit-app-region: no-drag;
transition: background 0.15s, border-color 0.15s;
}
.update-badge:hover {
background: color-mix(in srgb, var(--accent) 20%, transparent);
border-color: color-mix(in srgb, var(--accent) 40%, transparent);
}
.titlebar-actions {
display: flex;
align-items: center;
+20
View File
@@ -23,6 +23,7 @@ export const searchQuery = writable("");
export const showCommandPalette = writable(false);
export const showSearch = writable(false);
export const showSettings = writable(false);
export const settingsTab = writable<string | null>(null);
export const showInfo = writable(false);
export const notebookIcons = writable<Record<string, string>>({});
export const quickAccessPaths = writable<string[]>([]);
@@ -45,6 +46,25 @@ export const focusMode = writable(false);
// Theme
export const theme = writable<string>("system");
// Update state
export const updateAvailable = writable<{
version: string;
body?: string;
} | null>(null);
export const installType = writable<string>("native");
export async function checkForUpdate() {
try {
const { check } = await import("@tauri-apps/plugin-updater");
const update = await check();
if (update) {
updateAvailable.set({ version: update.version, body: update.body });
}
} catch {
// Silent fail — don't disrupt app startup
}
}
// Derived
export const sortedNotes = derived([notes, sortMode], ([$notes, $sortMode]) => {
const pinned = $notes.filter((n) => n.meta.pinned);
+8 -2
View File
@@ -1,9 +1,9 @@
<script lang="ts">
import { onMount } from 'svelte';
import '../app.css';
import { theme, appConfig, activeNotePath } from '$lib/stores/app';
import { theme, appConfig, activeNotePath, installType, checkForUpdate } from '$lib/stores/app';
import { openUrl } from '@tauri-apps/plugin-opener';
import { openFile } from '$lib/api';
import { openFile, getInstallType } from '$lib/api';
import { get } from 'svelte/store';
let { children } = $props();
@@ -31,6 +31,12 @@
return resolved.join('/');
}
// Detect install type and check for updates on startup
onMount(() => {
getInstallType().then(t => installType.set(t)).catch(() => {});
checkForUpdate();
});
// Intercept all link clicks in capture phase to prevent webview navigation
onMount(() => {
function handleLinkClick(e: MouseEvent) {