### Install Dependencies and Build Plugin (npm) Source: https://github.com/navidjalilian/obsidian-zettelkasten-graph/blob/main/README.md This snippet outlines the development installation process, which involves cloning the repository, installing project dependencies using npm, and building the plugin. These commands are essential for developers working on or testing the plugin. ```bash npm install npm run build ``` -------------------------------- ### Zettelkasten File Naming Examples (Markdown) Source: https://github.com/navidjalilian/obsidian-zettelkasten-graph/blob/main/README.md Illustrates the expected file naming conventions for the Zettelkasten Graph Visualizer plugin. These examples demonstrate how sequential and branching notes should be named to be correctly parsed and visualized by the plugin. ```markdown 21 Main Topic.md 21.1 Sequential Continuation.md 21.2 Next in Sequence.md 21a Branching Context.md 21b Additional Context.md 21.1a Sub-branch.md ``` -------------------------------- ### Manual Obsidian Plugin Installation (Bash) Source: https://context7.com/navidjalilian/obsidian-zettelkasten-graph/llms.txt This snippet shows how to manually install an Obsidian plugin by copying its files into the vault's plugins directory. It requires the compiled main.js, manifest.json, and styles.css files of the plugin. ```bash # Copy plugin files to vault mkdir -p /path/to/vault/.obsidian/plugins/zettelkasten-graph cp -r * /path/to/vault/.obsidian/plugins/zettelkasten-graph/ # The plugin requires these core files: # - main.js (compiled plugin) # - manifest.json # - styles.css ``` -------------------------------- ### Obsidian Plugin Settings Tab Configuration (TypeScript) Source: https://context7.com/navidjalilian/obsidian-zettelkasten-graph/llms.txt Provides an example of how to create a settings tab for an Obsidian plugin using TypeScript. It demonstrates adding a text input field for a folder path, which can be used to configure which folder the Zettelkasten graph should scan. It utilizes Obsidian's `PluginSettingTab` and `Setting` components. ```typescript // settings-tab.ts import { PluginSettingTab, Setting } from 'obsidian'; export class ZettelkastenGraphSettingTab extends PluginSettingTab { display(): void { const { containerEl } = this; containerEl.empty(); new Setting(containerEl) .setName('Zettelkasten Folder') .setDesc('Folder path to scan. Leave empty for entire vault.') .addText(text => text .setPlaceholder('folder/zettelkasten') .setValue(this.plugin.settings.folderPath) .onChange(async (value) => { this.plugin.settings.folderPath = value; await this.plugin.saveSettings(); })); } } // Usage: Navigate to Settings > Zettelkasten Graph // Enter "research/philosophy" to scan only that folder // Leave empty to scan all vault files ``` -------------------------------- ### Parse Zettelkasten Graph Structure (TypeScript) Source: https://context7.com/navidjalilian/obsidian-zettelkasten-graph/llms.txt Illustrates parsing a Zettelkasten vault to create a graph representation. It shows an example vault structure, how to instantiate a parser, and the resulting node structure, including properties like id, title, number, type, level, parentId, and children. ```typescript // Example vault structure: // - 21 Philosophy.md // - 21.1 Epistemology.md // - 21.2 Metaphysics.md // - 21a Historical Overview.md // - 21.1.1 Knowledge Theory.md // - 21.1a Epistemology Context.md const parser = new ZettelkastenParser(vault); const graph = await parser.parseZettelkasten(); // Node structure representation: const node21 = Array.from(graph.nodes.values()).find(n => n.number === '21'); /* { id: "21-Philosophy", title: "Philosophy", number: "21", type: "sequence", level: 1, parentId: undefined, children: ["21.1-Epistemology", "21.2-Metaphysics", "21a-Historical Overview"], file: TFile } */ const node21_1 = Array.from(graph.nodes.values()).find(n => n.number === '21.1'); /* { id: "21.1-Epistemology", title: "Epistemology", number: "21.1", type: "sequence", level: 2, parentId: "21-Philosophy", children: ["21.1.1-Knowledge Theory", "21.1a-Epistemology Context"], file: TFile } */ ``` -------------------------------- ### Initialize Graph Renderer and Callbacks (TypeScript) Source: https://context7.com/navidjalilian/obsidian-zettelkasten-graph/llms.txt Demonstrates initializing the graph renderer with callbacks for note creation and deletion. It shows how to set up the renderer with a container, workspace, and custom logic for handling sequential notes, branch notes, and note deletions, before rendering the Zettelkasten graph. ```typescript import { GraphRenderer } from './graph-renderer'; import { ZettelkastenParser, ZettelGraph } from './zettelkasten-parser'; const container = document.querySelector('.zettelkasten-graph-container'); const workspace = app.workspace; // Create renderer with note creation callbacks const noteCreationCallbacks = { onCreateSequential: async (node) => { console.log(`Creating sequential note after ${node.number}`); // Implementation in note-creator.ts }, onCreateBranch: async (node) => { console.log(`Creating branch note from ${node.number}`); }, onDeleteNote: async (node) => { console.log(`Deleting note ${node.number}`); } }; const renderer = new GraphRenderer(container, workspace, noteCreationCallbacks); // Initialize node manipulation features renderer.initializeNodeManipulator(app.vault, parser); // Render the graph const graph = await parser.parseZettelkasten(); renderer.render(graph); // Graph will display: // - Blue circles (radius 20) for sequential notes // - Red circles (radius 15) for branch notes // - Solid blue lines for sequential connections // - Dashed red lines for branch connections // - Interactive zoom/pan with mouse // - Click nodes to open files // - Hover for tooltips with titles ``` -------------------------------- ### Obsidian Plugin Registration and Activation (TypeScript) Source: https://context7.com/navidjalilian/obsidian-zettelkasten-graph/llms.txt Demonstrates the core structure of an Obsidian plugin in TypeScript. It includes registering a custom view type, adding a ribbon icon for quick access, and registering a command for the Command Palette to activate the custom view. Dependencies include Obsidian API modules like Plugin and WorkspaceLeaf. ```typescript import { Plugin, WorkspaceLeaf } from 'obsidian'; import { ZettelkastenGraphView, ZETTELKASTEN_GRAPH_VIEW_TYPE } from './graph-view'; export default class ZettelkastenGraphPlugin extends Plugin { settings: ZettelkastenGraphSettings; async onload() { await this.loadSettings(); // Register custom view type this.registerView( ZETTELKASTEN_GRAPH_VIEW_TYPE, (leaf) => new ZettelkastenGraphView(leaf, this) ); // Add ribbon icon for quick access this.addRibbonIcon('git-fork', 'Open Zettelkasten Graph', () => { this.activateView(); }); // Register command for Command Palette this.addCommand({ id: 'open-zettelkasten-graph', name: 'Open Zettelkasten Graph', callback: () => this.activateView() }); } async activateView() { const { workspace } = this.app; let leaf = workspace.getLeavesOfType(ZETTELKASTEN_GRAPH_VIEW_TYPE)[0]; if (!leaf) { leaf = workspace.getRightLeaf(false); await leaf?.setViewState({ type: ZETTELKASTEN_GRAPH_VIEW_TYPE, active: true }); } workspace.revealLeaf(leaf); } } ``` -------------------------------- ### TypeScript: Zettelkasten Graph View Lifecycle Source: https://context7.com/navidjalilian/obsidian-zettelkasten-graph/llms.txt Manages the lifecycle of the Zettelkasten graph view, including opening and closing. It sets up the view's container, creates UI controls like folder input and refresh buttons, initializes the graph renderer, and handles cleanup on view closure. Dependencies include Obsidian API (ItemView, Setting, App) and custom GraphRenderer. ```typescript export class ZettelkastenGraphView extends ItemView { getViewType(): string { return ZETTELKASTEN_GRAPH_VIEW_TYPE; } async onOpen() { const container = this.containerEl.children[1]; container.empty(); container.addClass("zettelkasten-graph-view"); // Create controls const controlsDiv = container.createDiv("zettelkasten-controls"); // Folder input new Setting(controlsDiv) .setName("Zettelkasten Folder") .addText(text => text .setPlaceholder("folder/path") .setValue(this.plugin.settings.folderPath) .onChange(async (value) => { this.plugin.settings.folderPath = value; await this.plugin.saveSettings(); })) .addButton(button => button .setButtonText("Refresh Graph") .onClick(() => this.refreshGraph())); // Graph container const graphContainer = container.createDiv("zettelkasten-graph-container"); // Initialize renderer this.renderer = new GraphRenderer(graphContainer, this.app.workspace, { onCreateSequential: this.createSequentialNote.bind(this), onCreateBranch: this.createBranchNote.bind(this), onDeleteNote: this.deleteNote.bind(this) }); this.renderer.initializeNodeManipulator(this.app.vault, this.parser); // Initial load await this.refreshGraph(); } async onClose() { if (this.renderer) { this.renderer.destroy(); // Cleanup: stop simulation, remove event listeners } } } ``` -------------------------------- ### Create Sequential Zettelkasten Notes in TypeScript Source: https://context7.com/navidjalilian/obsidian-zettelkasten-graph/llms.txt Automates the creation of the next sequential note in a Zettelkasten. It parses the current graph, identifies the current note, and generates a new note with an incremented number (e.g., 21.1 to 21.2). Handles cases where the next sequential note already exists by generating subsequent numbers (e.g., 21.3). Requires `NoteCreator` and `ZettelkastenParser` classes. ```typescript import { NoteCreator } from './note-creator'; import { ZettelkastenParser } from './zettelkasten-parser'; const vault = app.vault; const parser = new ZettelkastenParser(vault); const noteCreator = new NoteCreator(vault, parser); // Parse current graph const graph = await parser.parseZettelkasten(); const currentNode = Array.from(graph.nodes.values()) .find(n => n.number === '21.1'); // Create next sequential note (21.1 → 21.2) const newFile = await noteCreator.createSequentialNote( currentNode, graph, 'research/notes' // Optional folder path ); // Generated file: "research/notes/21.2 - New Note.md" // Content: // # 21.2 // // Sequential note created on 2025-10-11 // // ## Content // // // // --- // // *This note was automatically created as part of your Zettelkasten system.* console.log(`Created: ${newFile.basename}`); // If 21.2 already exists, algorithm generates 21.3 // Pattern: currentNumber → nextNumber // - 4 → 5 // - 4.1 → 4.2 // - 4.1.2 → 4.1.3 ``` -------------------------------- ### Configure D3.js Force Simulation (TypeScript) Source: https://context7.com/navidjalilian/obsidian-zettelkasten-graph/llms.txt Details the configuration of the D3.js force simulation used for graph visualization. It includes settings for link force, charge repulsion, centering, and collision detection. It also demonstrates how to preserve and restore node positions across renders, allowing for a more stable user experience. ```typescript // From graph-renderer.ts - configures physics simulation const simulation = d3.forceSimulation(nodes) .force("link", d3.forceLink(links) .id(d => d.id) .distance(100) // Distance between connected nodes .strength(0.8)) // Link rigidity (0-1) .force("charge", d3.forceManyBody() .strength(-300)) // Node repulsion (negative = repel) .force("center", d3.forceCenter(width / 2, height / 2)) .force("collision", d3.forceCollide() .radius(30)); // Prevent node overlap // Node position preservation across refreshes private nodePositions: Map = new Map(); // During render, restore previous positions: const savedPosition = this.nodePositions.get(zettel.id); if (savedPosition) { node.x = savedPosition.x; node.y = savedPosition.y; node.fx = savedPosition.x; // Fix initially node.fy = savedPosition.y; } // After 1 second, release fixed positions for natural movement setTimeout(() => { nodes.forEach(d => { d.fx = null; d.fy = null; }); }, 1000); ``` -------------------------------- ### AnimationSystem Methods Source: https://github.com/navidjalilian/obsidian-zettelkasten-graph/blob/main/INTERACTIVE_FEATURES.md Provides methods for animating node movements, text changes, creating pulse effects, and running batch animations. ```APIDOC ## AnimationSystem Methods ### Description Methods for animating node movements, text changes, creating pulse effects, and running animations in batches. ### Animate Node Movement - **Method**: `animateNodeMovement` - **Description**: Animates the movement of selected nodes to a specified position. - **Parameters**: - `selection` (Object) - The selection of nodes to animate. - `position` (Object) - The target position for the nodes. - `config` (Object) - Configuration options for the animation. - **Returns**: `Promise` ### Animate Text Change - **Method**: `animateTextChange` - **Description**: Animates changes to the text of selected nodes. - **Parameters**: - `selection` (Object) - The selection of nodes to animate. - `newText` (string) - The new text content. - `config` (Object) - Configuration options for the animation. - **Returns**: `Promise` ### Create Pulse Animation - **Method**: `createPulseAnimation` - **Description**: Creates a pulsing animation effect for selected nodes. - **Parameters**: - `selection` (Object) - The selection of nodes to apply the effect to. - `config` (Object) - Configuration options for the animation. ### Run Animation Batch - **Method**: `runAnimationBatch` - **Description**: Executes a batch of animations sequentially. - **Parameters**: - `animations` (Array) - An array of animation objects to run. - **Returns**: `Promise` ``` -------------------------------- ### Zettelkasten Note Parsing with Obsidian API (TypeScript) Source: https://context7.com/navidjalilian/obsidian-zettelkasten-graph/llms.txt This TypeScript snippet demonstrates how to parse Zettelkasten notes within an Obsidian vault using a `ZettelkastenParser`. It shows how to initialize the parser with the vault instance and parse either the entire vault or a specific folder. The parsed graph structure, including nodes, roots, children, and parents, can then be examined. ```typescript import { Vault } from 'obsidian'; import { ZettelkastenParser, ZettelGraph } from './zettelkasten-parser'; const vault = app.vault; const parser = new ZettelkastenParser(vault); // Parse entire vault const graph: ZettelGraph = await parser.parseZettelkasten(); // Parse specific folder only const filteredGraph: ZettelGraph = await parser.parseZettelkasten('research/notes'); // Examine parsed structure console.log(`Found ${graph.nodes.size} notes`); console.log(`Root nodes: ${graph.roots.length}`); // Access individual nodes for (const [nodeId, node] of graph.nodes) { console.log(`${node.number} - ${node.title} (${node.type})`); console.log(` Children: ${node.children.length}`); console.log(` Parent: ${node.parentId || 'none'}`); } // Example output: // Found 15 notes // Root nodes: 3 // 21 - Introduction to Philosophy (sequence) // Children: 3 // Parent: none // 21.1 - Epistemology Basics (sequence) // Children: 2 // Parent: 21-Introduction to Philosophy // 21a - Historical Context (branch) // Children: 0 // Parent: 21-Introduction to Philosophy ``` -------------------------------- ### AnimationSystem Methods for Visual Effects (TypeScript) Source: https://github.com/navidjalilian/obsidian-zettelkasten-graph/blob/main/INTERACTIVE_FEATURES.md Offers methods to animate node movements, text changes, create pulsing effects, and run animations in batches. These methods are crucial for providing a dynamic and responsive user experience. Configuration objects can be passed to customize the animations. ```typescript // Animate node movement async animateNodeMovement(selection, position, config) // Animate text changes async animateTextChange(selection, newText, config) // Create pulsing effects createPulseAnimation(selection, config) // Batch animations async runAnimationBatch(animations) ``` -------------------------------- ### Recognize Zettelkasten Filename Patterns (TypeScript) Source: https://context7.com/navidjalilian/obsidian-zettelkasten-graph/llms.txt Defines regular expressions to recognize sequential and branch numbering patterns in Zettelkasten note filenames. These patterns are crucial for parsing and organizing notes. ```typescript // The parser recognizes these filename patterns: // SEQUENTIAL NOTES (numbered progressions) // "21 Main Topic.md" → { number: "21", type: "sequence" } // "21.1 Subtopic.md" → { number: "21.1", type: "sequence" } // "21.1.2 Deep Dive.md" → { number: "21.1.2", type: "sequence" } // "4.7.3 Nested Idea.md" → { number: "4.7.3", type: "sequence" } // BRANCH NOTES (lettered additions) // "21a Context.md" → { number: "21a", type: "branch" } // "21b More Context.md" → { number: "21b", type: "branch" } // "21.1a Sub-branch.md" → { number: "21.1a", type: "branch" } // "4.7.3c Example.md" → { number: "4.7.3c", type: "branch" } // Regex patterns used: const SEQUENCE_PATTERN = /(\d+(?:\.\d+)*)/; const BRANCH_PATTERN = /(\d+(?:\.\d+)*[a-z]+)/; const FULL_PATTERN = /(\d+(?:\.\d+)*(?:[a-z]+)?)/g; ``` -------------------------------- ### Animation System Implementation (TypeScript) Source: https://github.com/navidjalilian/obsidian-zettelkasten-graph/blob/main/INTERACTIVE_FEATURES.md This snippet describes the AnimationSystem component, responsible for creating smooth visual transitions and feedback effects. It manages animation timing, easing, and supports both batch and sequential animations for interactive elements. ```typescript class AnimationSystem { // Provides smooth transition animations // Handles visual feedback effects // Manages animation timing and easing // Supports batch and sequential animations } ``` -------------------------------- ### Interactive Note Creation Buttons on SVG Graph Hover Source: https://context7.com/navidjalilian/obsidian-zettelkasten-graph/llms.txt Implements interactive buttons on an SVG graph that appear on node hover, allowing users to create sequential or branch notes. It appends a group of buttons to each node, with distinct visuals and click handlers for each note creation type. The workflow involves hovering, clicking a button, and the graph refreshing with the new note. ```typescript // SVG hover buttons appear when mouse enters node // Right button (blue →): Create sequential note // Left button (red ⤴): Create branch note // From graph-renderer.ts addSVGHoverButtons(): const buttonGroup = nodeSelection.append("g") .attr("class", "hover-buttons") .style("opacity", "0"); // Sequential button (right side, +30px offset) const sequentialButton = buttonGroup.append("g") .attr("transform", "translate(30, 0)"); sequentialButton.append("circle") .attr("r", 12) .attr("fill", "var(--color-blue)"); sequentialButton.on("click", (event, d) => { event.stopPropagation(); noteCreationCallbacks.onCreateSequential(d.zettel); }); // Branch button (left side, -30px offset) const branchButton = buttonGroup.append("g") .attr("transform", "translate(-30, 0)"); branchButton.on("click", (event, d) => { event.stopPropagation(); noteCreationCallbacks.onCreateBranch(d.zettel); }); // User workflow: // 1. Hover over node "21.1" // 2. Click blue → button // 3. Note "21.2" created and graph refreshes // 4. New file opens in editor automatically ``` -------------------------------- ### Visual Feedback for Manipulation in TypeScript and SVG Source: https://context7.com/navidjalilian/obsidian-zettelkasten-graph/llms.txt Provides visual cues to the user during drag-and-drop operations, including a pulsing drop zone indicator and styling for valid/invalid targets. It also implements a feedback overlay system to display success or error messages after operations. Relies on SVG manipulation and DOM element creation. ```typescript // Drop zone indicator (animated pulsing circle) const dropZoneIndicator = svg.select('g') .append('circle') .attr('class', 'drop-zone-indicator') .attr('cx', targetNode.x) .attr('cy', targetNode.y) .attr('r', 35) .style('opacity', 1); // Invalid drop styling (red pulsing) dropZoneIndicator .classed('invalid', true) .style('stroke', 'var(--text-error)'); // Drag state classes applied to nodes: // .dragging - Node being dragged (opacity 0.8, shadow) // .drag-target - Valid drop target (accent color, bright) // .invalid-drop-target - Invalid target (red, dimmed) // Feedback overlay for operation results private showFeedback(message: string, type: 'success' | 'error') { const overlay = document.createElement('div'); overlay.className = `manipulation-feedback ${type}`; overlay.textContent = message; document.body.appendChild(overlay); setTimeout(() => overlay.remove(), 3000); } // Usage: showFeedback('Moved 21.2 to 21.1.1', 'success'); showFeedback('Cannot move parent to descendant', 'error'); ``` -------------------------------- ### Create Branching Zettelkasten Notes in TypeScript Source: https://context7.com/navidjalilian/obsidian-zettelkasten-graph/llms.txt Enables the creation of branching notes from a parent note in a Zettelkasten. It generates a new note with an appended letter (e.g., 21 to 21a). If the lettered note already exists, it increments to the next letter (e.g., 21b, 21c). Includes logic for generating the next available branch letter and handling deep branching. ```typescript // Create branch note (21 → 21a) const currentNode = Array.from(graph.nodes.values()) .find(n => n.number === '21'); const branchFile = await noteCreator.createBranchNote( currentNode, graph, 'research/notes' ); // Generated file: "research/notes/21a - New Note.md" // If 21a exists, generates 21b, then 21c, etc. // Multiple branches from same parent: // 21 (parent) // ├── 21a (first branch) // ├── 21b (second branch) // └── 21c (third branch) // Branch letter generation logic: const existingNumbers = ['21', '21a', '21b']; const nextBranch = parser.generateNextBranchLetter('21', existingNumbers); console.log(nextBranch); // Output: "21c" // Deep branch example: const deepNode = Array.from(graph.nodes.values()) .find(n => n.number === '4.7.3'); const deepBranch = await noteCreator.createBranchNote( deepNode, graph, 'notes' ); // Generates: 4.7.3a, then 4.7.3b, etc. ``` -------------------------------- ### CSS for Drag States and Visual Indicators Source: https://github.com/navidjalilian/obsidian-zettelkasten-graph/blob/main/INTERACTIVE_FEATURES.md This snippet provides CSS class definitions used to style nodes during drag operations and to display visual feedback indicators. It covers states like dragging, valid/invalid drop targets, and visual cues for relationship previews. ```css .node.dragging { /* Applied to node being dragged */ } .node.drag-target { /* Applied to valid drop targets */ } .node.invalid-drop-target { /* Applied to invalid drop targets */ } .drop-zone-indicator { /* Pulsing circle around drop targets */ } .relationship-preview { /* Dashed line showing new connections */ } .manipulation-feedback { /* Success/error message overlay */ } ``` -------------------------------- ### Refresh Zettelkasten Graph: Full vs Stable Refresh (TypeScript) Source: https://context7.com/navidjalilian/obsidian-zettelkasten-graph/llms.txt This snippet demonstrates two methods for refreshing the Zettelkasten graph: 'refreshGraph' for a full re-initialization (useful for view resizing) and 'refreshGraphStable' for updating graph data without re-initialization (suitable for note creation/deletion). It also includes command registration to trigger the refresh. ```typescript async refreshGraph() { const graphContainer = this.containerEl.querySelector('.zettelkasten-graph-container'); // Show loading state graphContainer.empty(); graphContainer.createDiv('zettelkasten-loading-state') .textContent = 'Loading graph...'; // Parse vault const folderPath = this.plugin.settings.folderPath.trim() || undefined; this.currentGraph = await this.parser.parseZettelkasten(folderPath); // Re-initialize renderer graphContainer.empty(); this.renderer = new GraphRenderer(graphContainer, this.app.workspace, callbacks); this.renderer.initializeNodeManipulator(this.app.vault, this.parser); if (this.currentGraph.nodes.size > 0) { this.renderer.render(this.currentGraph); } else { graphContainer.createDiv('zettelkasten-empty-state') .textContent = 'No Zettelkasten notes found. Use patterns: 21, 21.1, 21a'; } this.updateStats(); } // Stable refresh: Updates graph data without re-initializing (use after note creation/deletion) private async refreshGraphStable() { const folderPath = this.plugin.settings.folderPath.trim() || undefined; this.currentGraph = await this.parser.parseZettelkasten(folderPath); if (this.renderer && this.currentGraph.nodes.size > 0) { this.renderer.render(this.currentGraph); // Preserves zoom, positions } this.updateStats(); } // Command registration for external refresh this.addCommand({ id: 'refresh-zettelkasten-graph', name: 'Refresh Zettelkasten Graph', callback: () => { const leaves = this.app.workspace.getLeavesOfType(ZETTELKASTEN_GRAPH_VIEW_TYPE); if (leaves.length > 0) { const view = leaves[0].view as ZettelkastenGraphView; view.refreshGraph(); } } }); ``` -------------------------------- ### Context Menu for Note Deletion in TypeScript Source: https://context7.com/navidjalilian/obsidian-zettelkasten-graph/llms.txt Implements a right-click context menu for graph nodes, offering an option to delete the associated note. The menu is dynamically created and positioned, and includes event listeners for user interaction, such as hiding the menu when clicking outside. It integrates with a `deleteNote` function and confirmation dialog. ```typescript // Context menu triggered on right-click node.on("contextmenu", (event, d) => { event.preventDefault(); showContextMenu(event, d); }); function showContextMenu(event: MouseEvent, node: GraphNode) { const contextMenu = document.createElement('div'); contextMenu.className = 'zettelkasten-context-menu'; contextMenu.style.left = `${event.pageX}px`; contextMenu.style.top = `${event.pageY}px`; const deleteOption = document.createElement('div'); deleteOption.className = 'zettelkasten-context-menu-item'; deleteOption.textContent = 'Delete Note'; deleteOption.addEventListener('click', async () => { hideContextMenu(); await deleteNote(node.zettel); }); contextMenu.appendChild(deleteOption); document.body.appendChild(contextMenu); // Hide on click outside const hideOnClick = (e: MouseEvent) => { if (!contextMenu.contains(e.target as Node)) { hideContextMenu(); document.removeEventListener('click', hideOnClick); } }; setTimeout(() => document.addEventListener('click', hideOnClick), 0); } // User interaction: // 1. Right-click node "21.3" // 2. Menu appears: "Delete Note" // 3. Click "Delete Note" // 4. Confirmation dialog appears ``` -------------------------------- ### Undo/Redo Functionality with Command History in TypeScript Source: https://context7.com/navidjalilian/obsidian-zettelkasten-graph/llms.txt Manages undo and redo operations for graph manipulations using a command history structure. It allows users to revert changes and reapply them, with functions to check availability, clear history, and handle keyboard shortcuts. The system tracks node moves and renumbering operations. ```typescript // Command history structure interface ManipulationCommand { type: 'move' | 'renumber'; nodeId: string; oldData: { number: string; parentId?: string }; newData: { number: string; parentId?: string }; timestamp: number; } // Undo last operation (Ctrl+Z or Cmd+Z) const undoSuccess = await manipulator.undo(graph, (updatedGraph) => { renderer.render(updatedGraph); }); // Redo operation (Ctrl+Y or Cmd+Y) const redoSuccess = await manipulator.redo(graph, (updatedGraph) => { renderer.render(updatedGraph); }); // Check availability if (manipulator.canUndo()) { console.log('Undo available'); // Enable undo button } if (manipulator.canRedo()) { console.log('Redo available'); // Enable redo button } // History management manipulator.clearHistory(); // Clear all undo/redo history // Example scenario: // 1. Move node 21.3 → becomes 21.1.1 // 2. Press Ctrl+Z → reverts to 21.3 // 3. Press Ctrl+Y → changes back to 21.1.1 // 4. Make new change → clears redo stack // UI buttons in graph-renderer.ts: private undoButton: HTMLButtonElement; private redoButton: HTMLButtonElement; // Keyboard shortcuts registered: document.addEventListener('keydown', (event) => { if (event.ctrlKey || event.metaKey) { if (event.key === 'z' && !event.shiftKey) { event.preventDefault(); this.handleUndo(); } else if (event.key === 'y' || (event.key === 'z' && event.shiftKey)) { event.preventDefault(); this.handleRedo(); } } }); ``` -------------------------------- ### NodeManipulator Methods Source: https://github.com/navidjalilian/obsidian-zettelkasten-graph/blob/main/INTERACTIVE_FEATURES.md Provides methods for manipulating graph nodes, including drag behavior, undo/redo operations, and checking operation availability. ```APIDOC ## NodeManipulator Methods ### Description Methods for creating enhanced drag behavior, executing undo/redo operations, and checking if undo/redo is possible. ### Create Enhanced Drag Behavior - **Method**: `createEnhancedDragBehavior` - **Description**: Creates an enhanced drag behavior for nodes. - **Parameters**: - `nodes` (Array) - The nodes to apply the behavior to. - `graph` (Object) - The graph object. - `onUpdate` (Function) - Callback function to be executed on update. ### Undo Operation - **Method**: `undo` - **Description**: Executes the undo operation. - **Parameters**: - `graph` (Object) - The graph object. - `onUpdate` (Function) - Callback function to be executed on update. - **Returns**: `Promise` - True if undo was successful, false otherwise. ### Redo Operation - **Method**: `redo` - **Description**: Executes the redo operation. - **Parameters**: - `graph` (Object) - The graph object. - `onUpdate` (Function) - Callback function to be executed on update. - **Returns**: `Promise` - True if redo was successful, false otherwise. ### Check Undo Availability - **Method**: `canUndo` - **Description**: Checks if the undo operation is available. - **Returns**: `boolean` - True if undo is available, false otherwise. ### Check Redo Availability - **Method**: `canRedo` - **Description**: Checks if the redo operation is available. - **Returns**: `boolean` - True if redo is available, false otherwise. ``` -------------------------------- ### TypeScript: Show Delete Confirmation Dialog Source: https://context7.com/navidjalilian/obsidian-zettelkasten-graph/llms.txt Provides a modal dialog to confirm the deletion of a Zettelkasten node. It handles user interactions, keyboard events (Escape key), and resolves a promise with a boolean indicating confirmation. Dependencies include DOM manipulation and ZettelNode type. ```typescript async function showDeleteConfirmation(node: ZettelNode): Promise { return new Promise((resolve) => { const modal = document.createElement('div'); modal.className = 'zettelkasten-delete-modal'; const dialog = document.createElement('div'); dialog.className = 'zettelkasten-delete-dialog'; const message = document.createElement('p'); message.textContent = `Are you sure you want to delete "${node.file.basename}"?`; const cancelButton = document.createElement('button'); cancelButton.className = 'zettelkasten-delete-button-cancel'; cancelButton.textContent = 'Cancel'; const deleteButton = document.createElement('button'); deleteButton.className = 'zettelkasten-delete-button-confirm'; deleteButton.textContent = 'Delete'; cancelButton.addEventListener('click', () => { document.body.removeChild(modal); resolve(false); }); deleteButton.addEventListener('click', () => { document.body.removeChild(modal); resolve(true); }); // ESC key cancels const handleEscape = (e: KeyboardEvent) => { if (e.key === 'Escape') { document.body.removeChild(modal); resolve(false); document.removeEventListener('keydown', handleEscape); } }; document.addEventListener('keydown', handleEscape); dialog.appendChild(message); dialog.appendChild(cancelButton); dialog.appendChild(deleteButton); modal.appendChild(dialog); document.body.appendChild(modal); }); } // Delete operation async function deleteNote(node: ZettelNode) { const confirmed = await showDeleteConfirmation(node); if (!confirmed) return; // Respects user trash settings (system trash or .trash folder) await app.fileManager.trashFile(node.file); new Notice(`Deleted note: ${node.file.basename}`); await refreshGraphStable(); // Update without full re-render } ``` -------------------------------- ### Drag-and-Drop Hierarchical Node Movement in TypeScript Source: https://context7.com/navidjalilian/obsidian-zettelkasten-graph/llms.txt Implements enhanced drag-and-drop behavior for nodes, allowing hierarchical restructuring of the graph. It includes drop zone detection, automatic renumbering, and validation to prevent circular dependencies or invalid moves. Dependencies include NodeManipulator, graph rendering, and node selection. ```typescript import { NodeManipulator } from './node-manipulator'; const manipulator = new NodeManipulator(vault, parser, svg); // Enhanced drag behavior with drop zone detection const dragBehavior = manipulator.createEnhancedDragBehavior( nodes, graph, (updatedGraph) => { // Callback when graph structure changes renderer.render(updatedGraph); } ); // Apply to node selection nodeSelection.call(dragBehavior); // Example drag operation: // 1. User drags node "21.2" near node "21.1" // 2. Drop zone appears (pulsing circle around target) // 3. On release: "21.2" becomes child of "21.1" → renumbered to "21.1.1" // 4. Graph updates with new hierarchy // 5. Command added to undo/redo history // Validation prevents: // - Moving parent to its own descendant (21 → 21.1) // - Moving node to itself // - Creating circular dependencies ``` -------------------------------- ### CSS for Animation Effects Source: https://github.com/navidjalilian/obsidian-zettelkasten-graph/blob/main/INTERACTIVE_FEATURES.md This snippet outlines CSS classes used to implement various animation effects within the interactive node manipulation system. It includes styles for smooth transitions, number changes, and other visual effects during user interactions. ```css .node.transitioning { /* Smooth position transitions */ } .node.renumbering { /* Number change animations */ } /* Various keyframe animations for visual effects */ ``` -------------------------------- ### Graph Renderer Enhancements (TypeScript) Source: https://github.com/navidjalilian/obsidian-zettelkasten-graph/blob/main/INTERACTIVE_FEATURES.md This snippet details the EnhancedGraphRenderer, which integrates the new manipulation system into the existing graph visualization. It includes adding action buttons for undo/redo and implementing keyboard shortcut support for a seamless user experience. ```typescript class EnhancedGraphRenderer { // Integrates manipulation system with existing renderer // Adds action buttons for undo/redo // Provides keyboard shortcut support // Manages visual state updates } ``` -------------------------------- ### Node Manipulation Logic (TypeScript) Source: https://github.com/navidjalilian/obsidian-zettelkasten-graph/blob/main/INTERACTIVE_FEATURES.md This snippet outlines the core logic for handling drag-and-drop operations, hierarchical validation, and executing node movement. It is responsible for managing the command history for undo/redo functionality within the Zettelkasten Graph Visualizer. ```typescript class NodeManipulator { // Handles drag and drop logic // Manages hierarchical validation // Executes node movement operations // Maintains command history for undo/redo } ``` -------------------------------- ### Display Zettelkasten Graph Statistics (TypeScript) Source: https://context7.com/navidjalilian/obsidian-zettelkasten-graph/llms.txt This TypeScript function, 'updateStats', is responsible for calculating and displaying various statistics about the Zettelkasten graph, such as the total number of notes, sequential notes, branch notes, and root notes. It dynamically updates a statistics display within the UI. ```typescript function updateStats() { if (!this.currentGraph) return; const controlsDiv = this.containerEl.querySelector('.zettelkasten-controls'); const existingStats = controlsDiv.querySelector('.zettelkasten-stats'); if (existingStats) existingStats.remove(); const statsDiv = controlsDiv.createDiv('zettelkasten-stats'); const totalNodes = this.currentGraph.nodes.size; const sequenceNodes = Array.from(this.currentGraph.nodes.values()) .filter(n => n.type === 'sequence').length; const branchNodes = Array.from(this.currentGraph.nodes.values()) .filter(n => n.type === 'branch').length; const rootNodes = this.currentGraph.roots.length; statsDiv.createDiv().createEl('strong', { text: 'Graph Statistics:' }); statsDiv.createDiv({ text: `Total Notes: ${totalNodes}` }); statsDiv.createDiv({ text: `Sequential Notes: ${sequenceNodes}` }); statsDiv.createDiv({ text: `Branch Notes: ${branchNodes}` }); statsDiv.createDiv({ text: `Root Notes: ${rootNodes}` }); } ``` -------------------------------- ### SVG Zoom and Pan Interactions with D3.js Source: https://context7.com/navidjalilian/obsidian-zettelkasten-graph/llms.txt Implements zoom and pan functionality for an SVG element using D3.js. It includes scale limits, transform preservation, and programmatic controls for zooming in, out, resetting, and focusing on specific nodes. This behavior is attached to the main SVG element. ```typescript // SVG zoom behavior with scale limits and transform preservation const zoom = d3.zoom() .scaleExtent([0.1, 4]) // Min 10%, Max 400% .on("zoom", (event) => { this.currentTransform = event.transform; this.svg.select("g").attr("transform", event.transform); }); this.svg.call(zoom); // Programmatic zoom controls: // Zoom in svg.transition().duration(750).call( zoom.scaleBy, 1.5 ); // Zoom out svg.transition().duration(750).call( zoom.scaleBy, 0.67 ); // Reset zoom svg.transition().duration(750).call( zoom.transform, d3.zoomIdentity ); // Zoom to specific node const node = graph.nodes.get("21.1-Epistemology"); if (node && node.x && node.y) { const transform = d3.zoomIdentity .translate(width / 2, height / 2) .scale(2) .translate(-node.x, -node.y); svg.transition().duration(750).call( zoom.transform, transform ); } ``` -------------------------------- ### NodeManipulator Methods for Graph Interaction (TypeScript) Source: https://github.com/navidjalilian/obsidian-zettelkasten-graph/blob/main/INTERACTIVE_FEATURES.md Provides methods for enhancing drag behavior, performing undo/redo operations, and checking their availability. These functions interact with the graph structure and update the visualization. Ensure the graph object and an update callback are provided for operations that modify the graph. ```typescript // Create enhanced drag behavior createEnhancedDragBehavior(nodes, graph, onUpdate) // Execute undo operation async undo(graph, onUpdate): Promise // Execute redo operation async redo(graph, onUpdate): Promise // Check if operations are available canUndo(): boolean canRedo(): boolean ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.