v1.1.4 - Hide asset folders, notebook path in All Notes, nested notebook creation UX

- Hide asset folders (_res, _resources, _attachments, _assets, assets) from notebook listing
- Show notebook path in "All Notes" view
- Sub-notebook context menu + smart parent detection for nested notebook creation
This commit is contained in:
Yuri Karamian
2026-02-19 21:47:22 +01:00
parent a3229d451e
commit b4824973a5
19 changed files with 1588 additions and 472 deletions
+454 -353
View File
File diff suppressed because it is too large Load Diff
+6 -3
View File
@@ -1,6 +1,6 @@
[package]
name = "helixnotes"
version = "1.1.3"
version = "1.1.4"
description = "Local markdown note-taking app"
authors = ["HelixNotes"]
license = "AGPL-3.0-or-later"
@@ -20,7 +20,6 @@ tauri-plugin-log = "2"
tauri-plugin-dialog = "2"
tauri-plugin-fs = "2"
tauri-plugin-opener = "2"
tauri-plugin-updater = "2"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
serde_yaml = "0.9"
@@ -35,7 +34,11 @@ tokio = { version = "1", features = ["full"] }
dirs = "6"
regex = "1"
zip = { version = "2", default-features = false, features = ["deflate"] }
reqwest = { version = "0.12", features = ["json", "stream"] }
reqwest = { version = "0.12", default-features = false, features = ["json", "stream", "rustls-tls"] }
futures = "0.3"
rayon = "1"
rustls = { version = "0.23", default-features = false, features = ["ring", "logging", "std", "tls12"] }
[target.'cfg(not(target_os = "android"))'.dependencies]
tauri-plugin-updater = "2"
tauri-plugin-single-instance = "2"
+1 -2
View File
@@ -44,7 +44,6 @@
{ "url": "mailto:*" }
]
},
"opener:allow-reveal-item-in-dir",
"updater:default"
"opener:allow-reveal-item-in-dir"
]
}
+10
View File
@@ -0,0 +1,10 @@
{
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "desktop",
"description": "Desktop-only capabilities",
"platforms": ["linux", "macOS", "windows"],
"windows": ["main"],
"permissions": [
"updater:default"
]
}
+13 -2
View File
@@ -1450,9 +1450,20 @@ pub fn ai_ask(
// ── Helpers ──
static ANDROID_CONFIG_DIR: std::sync::OnceLock<std::path::PathBuf> = std::sync::OnceLock::new();
pub fn set_android_config_dir(path: std::path::PathBuf) {
let _ = ANDROID_CONFIG_DIR.set(path);
}
fn app_config_path() -> Result<std::path::PathBuf, String> {
let config_dir = dirs::config_dir().ok_or("Cannot find config directory")?;
let app_dir = config_dir.join("helixnotes");
let app_dir = if let Some(config_dir) = dirs::config_dir() {
config_dir.join("helixnotes")
} else if let Some(android_dir) = ANDROID_CONFIG_DIR.get() {
android_dir.join("helixnotes")
} else {
return Err("Config directory not available yet".to_string());
};
std::fs::create_dir_all(&app_dir).map_err(|e| e.to_string())?;
Ok(app_dir.join("config.json"))
}
+41 -15
View File
@@ -8,32 +8,33 @@ mod types;
mod vault;
use state::AppState;
#[allow(unused_imports)]
use tauri::Manager;
#[cfg(not(target_os = "android"))]
use tauri::{
image::Image,
menu::{MenuBuilder, MenuItemBuilder},
tray::TrayIconBuilder,
Manager,
};
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
rustls::crypto::ring::default_provider()
.install_default()
.expect("Failed to install rustls crypto provider");
let config = commands::load_app_config();
#[cfg(not(target_os = "android"))]
let show_tray = config.show_tray_icon;
#[cfg(not(target_os = "android"))]
let close_to_tray = config.close_to_tray && show_tray;
let app_state = AppState::new(config);
let mut builder = tauri::Builder::default()
.plugin(tauri_plugin_single_instance::init(|app, _args, _cwd| {
if let Some(window) = app.get_webview_window("main") {
let _ = window.show();
let _ = window.unminimize();
let _ = window.set_focus();
}
}))
.plugin(tauri_plugin_dialog::init())
.plugin(tauri_plugin_fs::init())
.plugin(tauri_plugin_opener::init())
.plugin(tauri_plugin_updater::Builder::new().build())
.setup(move |app| {
if cfg!(debug_assertions) {
app.handle().plugin(
@@ -43,6 +44,17 @@ pub fn run() {
)?;
}
// On Android, set config dir from Tauri's path resolver
#[cfg(target_os = "android")]
{
if let Ok(config_dir) = app.path().config_dir() {
commands::set_android_config_dir(config_dir);
} else if let Ok(data_dir) = app.path().data_dir() {
commands::set_android_config_dir(data_dir);
}
}
#[cfg(not(target_os = "android"))]
if show_tray {
setup_tray(app)?;
}
@@ -107,13 +119,26 @@ pub fn run() {
commands::get_install_type,
]);
if close_to_tray {
builder = builder.on_window_event(|window, event| {
if let tauri::WindowEvent::CloseRequested { api, .. } = event {
api.prevent_close();
let _ = window.hide();
#[cfg(not(target_os = "android"))]
{
builder = builder.plugin(tauri_plugin_single_instance::init(|app, _args, _cwd| {
if let Some(window) = app.get_webview_window("main") {
let _ = window.show();
let _ = window.unminimize();
let _ = window.set_focus();
}
});
}));
builder = builder.plugin(tauri_plugin_updater::Builder::new().build());
if close_to_tray {
builder = builder.on_window_event(|window, event| {
if let tauri::WindowEvent::CloseRequested { api, .. } = event {
api.prevent_close();
let _ = window.hide();
}
});
}
}
builder
@@ -121,6 +146,7 @@ pub fn run() {
.expect("error while running tauri application");
}
#[cfg(not(target_os = "android"))]
fn setup_tray(app: &mut tauri::App) -> Result<(), Box<dyn std::error::Error>> {
let show = MenuItemBuilder::with_id("show", "Show HelixNotes").build(app)?;
let quit = MenuItemBuilder::with_id("quit", "Quit").build(app)?;
+27 -10
View File
@@ -5,7 +5,10 @@ use std::fs;
use std::path::Path;
use std::sync::Mutex;
use tantivy::collector::TopDocs;
#[cfg(not(target_os = "android"))]
use tantivy::directory::MmapDirectory;
#[cfg(target_os = "android")]
use tantivy::directory::RamDirectory;
use tantivy::query::QueryParser;
use tantivy::schema::*;
use tantivy::{Index, IndexWriter, TantivyDocument};
@@ -24,9 +27,6 @@ pub struct SearchIndex {
impl SearchIndex {
pub fn new(vault_path: &str) -> Result<Self, String> {
let index_dir = helixnotes_dir(vault_path).join("search_index");
fs::create_dir_all(&index_dir).map_err(|e| e.to_string())?;
let mut schema_builder = Schema::builder();
let path_field = schema_builder.add_text_field("path", STRING | STORED);
let title_field = schema_builder.add_text_field("title", TEXT | STORED);
@@ -34,12 +34,27 @@ impl SearchIndex {
let tags_field = schema_builder.add_text_field("tags", TEXT | STORED);
let schema = schema_builder.build();
let dir = MmapDirectory::open(&index_dir).map_err(|e| e.to_string())?;
let index = Index::open_or_create(dir, schema.clone()).map_err(|e| e.to_string())?;
// Android: use in-memory index (flock doesn't work on /storage/emulated FUSE filesystem)
// Desktop: use mmap directory for persistent index on disk
#[cfg(target_os = "android")]
let index = {
let dir = RamDirectory::create();
Index::open_or_create(dir, schema.clone()).map_err(|e| e.to_string())?
};
#[cfg(not(target_os = "android"))]
let index = {
let index_dir = helixnotes_dir(vault_path).join("search_index");
fs::create_dir_all(&index_dir).map_err(|e| e.to_string())?;
let dir = MmapDirectory::open(&index_dir).map_err(|e| e.to_string())?;
Index::open_or_create(dir, schema.clone()).map_err(|e| e.to_string())?
};
let writer = index
.writer(50_000_000)
.map_err(|e| e.to_string())?;
#[cfg(target_os = "android")]
let heap_size = 15_000_000;
#[cfg(not(target_os = "android"))]
let heap_size = 50_000_000;
let writer = index.writer(heap_size).map_err(|e| e.to_string())?;
Ok(Self {
index,
@@ -135,8 +150,10 @@ impl SearchIndex {
let reader = self.index.reader().map_err(|e| e.to_string())?;
let searcher = reader.searcher();
let query_parser =
QueryParser::for_index(&self.index, vec![self.title_field, self.body_field, self.tags_field]);
let query_parser = QueryParser::for_index(
&self.index,
vec![self.title_field, self.body_field, self.tags_field],
);
let query = query_parser
.parse_query(query_str)
+7 -1
View File
@@ -114,7 +114,13 @@ fn count_notes_in_dir(dir: &Path) -> usize {
fn is_hidden(path: &Path) -> bool {
path.file_name()
.and_then(|n| n.to_str())
.map(|n| n.starts_with('.'))
.map(|n| {
n.starts_with('.')
|| matches!(
n,
"_res" | "_resources" | "_attachments" | "_assets" | "assets" | "node_modules"
)
})
.unwrap_or(false)
}
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "../node_modules/@tauri-apps/cli/config.schema.json",
"productName": "HelixNotes",
"version": "1.1.3",
"version": "1.1.4",
"identifier": "com.helixnotes.app",
"build": {
"frontendDist": "../build",