feat: add Obsidian-style graph visualization
Implement force-directed graph view for visualizing note connections:
- Add graph data model parsing [[wiki-style links]]
- Implement Fruchterman-Reingold layout algorithm
- Create character-based canvas renderer with highlights
- Add interactive filtering by tag/folder
- Support navigation (h/j/k/l), zoom (+/-), and node selection
- New commands: :IdeaGraph, :IdeaGraphFilter
New files:
- lua/ideaDrop/ui/graph/{init,types,data,layout,renderer}.lua
Updated documentation in README.md, CHANGELOG.md, and llms.txt
This commit is contained in:
@@ -4,21 +4,34 @@
|
||||
---@field options IdeaDropOptions
|
||||
---@field setup fun(user_opts: IdeaDropOptions|nil): nil
|
||||
|
||||
---@class GraphOptions
|
||||
---@field animate boolean Whether to animate layout (default: false)
|
||||
---@field show_orphans boolean Whether to show orphan nodes (default: true)
|
||||
---@field show_labels boolean Whether to show node labels by default (default: true)
|
||||
---@field node_colors table<string, string>|nil Custom colors for folders/tags
|
||||
|
||||
---@class IdeaDropOptions
|
||||
---@field idea_dir string Directory where idea files will be stored
|
||||
---@field graph GraphOptions|nil Graph visualization options
|
||||
|
||||
local M = {}
|
||||
|
||||
---Default configuration options
|
||||
M.options = {
|
||||
idea_dir = vim.fn.stdpath("data") .. "/ideaDrop" -- default path
|
||||
idea_dir = vim.fn.stdpath("data") .. "/ideaDrop", -- default path
|
||||
graph = {
|
||||
animate = false, -- Set to true for animated layout
|
||||
show_orphans = true, -- Show nodes with no connections
|
||||
show_labels = true, -- Show node labels by default
|
||||
node_colors = nil, -- Custom node colors by folder/tag
|
||||
},
|
||||
}
|
||||
|
||||
---Setup function to merge user options with defaults
|
||||
---@param user_opts IdeaDropOptions|nil User configuration options
|
||||
---@return nil
|
||||
function M.setup(user_opts)
|
||||
M.options = vim.tbl_deep_extend("force", M.options, user_opts or {})
|
||||
M.options = vim.tbl_deep_extend("force", M.options, user_opts or {})
|
||||
end
|
||||
|
||||
return M
|
||||
|
||||
@@ -5,6 +5,7 @@ local config = require("ideaDrop.core.config")
|
||||
-- UI modules
|
||||
local sidebar = require("ideaDrop.ui.sidebar")
|
||||
local tree = require("ideaDrop.ui.tree")
|
||||
local graph = require("ideaDrop.ui.graph")
|
||||
|
||||
-- Feature modules
|
||||
local list = require("ideaDrop.features.list")
|
||||
@@ -239,6 +240,75 @@ function M.setup(user_opts)
|
||||
desc = "Search only in idea titles",
|
||||
})
|
||||
|
||||
-- Graph visualization commands
|
||||
vim.api.nvim_create_user_command("IdeaGraph", function(opts)
|
||||
local arg = opts.args
|
||||
|
||||
if arg == "close" then
|
||||
graph.close()
|
||||
elseif arg == "refresh" then
|
||||
graph.refresh()
|
||||
elseif arg == "animate" then
|
||||
graph.open({ animate = true })
|
||||
else
|
||||
graph.open()
|
||||
end
|
||||
end, {
|
||||
nargs = "?",
|
||||
complete = function()
|
||||
return { "close", "refresh", "animate" }
|
||||
end,
|
||||
desc = "Open Obsidian-style graph visualization of notes and links",
|
||||
})
|
||||
|
||||
vim.api.nvim_create_user_command("IdeaGraphFilter", function(opts)
|
||||
local args = vim.split(opts.args, " ", { trimempty = true })
|
||||
|
||||
if #args < 2 then
|
||||
vim.notify("Usage: :IdeaGraphFilter <tag|folder> <value>", vim.log.levels.ERROR)
|
||||
return
|
||||
end
|
||||
|
||||
local filter_type = args[1]
|
||||
local filter_value = args[2]
|
||||
|
||||
if filter_type ~= "tag" and filter_type ~= "folder" then
|
||||
vim.notify("Filter type must be 'tag' or 'folder'", vim.log.levels.ERROR)
|
||||
return
|
||||
end
|
||||
|
||||
-- If graph is open, apply filter
|
||||
if graph.is_open() then
|
||||
local graph_data = graph.get_graph()
|
||||
if graph_data then
|
||||
local data_module = require("ideaDrop.ui.graph.data")
|
||||
data_module.apply_filter(graph_data, filter_type, filter_value)
|
||||
graph.refresh()
|
||||
end
|
||||
else
|
||||
-- Open graph with filter
|
||||
graph.open()
|
||||
vim.defer_fn(function()
|
||||
local graph_data = graph.get_graph()
|
||||
if graph_data then
|
||||
local data_module = require("ideaDrop.ui.graph.data")
|
||||
data_module.apply_filter(graph_data, filter_type, filter_value)
|
||||
graph.refresh()
|
||||
end
|
||||
end, 100)
|
||||
end
|
||||
end, {
|
||||
nargs = "+",
|
||||
complete = function(_, cmd_line, _)
|
||||
local args = vim.split(cmd_line, " ", { trimempty = true })
|
||||
if #args <= 2 then
|
||||
return { "tag", "folder" }
|
||||
end
|
||||
return {}
|
||||
end,
|
||||
desc = "Filter graph by tag or folder",
|
||||
})
|
||||
|
||||
-- Set up keymaps
|
||||
keymaps.setup()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user