mirror of
https://gitlab.com/ArkHost/HelixNotes.git
synced 2026-07-23 23:35:57 +02:00
v1.0.2 - Update banner, smart updater, install type detection
This commit is contained in:
@@ -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
@@ -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",
|
||||
|
||||
Generated
+1
-1
@@ -1778,7 +1778,7 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "helixnotes"
|
||||
version = "1.0.1"
|
||||
version = "1.0.2"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"dirs",
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,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",
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user