Skip to content

Standalone mobile nav - #9525

Merged
eliandoran merged 32 commits into
standalonefrom
standalone_mobile_nav
Apr 21, 2026
Merged

Standalone mobile nav#9525
eliandoran merged 32 commits into
standalonefrom
standalone_mobile_nav

Conversation

@eliandoran

Copy link
Copy Markdown
Contributor

No description provided.

@eliandoran eliandoran modified the milestones: v0.103.0, Standalone Apr 21, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📚 Website preview is ready!

🔗 Preview URL: https://pr-9525.trilium-homepage.pages.dev
📖 Production URL: https://triliumnotes.org

✅ All checks passed

This preview will be updated automatically with new commits.

@github-actions

github-actions Bot commented Apr 21, 2026

Copy link
Copy Markdown
Contributor

🖥️ App preview is ready!

🔗 Preview URL: https://pr-9525.trilium-app.pages.dev
📖 Production URL: https://app.triliumnotes.org

✅ All checks passed

This preview will be updated automatically with new commits.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a touch-native MobileNoteNavigator for mobile devices, replacing the previous Fancytree-based implementation. To support this, the tree context menu logic and several command executors were refactored into shared, UI-agnostic modules. The PR also includes dependency updates for Node.js and TypeScript, along with CSS refinements for rendered content. Feedback was provided regarding the navigation stack construction logic under hoisting constraints and the need for a null check when accessing parent nodes in tree commands.

Comment on lines +514 to +524
let start = parentSegments.indexOf(hoistedId);
if (start < 0) start = 0;
const clamped = parentSegments.slice(start);
if (clamped.length === 0) {
return [hoistedId];
}

const stack: string[] = [];
for (let i = 0; i < clamped.length; i++) {
stack.push(clamped.slice(0, i + 1).join("/"));
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The logic for building the navigation stack has two issues when hoisting is active:

  1. If the active note is not a descendant of the hoisted note (start < 0), it incorrectly falls back to showing the path from the real root (start = 0). This violates the hoisting constraint where only the hoisted subtree should be visible. It should instead return a stack containing only the hoistedId.
  2. The stack construction joins segments of the clamped array but omits the path prefix before the hoisted note (e.g., "root/"). This results in relative paths that may not be correctly resolved by noteContext.setNote or other services expecting absolute paths.

Preserving the prefix and correctly handling the non-descendant case ensures consistent navigation behavior.

Suggested change
let start = parentSegments.indexOf(hoistedId);
if (start < 0) start = 0;
const clamped = parentSegments.slice(start);
if (clamped.length === 0) {
return [hoistedId];
}
const stack: string[] = [];
for (let i = 0; i < clamped.length; i++) {
stack.push(clamped.slice(0, i + 1).join("/"));
}
let start = parentSegments.indexOf(hoistedId);
if (start < 0) {
return [hoistedId];
}
const clamped = parentSegments.slice(start);
const prefix = parentSegments.slice(0, start).join("/");
const stack: string[] = [];
for (let i = 0; i < clamped.length; i++) {
const path = clamped.slice(0, i + 1).join("/");
stack.push(prefix ? `${prefix}/${path}` : path);
}

Comment on lines 1717 to 1722
selectAllNotesInParentCommand({ node }: CommandListenerData<"selectAllNotesInParent">) {
if (!node) return;
for (const child of node.getParent().getChildren()) {
child.setSelected(true);
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

selectAllNotesInParentCommand should verify that node.getParent() returns a valid parent node before attempting to call getChildren(). While node is guaranteed to be present if the command is triggered, getParent() could theoretically return null in certain tree states or if called on a virtual root, leading to a runtime error.

Suggested change
selectAllNotesInParentCommand({ node }: CommandListenerData<"selectAllNotesInParent">) {
if (!node) return;
for (const child of node.getParent().getChildren()) {
child.setSelected(true);
}
}
selectAllNotesInParentCommand({ node }: CommandListenerData<"selectAllNotesInParent">) {
const parent = node?.getParent();
if (!parent) return;
for (const child of parent.getChildren()) {
child.setSelected(true);
}
}

@eliandoran
eliandoran marked this pull request as ready for review April 21, 2026 17:12
@dosubot dosubot Bot added the size:XXL This PR changes 1000+ lines, ignoring generated files. label Apr 21, 2026
@eliandoran
eliandoran merged commit 7556c38 into standalone Apr 21, 2026
14 of 15 checks passed
@eliandoran
eliandoran deleted the standalone_mobile_nav branch April 21, 2026 17:12
@eliandoran eliandoran modified the milestones: Standalone, v0.104.0 May 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL This PR changes 1000+ lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant