v0.2.1 is ready! Adds independent mobile scrolling in folder nav and note list for better experience on phones/small screens

This commit is contained in:
mrkmntal 2026-10-05 16:31:19 -04:00
commit 010d9f99f6
6 changed files with 68 additions and 3 deletions

View file

@ -52,7 +52,7 @@ Re-filing a note has **three entry points, and they are not equivalent**. `File
Drag-and-drop is the third path and **does work on touch** — this was previously documented here as impossible, which was wrong. On a touch screen the gesture is a *long press* rather than an immediate drag, verified working on iOS 18.7 / Firefox 156. It is not universal: it requires JS, and in engines without touch support for HTML5 drag the same gesture scrolls the sidebar instead. So a phone's three paths are all live; do not assume drag is desktop-only when adding UI around it.
Two touch-specific details follow from the long press. `.note-item` sets `-webkit-touch-callout: none` so Safari's long-press menu does not compete with the gesture, while leaving `user-select` alone so preview text stays copyable. And the `dragstart` text-selection guard in `app.js` is gated on `matchMedia('(hover: hover)')` — a long press creates a selection *before* the drag starts, so an ungated guard cancels the gesture itself. `.folder-nav` is `position: sticky` inside the scrolling `.sidebar`, because nothing auto-scrolls a nested overflow container mid-drag and the folders are the drop targets.
Two touch-specific details follow from the long press. `.note-item` sets `-webkit-touch-callout: none` so Safari's long-press menu does not compete with the gesture, while leaving `user-select` alone so preview text stays copyable. And the `dragstart` text-selection guard in `app.js` is gated on `matchMedia('(hover: hover)')` — a long press creates a selection *before* the drag starts, so an ungated guard cancels the gesture itself. `.folder-nav` is `position: sticky` inside the scrolling `.sidebar` on desktop, because nothing auto-scrolls a nested overflow container mid-drag and the folders are the drop targets. On mobile, the sidebar does not scroll; instead `.folder-nav` and `.note-list` each scroll independently within their own bounded areas, and the pull-tab toggle stays visible above the scrolling folder list.
Neither entry point may carry `.desktop-only`. That class hides at `max-width: 600px`, and the bar is already hidden above 600px by a `min-width` query; adding the class composes to hiding it at *every* width and phones lose re-filing entirely. That shipped once. No Rust test can observe `display: none`, so `move_entry_points_are_not_desktop_only` pins the markup contract instead. All forms post the same `folder` field, where an empty value means the root. See §11 for the rename rule that makes a move destructive if done any other way, and for why user-supplied folder names force the flyout's `max-width`/wrapping.

2
Cargo.lock generated
View file

@ -386,7 +386,7 @@ dependencies = [
[[package]]
name = "eternalibre-notes"
version = "0.2.0"
version = "0.2.1"
dependencies = [
"anyhow",
"axum",

View file

@ -1,6 +1,6 @@
[package]
name = "eternalibre-notes"
version = "0.2.0"
version = "0.2.1"
edition = "2021"
license = "GPL-3.0-or-later"
description = "EternaLibre Notes - a server-side rendered, copyleft Markdown notes application"

View file

@ -14,6 +14,12 @@ binds to all interfaces by default, so you can reach your notes from a phone or
another machine on your network, protected by a TOTP code from your
authenticator app. No telemetry, no accounts, no third-party services.
## What's new in 0.2.1
- **Collapsible folder navigation** — a pull-tab button on mobile and a `View → Hide/Show Folder Nav` toggle at any width let you collapse the folder list to give the note list more room
- **Independent mobile scrolling** — the folder nav and note list each scroll within their own bounded area on phones, instead of sharing one scroll position
- **Save Note moved** — the action now lives in the `File` menu alongside `New Note`, rather than under `Edit`
## What's new in 0.2
- **Virtual folders** — organize notes into folders without creating real directories. A folder is encoded as a `[Folder]uuid.md` filename prefix, so a single notes directory can hold many of them

View file

@ -1370,7 +1370,26 @@ button, input, select, textarea {
display: flex;
}
/* Independent scrolling: folder nav and note list each scroll within
their own area. The sidebar itself does not scroll on mobile. The
pull-tab button stays visible above the scrolling folder list. */
.sidebar {
overflow: hidden;
}
.folder-nav-container {
position: static;
display: flex;
flex-direction: column;
max-height: 40%;
flex-shrink: 0;
}
.folder-nav {
overflow-y: auto;
flex: 1;
min-height: 0;
-webkit-overflow-scrolling: touch;
padding: 8px;
}
@ -1380,6 +1399,10 @@ button, input, select, textarea {
}
.note-list {
overflow-y: auto;
flex: 1;
min-height: 0;
-webkit-overflow-scrolling: touch;
padding: 8px;
}

View file

@ -1376,6 +1376,42 @@ async fn save_note_is_in_file_menu() {
);
}
/// On mobile, the folder nav and note list scroll independently within their
/// own bounded areas. The sidebar itself does not scroll, and the pull-tab
/// toggle stays visible above the scrolling folder list.
#[tokio::test]
async fn mobile_folder_nav_scrolls_independently() {
let f = Fixture::provisioned("mobile-independent-scroll");
f.post_authed("/folders", "name=Alpha").await;
let (status, body) = f.get_authed("/").await;
assert_eq!(status, StatusCode::OK);
// The mobile CSS must make the sidebar non-scrollable and give the
// folder nav and note list their own scroll areas.
let mobile_css_start = body
.find("@media (max-width: 600px)")
.expect("mobile media query should exist");
let mobile_css = &body[mobile_css_start..];
assert!(
mobile_css.contains(".sidebar") && mobile_css.contains("overflow: hidden"),
"mobile sidebar should not scroll"
);
assert!(
mobile_css.contains(".folder-nav") && mobile_css.contains("overflow-y: auto"),
"folder nav should scroll independently"
);
assert!(
mobile_css.contains(".note-list") && mobile_css.contains("overflow-y: auto"),
"note list should scroll independently"
);
assert!(
mobile_css.contains("max-height: 40%"),
"folder nav container should have a max height"
);
}
/// The `folder-nav` list, and nothing else.
///
/// Scoped deliberately: `.nav-count` also appears in the inlined stylesheet,