From 06cbff8c21490d26ce3f04862633129f5c71d868 Mon Sep 17 00:00:00 2001 From: celeste Date: Mon, 24 Jul 2023 21:36:05 -0400 Subject: [PATCH] small improvements --- main.ts | 29 +++++++++-------- manifest.json | 2 +- package.json | 2 +- styles.css | 18 +++++++++++ views.ts | 86 +++++++++++++++++++++++++++++++-------------------- 5 files changed, 86 insertions(+), 51 deletions(-) diff --git a/main.ts b/main.ts index 3b492ba..cd0abd4 100644 --- a/main.ts +++ b/main.ts @@ -6,7 +6,6 @@ import { MakePromptFromPassagesModal, } from './views'; import { - PROVIDERS, Provider, LoomSettings, SearchResultState, @@ -492,7 +491,7 @@ export default class LoomPlugin extends Plugin { checking, (state) => canDelete(state, state.current, checking), (state) => { - this.app.workspace.trigger("loom:delete", state.current); + this.app.workspace.trigger("loom:delete", [state.current]); } ), hotkeys: [{ modifiers: ["Alt"], key: "Backspace" }], @@ -828,25 +827,26 @@ export default class LoomPlugin extends Plugin { // switch to the merged node and delete the child node this.app.workspace.trigger("loom:switch-to", parentId); - this.app.workspace.trigger("loom:delete", id); + this.app.workspace.trigger("loom:delete", [id]); }) ) ); this.registerEvent( // @ts-expect-error - this.app.workspace.on("loom:delete", (id: string) => + this.app.workspace.on("loom:delete", (ids: string[]) => this.wftsar((file) => { const state = this.state[file.path]; - if (!canDelete(state, id, false)) return; - const parentId = state.nodes[id].parentId; - // remove the node from the hoist stack - this.state[file.path].hoisted = state.hoisted.filter((id_) => id_ !== id); + ids = ids.filter((id) => canDelete(state, id, false)); + if (ids.length === 0) return; - // add the node and its descendants to a list of nodes to delete + // remove the nodes from the hoist stack + this.state[file.path].hoisted = state.hoisted.filter((id) => !ids.includes(id)); - let deleted = [id]; + // add the nodes and their descendants to a list of nodes to delete + + let deleted = [...ids]; const addChildren = (id: string) => { const children = Object.entries(state.nodes) @@ -855,10 +855,11 @@ export default class LoomPlugin extends Plugin { deleted = deleted.concat(children); children.forEach(addChildren); } - addChildren(id); + ids.forEach(addChildren); // if the current node will be deleted, switch to its next sibling or its closest ancestor if (deleted.includes(state.current)) { + const parentId = state.nodes[state.current].parentId; const siblings = Object.entries(state.nodes) .filter(([, node]) => node.parentId === parentId) .map(([id]) => id); @@ -908,8 +909,7 @@ export default class LoomPlugin extends Plugin { const children = Object.entries(this.state[file.path].nodes) .filter(([, node]) => node.parentId === id) .map(([id]) => id); - for (const id of children) - this.app.workspace.trigger("loom:delete", id); + this.app.workspace.trigger("loom:delete", children); }) ) ); @@ -922,8 +922,7 @@ export default class LoomPlugin extends Plugin { const siblings = Object.entries(this.state[file.path].nodes) .filter(([id_, node]) => node.parentId === parentId && id_ !== id) .map(([id]) => id); - for (const id of siblings) - this.app.workspace.trigger("loom:delete", id); + this.app.workspace.trigger("loom:delete", siblings); }) ) ); diff --git a/manifest.json b/manifest.json index 70ae3ce..d9b8249 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "loom", "name": "Loom", - "version": "1.16.1", + "version": "1.16.2", "minAppVersion": "0.15.0", "description": "Loom in Obsidian", "author": "celeste", diff --git a/package.json b/package.json index 32de33d..1babcfc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "obsidian-loom", - "version": "1.16.1", + "version": "1.16.2", "description": "Loom in Obsidian", "main": "main.js", "scripts": { diff --git a/styles.css b/styles.css index bf1358f..876befd 100644 --- a/styles.css +++ b/styles.css @@ -249,8 +249,11 @@ body { margin: 0.2em 0; padding: 0.8em; white-space: pre-wrap; + + position: relative; } .loom__sibling:hover { + background-color: var(--nav-item-background-active); color: var(--nav-item-color-active); } .loom__sibling.is-active { @@ -263,6 +266,21 @@ body { margin-right: 0.3em; } +.loom__sibling-buttons { + display: none; + + background-color: rgba(16, 16, 16, 0.6); + border-radius: 0.3em; + padding: 0.2em; + + position: absolute; + right: 0.5em; + top: 0.5em; +} +.loom__sibling:hover .loom__sibling-buttons { + display: inline-flex !important; +} + .loom__sibling-separator { margin: 0; } diff --git a/views.ts b/views.ts index 8ec6da9..fe2c1d0 100644 --- a/views.ts +++ b/views.ts @@ -11,16 +11,15 @@ import { } from "@codemirror/view"; const dialog = require("electron").remote.dialog; -interface MenuContext { +interface NodeContext { app: App; - event: MouseEvent; state: NoteState; id: string; node: Node; deletable: boolean; } -const showNodeMenu = ({ app, event, state, id, node, deletable }: MenuContext) => { +const showNodeMenu = (event: MouseEvent, { app, state, id, node, deletable }: NodeContext) => { const menu = new Menu(); const menuItem = (name: string, icon: string, callback: () => void) => @@ -34,6 +33,8 @@ const showNodeMenu = ({ app, event, state, id, node, deletable }: MenuContext) = menuItem(name, icon, () => app.workspace.trigger(event)); const selfArgMenuItem = (name: string, icon: string, event: string) => menuItem(name, icon, () => app.workspace.trigger(event, id)); + const selfListArgMenuItem = (name: string, icon: string, event: string) => + menuItem(name, icon, () => app.workspace.trigger(event, [id])); if (state.hoisted[state.hoisted.length - 1] === id) zeroArgMenuItem("Unhoist", "arrow-down", "loom:unhoist"); @@ -60,12 +61,46 @@ const showNodeMenu = ({ app, event, state, id, node, deletable }: MenuContext) = if (deletable) { menu.addSeparator(); - selfArgMenuItem("Delete", "trash", "loom:delete"); + selfListArgMenuItem("Delete", "trash", "loom:delete"); } menu.showAtMouseEvent(event); } +const renderNodeButtons = ( + container: HTMLElement, + { app, state, id, node, deletable }: NodeContext +) => { + const button = (label: string, icon: string, callback: (event: MouseEvent) => void) => { + const button_ = container.createDiv({ + cls: "loom__node-button", + attr: { "aria-label": label }, + }); + setIcon(button_, icon); + button_.addEventListener("click", event => { event.stopPropagation(); callback(event); }); + }; + + button("Show menu", "menu", (event) => showNodeMenu(event, { app, state, id, node, deletable })); + + if (state.hoisted[state.hoisted.length - 1] === id) + button("Unhoist", "arrow-down", () => app.workspace.trigger("loom:unhoist")); + else button("Hoist", "arrow-up", () => app.workspace.trigger("loom:hoist", id)); + + if (node.bookmarked) + button( + "Remove bookmark", + "bookmark-minus", + () => app.workspace.trigger("loom:toggle-bookmark", id) + ); + else + button("Bookmark", "bookmark", () => + app.workspace.trigger("loom:toggle-bookmark", id) + ); + + if (deletable) + button("Delete", "trash", () => app.workspace.trigger("loom:delete", [id])); +}; + export class LoomView extends ItemView { getNoteState: () => NoteState | null; getSettings: () => LoomSettings; @@ -403,10 +438,12 @@ export class LoomView extends ItemView { const rootNodes = Object.entries(state.nodes) .filter(([, node]) => node.parentId === null) const deletable = rootNodes.length !== 1 || rootNodes[0][0] !== id; + + const nodeContext: NodeContext = { app: this.app, state, id, node, deletable }; nodeContainer.addEventListener("contextmenu", (event) => { event.preventDefault(); - showNodeMenu({ app: this.app, event, state, id, node, deletable }); + showNodeMenu(event, nodeContext); }); // add buttons on hover @@ -415,34 +452,7 @@ export class LoomView extends ItemView { cls: "loom__node-buttons" }); - const button = (label: string, icon: string, callback: (event: MouseEvent) => void) => { - const button_ = nodeButtonsContainer.createDiv({ - cls: "loom__node-button", - attr: { "aria-label": label }, - }); - setIcon(button_, icon); - button_.addEventListener("click", callback); - }; - - button("Show menu", "menu", (event) => showNodeMenu({ app: this.app, event, state, id, node, deletable })); - - if (state.hoisted[state.hoisted.length - 1] === id) - button("Unhoist", "arrow-down", () => this.app.workspace.trigger("loom:unhoist")); - else button("Hoist", "arrow-up", () => this.app.workspace.trigger("loom:hoist", id)); - - if (node.bookmarked) - button( - "Remove bookmark", - "bookmark-minus", - () => this.app.workspace.trigger("loom:toggle-bookmark", id) - ); - else - button("Bookmark", "bookmark", () => - this.app.workspace.trigger("loom:toggle-bookmark", id) - ); - - if (deletable) - button("Delete", "trash", () => this.app.workspace.trigger("loom:delete", id)); + renderNodeButtons(nodeButtonsContainer, nodeContext); // indicate if loom is currently generating children for this node @@ -554,9 +564,17 @@ export class LoomSiblingsView extends ItemView { const rootNodes = Object.entries(state.nodes) .filter(([, node]) => node.parentId === null) const deletable = rootNodes.length !== 1 || rootNodes[0][0] !== id; + + const nodeContext: NodeContext = { app: this.app, state, id, node, deletable }; + + const nodeButtonsContainer = nodeContainer.createDiv({ + cls: "loom__sibling-buttons" + }); + renderNodeButtons(nodeButtonsContainer, nodeContext); + nodeContainer.addEventListener("contextmenu", (event) => { event.preventDefault(); - showNodeMenu({ app: this.app, event, state, id, node, deletable }); + showNodeMenu(event, nodeContext); }); if (parseInt(i) !== siblings.length - 1)