feat: parent notes (file bookmarks with children) + no default icon

- File bookmarks can now have children, rendered with a chevron
- Group bookmarks can have an associated filePath; clicking opens the file,
  clicking chevron toggles collapse
- 'Add as parent note' option in header context menu creates a group
  with the current file linked
- 'Add child note' option in group context menu creates a sub-group
  with the current file linked (parent note pattern)
- Default icon changed from 'file'/'folder' to '' (no icon)
- Chevron click always toggles collapse regardless of type
This commit is contained in:
2026-06-10 20:19:01 -04:00
parent 4278507ae2
commit 3c4fc2d91c
3 changed files with 85 additions and 18 deletions
+6 -6
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -254,7 +254,7 @@ export default class WaypointPlugin extends Plugin {
// ── Bookmarks ──
addBookmark(filePath: string, label: string, type: 'file' | 'group' | 'separator' | 'spacer' = 'file', icon = 'file'): BookmarkItem {
addBookmark(filePath: string, label: string, type: 'file' | 'group' | 'separator' | 'spacer' = 'file', icon = ''): BookmarkItem {
const id = `bm-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`;
const item: BookmarkItem = {
id,
+78 -11
View File
@@ -316,12 +316,25 @@ export class WaypointView extends ItemView {
this.plugin.addBookmark(file.path, file.basename, 'file');
});
});
menu.addItem((item) => {
item
.setTitle('Add as parent note')
.setIcon('folder-plus')
.onClick(() => {
const file = this.app.workspace.getActiveFile();
if (!file) return;
const bm = this.plugin.addBookmark(file.path, file.basename, 'group', '');
bm.filePath = file.path;
this.plugin.saveWaypointData();
this.redraw();
});
});
menu.addItem((item) => {
item
.setTitle('New group')
.setIcon('folder-plus')
.onClick(() => {
this.plugin.addBookmark('', 'New Group', 'group', 'folder');
this.plugin.addBookmark('', 'New Group', 'group', '');
});
});
menu.addItem((item) => {
@@ -534,23 +547,30 @@ export class WaypointView extends ItemView {
if (isGroup) {
const chevron = rowEl.createDiv({ cls: 'waypoint-bm-chevron' });
setIcon(chevron, 'chevron-down');
chevron.addEventListener('click', (event: MouseEvent) => {
event.stopPropagation();
this.plugin.updateBookmark(item.id, { collapsed: !item.collapsed });
});
const iconEl = rowEl.createDiv({ cls: 'waypoint-bm-icon' });
setIcon(iconEl, item.icon || 'folder');
if (item.icon) setIcon(iconEl, item.icon);
const labelEl = rowEl.createDiv({ cls: 'waypoint-bm-label', text: item.label });
// Click group row → open linked file (if any), otherwise toggle collapse
rowEl.addEventListener('click', (event: MouseEvent) => {
// Mod+click opens group's linked file
if (item.filePath && Keymap.isModEvent(event)) {
if (item.filePath) {
const tfile = this.app.vault.getFileByPath(item.filePath);
if (tfile) this.app.workspace.getLeaf(false).openFile(tfile);
return;
if (tfile) {
const newLeaf = Keymap.isModEvent(event);
this.app.workspace.getLeaf(newLeaf as any).openFile(tfile);
return;
}
}
this.plugin.updateBookmark(item.id, { collapsed: !item.collapsed });
});
// Middle click on group header → open group's linked file in new tab
// Middle click on group → open in new tab
rowEl.addEventListener('mousedown', (event: MouseEvent) => {
if (event.button === 1 && item.filePath) {
event.preventDefault();
@@ -570,8 +590,22 @@ export class WaypointView extends ItemView {
// ── File: icon + label ──
} else {
// Chevron for file bookmarks that have children
if (item.children && item.children.length > 0) {
const chevron = rowEl.createDiv({ cls: 'waypoint-bm-chevron' });
setIcon(chevron, 'chevron-down');
chevron.addEventListener('click', (event: MouseEvent) => {
event.stopPropagation();
this.plugin.updateBookmark(item.id, { collapsed: !item.collapsed });
});
if (item.collapsed) {
rowEl.addClass('collapsed');
chevron.style.transform = 'rotate(-90deg)';
}
}
const iconEl = rowEl.createDiv({ cls: 'waypoint-bm-icon' });
setIcon(iconEl, item.icon || 'file');
if (item.icon) setIcon(iconEl, item.icon);
const labelEl = rowEl.createDiv({ cls: 'waypoint-bm-label', text: item.label });
setTooltip(rowEl, item.filePath);
@@ -599,6 +633,14 @@ export class WaypointView extends ItemView {
}
}
});
// Children container for file bookmarks with children
if (item.children && item.children.length > 0) {
const childrenContainer = container.createDiv({
cls: `waypoint-bookmark-children${item.collapsed ? ' collapsed' : ''}`,
});
this.renderBookmarkList(childrenContainer, item.children, depth + 1);
}
}
// ── Right-click context menu (both types) ──
@@ -687,7 +729,7 @@ export class WaypointView extends ItemView {
menu.addItem((i) =>
i
.setTitle('Add bookmark here')
.setIcon('plus')
.setIcon('file-plus')
.onClick(() => {
const file = this.app.workspace.getActiveFile();
if (!file) {
@@ -699,7 +741,32 @@ export class WaypointView extends ItemView {
type: 'file',
label: file.basename,
filePath: file.path,
icon: 'file',
icon: '',
children: [],
collapsed: false,
indent: item.indent + 1,
};
item.children.push(child);
this.plugin.saveWaypointData();
this.redraw();
}),
);
menu.addItem((i) =>
i
.setTitle('Add child note')
.setIcon('file-plus')
.onClick(() => {
const file = this.app.workspace.getActiveFile();
if (!file) {
new Notice('No active file');
return;
}
const child: BookmarkItem = {
id: `bm-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`,
type: 'group',
label: file.basename,
filePath: file.path,
icon: '',
children: [],
collapsed: false,
indent: item.indent + 1,
@@ -719,7 +786,7 @@ export class WaypointView extends ItemView {
type: 'group',
label: 'New Group',
filePath: '',
icon: 'folder',
icon: '',
children: [],
collapsed: false,
indent: item.indent + 1,