refactor: simplify bookmark menus — visible ⋯ button, flat context menus

Header:
- Replace hidden right-click with visible ⋯ button (more-horizontal icon)
- Menu: Add current file / Add as parent note / New group / separator / Add spacer

File bookmark right-click (5 items, flat):
  Open in new tab / Rename / Change icon / Remove

Group/parent note right-click (7 items max, flat):
  Open in new tab (only if linked) / Rename / Change icon / Expand|Collapse /
  Add child bookmark / Add child note / New sub-group / Remove

Separator/Spacer right-click (1 item):
  Remove

Removed dead code: insertAdjacent, collectGroups, collectDescendantIds,
moveBookmarkToGroup — all moving is now drag-and-drop only
This commit is contained in:
2026-06-10 21:45:16 -04:00
parent a692dc9a34
commit 2e8ac7047f
4 changed files with 93 additions and 151 deletions
+9 -9
View File
File diff suppressed because one or more lines are too long
+60 -142
View File
@@ -303,8 +303,11 @@ export class WaypointView extends ItemView {
const headerEl = section.createDiv({ cls: 'waypoint-section-header' });
headerEl.setText('Waypoint Bookmarks');
// Right-click on header area → add bookmark / group
headerEl.addEventListener('contextmenu', (event: MouseEvent) => {
// ⋯ button — visible add menu
const moreBtn = headerEl.createEl('button', { cls: 'waypoint-header-more' });
setIcon(moreBtn, 'more-horizontal');
setTooltip(moreBtn, 'Add bookmark');
moreBtn.addEventListener('click', (event: MouseEvent) => {
const menu = new Menu();
menu.addItem((item) => {
item
@@ -337,6 +340,7 @@ export class WaypointView extends ItemView {
this.plugin.addBookmark('', 'New Group', 'group', '');
});
});
menu.addSeparator();
menu.addItem((item) => {
item
.setTitle('Add separator')
@@ -353,7 +357,6 @@ export class WaypointView extends ItemView {
this.plugin.addBookmark('', '', 'spacer');
});
});
menu.showAtPosition({ x: event.clientX, y: event.clientY });
});
@@ -655,6 +658,17 @@ export class WaypointView extends ItemView {
private showBookmarkContextMenu(event: MouseEvent, item: BookmarkItem): void {
const menu = new Menu();
if (item.type === 'separator' || item.type === 'spacer') {
menu.addItem((i) =>
i
.setTitle('Remove')
.setIcon('trash')
.onClick(() => this.plugin.removeBookmark(item.id)),
);
menu.showAtPosition({ x: event.clientX, y: event.clientY });
return;
}
if (item.type === 'file') {
menu.addItem((i) =>
i
@@ -662,80 +676,69 @@ export class WaypointView extends ItemView {
.setIcon('file-plus')
.onClick(() => {
const tfile = this.app.vault.getFileByPath(item.filePath);
if (tfile) {
this.app.workspace.getLeaf('tab').openFile(tfile);
}
if (tfile) this.app.workspace.getLeaf('tab').openFile(tfile);
}),
);
menu.addSeparator();
}
if (item.type !== 'separator' && item.type !== 'spacer') {
menu.addItem((i) =>
i
.setTitle('Rename')
.setIcon('pencil')
.onClick(() => this.promptRename(item)),
);
menu.addItem((i) =>
i
.setTitle('Change icon')
.setIcon('image')
.onClick(() => this.promptIcon(item)),
);
}
// ── Move to group options ──
const allGroups = this.collectGroups(this.plugin.waypointData.bookmarks);
const ownIds = this.collectDescendantIds(item);
const availableGroups = allGroups.filter(g => !ownIds.includes(g.id) && g.id !== item.id);
if ((availableGroups.length > 0 || item.type === 'file' || item.type === 'group') && item.type !== 'separator' && item.type !== 'spacer') {
menu.addSeparator();
if (availableGroups.length > 0) {
for (const group of availableGroups) {
menu.addItem((mi) =>
mi
.setTitle(` ${group.label}`)
.setIcon(group.icon || 'folder')
.onClick(() => {
this.moveBookmarkToGroup(item.id, group.id);
}),
);
}
}
// Add "Root" option for ungrouping
menu.addItem((mi) =>
mi
.setTitle(' (Root)')
.setIcon('layout')
.onClick(() => {
this.moveBookmarkToGroup(item.id, null);
}),
menu.addItem((i) =>
i
.setTitle('Remove')
.setIcon('trash')
.onClick(() => this.plugin.removeBookmark(item.id)),
);
}
} else if (item.type === 'group') {
if (item.filePath) {
menu.addItem((i) =>
i
.setTitle('Open in new tab')
.setIcon('file-plus')
.onClick(() => {
const tfile = this.app.vault.getFileByPath(item.filePath);
if (tfile) this.app.workspace.getLeaf('tab').openFile(tfile);
}),
);
menu.addSeparator();
}
if (item.type === 'group') {
menu.addItem((i) =>
i
.setTitle('Rename')
.setIcon('pencil')
.onClick(() => this.promptRename(item)),
);
menu.addItem((i) =>
i
.setTitle('Change icon')
.setIcon('image')
.onClick(() => this.promptIcon(item)),
);
menu.addItem((i) =>
i
.setTitle(item.collapsed ? 'Expand' : 'Collapse')
.setIcon(item.collapsed ? 'chevron-down' : 'chevron-up')
.onClick(() => {
this.plugin.updateBookmark(item.id, { collapsed: !item.collapsed });
}),
.onClick(() => this.plugin.updateBookmark(item.id, { collapsed: !item.collapsed })),
);
menu.addSeparator();
menu.addItem((i) =>
i
.setTitle('Add bookmark here')
.setTitle('Add child bookmark')
.setIcon('file-plus')
.onClick(() => {
const file = this.app.workspace.getActiveFile();
if (!file) {
new Notice('No active file');
return;
}
if (!file) { new Notice('No active file'); return; }
const child: BookmarkItem = {
id: `bm-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`,
type: 'file',
@@ -754,13 +757,10 @@ export class WaypointView extends ItemView {
menu.addItem((i) =>
i
.setTitle('Add child note')
.setIcon('file-plus')
.setIcon('folder-plus')
.onClick(() => {
const file = this.app.workspace.getActiveFile();
if (!file) {
new Notice('No active file');
return;
}
if (!file) { new Notice('No active file'); return; }
const child: BookmarkItem = {
id: `bm-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`,
type: 'group',
@@ -796,100 +796,18 @@ export class WaypointView extends ItemView {
this.redraw();
}),
);
menu.addSeparator();
menu.addItem((i) =>
i
.setTitle('Remove')
.setIcon('trash')
.onClick(() => this.plugin.removeBookmark(item.id)),
);
}
menu.addSeparator();
// ── Insert separator / spacer ──
menu.addItem((i) =>
i
.setTitle('Insert separator above')
.setIcon('separator-vertical')
.onClick(() => this.insertAdjacent(item.id, 'separator', 'above')),
);
menu.addItem((i) =>
i
.setTitle('Insert separator below')
.setIcon('separator-vertical')
.onClick(() => this.insertAdjacent(item.id, 'separator', 'below')),
);
menu.addItem((i) =>
i
.setTitle('Insert spacer above')
.setIcon('space')
.onClick(() => this.insertAdjacent(item.id, 'spacer', 'above')),
);
menu.addItem((i) =>
i
.setTitle('Insert spacer below')
.setIcon('space')
.onClick(() => this.insertAdjacent(item.id, 'spacer', 'below')),
);
menu.addSeparator();
menu.addItem((i) =>
i
.setTitle('Remove')
.setIcon('trash')
.onClick(() => {
this.plugin.removeBookmark(item.id);
}),
);
menu.showAtPosition({ x: event.clientX, y: event.clientY });
}
private insertAdjacent(targetId: string, type: 'separator' | 'spacer', position: 'above' | 'below'): void {
const newItem: BookmarkItem = {
id: `bm-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`,
type,
label: '',
filePath: '',
icon: '',
children: [],
collapsed: false,
indent: 0,
};
const insert = (items: BookmarkItem[]): boolean => {
const idx = items.findIndex(i => i.id === targetId);
if (idx >= 0) {
const insertIdx = position === 'above' ? idx : idx + 1;
items.splice(insertIdx, 0, newItem);
return true;
}
for (const item of items) {
if (item.children && insert(item.children)) return true;
}
return false;
};
insert(this.plugin.waypointData.bookmarks);
this.plugin.saveWaypointData();
this.redraw();
}
private collectGroups(items: BookmarkItem[]): BookmarkItem[] {
const groups: BookmarkItem[] = [];
for (const item of items) {
if (item.type === 'group') {
groups.push(item);
groups.push(...this.collectGroups(item.children));
}
}
return groups;
}
private collectDescendantIds(item: BookmarkItem): string[] {
const ids: string[] = [];
for (const child of item.children) {
ids.push(child.id);
ids.push(...this.collectDescendantIds(child));
}
return ids;
}
private showDropIndicator(el: HTMLElement, e: MouseEvent): void {
// Clear indicator from all siblings first
const parent = el.parentElement;
+22
View File
@@ -27,6 +27,28 @@
margin-bottom: 4px;
padding-bottom: 4px;
border-bottom: 1px solid var(--background-modifier-border);
display: flex;
align-items: center;
gap: 6px;
}
.waypoint-header-more {
margin-left: auto;
background: none;
border: none;
cursor: var(--cursor);
color: var(--text-faint);
padding: 2px 4px;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
transition: color 80ms, background 80ms;
}
.waypoint-header-more:hover {
color: var(--text-accent);
background-color: var(--background-modifier-active-hover);
}
/* ── Calendar ── */
+2
View File
@@ -3,6 +3,8 @@
- [ ] in the bookmarks, i want to have notes that are parents to other notes
- [ ] by default, the bookmark should have no icon
- [ ] the way to search for icons should be improved. on the lucide icon website, the search works by more than only the icons title. i want to be able to load all the icon (not by default though)
- [ ] add a right click menu on the recent files to make them bookmarks
- [ ] improve the drag and drop to have identations and groups
- [ ] calendar
- [ ] middle click on a date/week/etc. should open it in a new tab
- [ ] i want to improve the look of the calendar. more space, a bit more height