-- UI coordination module local M = {} local buffer_manager = require('notex.ui.buffer') local view = require('notex.ui.view') local editor = require('notex.ui.editor') local query_engine = require('notex.query') local utils = require('notex.utils') -- UI state local ui_state = { active_views = {}, default_options = { max_width = 120, max_height = 30, show_help = true, border = "rounded" } } -- Show query results function M.show_query_results(query_results, options) options = options or {} options = vim.tbl_deep_extend("force", ui_state.default_options, options) -- Validate query results if not query_results.success then M.show_error("Query Error", query_results.errors, options) return nil, query_results.errors end -- Create view local view_config = view.create_query_view(query_results, options) if not view_config then return nil, "Failed to create query view" end -- Store active view ui_state.active_views[view_config.buffer_id] = view_config -- Setup auto-cleanup M.setup_view_cleanup(view_config) utils.log("INFO", "Created query view", { buffer_id = view_config.buffer_id, document_count = #query_results.documents, view_type = options.view_type or "table" }) return view_config end -- Show error message function M.show_error(title, errors, options) options = options or {} local error_lines = {title, string.rep("=", #title), ""} if type(errors) == "string" then table.insert(error_lines, errors) elseif type(errors) == "table" then for _, error in ipairs(errors) do table.insert(error_lines, "• " .. error) end end table.insert(error_lines, "") table.insert(error_lines, "Press any key to close") local buffer = vim.api.nvim_create_buf(false, true) vim.api.nvim_buf_set_lines(buffer, 0, -1, false, error_lines) vim.api.nvim_buf_set_option(buffer, "filetype", "text") vim.api.nvim_buf_set_name(buffer, "notex://error") local window = vim.api.nvim_open_win(buffer, true, { relative = "editor", width = math.min(80, vim.api.nvim_get_option_value("columns", {})), height = math.min(20, vim.api.nvim_get_option_value("lines", {})), row = math.floor((vim.api.nvim_get_option_value("lines", {}) - 20) / 2), col = math.floor((vim.api.nvim_get_option_value("columns", {}) - 80) / 2), border = "rounded", style = "minimal", title = " Error " }) -- Close on any key vim.api.nvim_create_autocmd("CursorMoved,WinLeave", { buffer = buffer, once = true, callback = function() vim.api.nvim_win_close(window, true) end }) return { buffer_id = buffer, window_id = window, type = "error" } end -- Show document details function M.show_document_details(document_id, options) options = options or {} -- Get document details local indexer = require('notex.index') local doc_details, err = indexer.get_document_details(document_id) if not doc_details then M.show_error("Document Error", {err or "Failed to get document details"}) return nil end -- Create detail view local buffer = vim.api.nvim_create_buf(false, true) vim.api.nvim_buf_set_option(buffer, "filetype", "yaml") vim.api.nvim_buf_set_name(buffer, "notex://document-details") -- Generate detail content local lines = M.generate_document_details(doc_details) vim.api.nvim_buf_set_lines(buffer, 0, -1, false, lines) -- Create window local window = vim.api.nvim_open_win(buffer, true, { relative = "editor", width = math.min(100, vim.api.nvim_get_option_value("columns", {})), height = math.min(40, vim.api.nvim_get_option_value("lines", {})), row = 1, col = 1, border = "rounded", style = "minimal", title = " Document Details " }) -- Setup mappings local mappings = { [""] = { callback = function() vim.cmd('edit ' .. doc_details.document.file_path) vim.api.nvim_win_close(window, true) end, description = "Open document" }, ["e"] = { callback = function() editor.start_edit_mode(buffer, 1, 1) end, description = "Edit properties" }, ["q"] = { callback = function() vim.api.nvim_win_close(window, true) end, description = "Close" } } for key, action in pairs(mappings) do vim.keymap.set("n", key, action.callback, { buffer = buffer, noremap = true, silent = true }) end return { buffer_id = buffer, window_id = window, type = "document_details", document_id = document_id } end -- Generate document details content function M.generate_document_details(doc_details) local lines = {} -- Header table.insert(lines, "Document Details") table.insert(lines, string.rep("=", 50)) table.insert(lines, "") -- File information table.insert(lines, "## File Information") table.insert(lines, string.format("Path: %s", doc_details.document.file_path)) table.insert(lines, string.format("Created: %s", os.date("%Y-%m-%d %H:%M:%S", doc_details.document.created_at))) table.insert(lines, string.format("Modified: %s", os.date("%Y-%m-%d %H:%M:%S", doc_details.document.updated_at))) table.insert(lines, string.format("Content Hash: %s", doc_details.document.content_hash)) table.insert(lines, string.format("File Exists: %s", doc_details.file_exists and "Yes" or "No")) table.insert(lines, "") -- Properties table.insert(lines, "## Properties") if doc_details.properties and #doc_details.properties > 0 then for _, prop in ipairs(doc_details.properties) do local value_str = tostring(prop.value) if #value_str > 100 then value_str = value_str:sub(1, 97) .. "..." end table.insert(lines, string.format("%s: %s (%s)", prop.key, value_str, prop.value_type)) end else table.insert(lines, "No properties found") end table.insert(lines, "") -- Parse information if doc_details.parse_result and doc_details.parse_result.success then table.insert(lines, "## Analysis") local analysis = doc_details.parse_result.markdown_analysis table.insert(lines, string.format("Word Count: %d", analysis.word_count)) table.insert(lines, string.format("Character Count: %d", analysis.character_count)) table.insert(lines, string.format("Line Count: %d", analysis.line_count)) table.insert(lines, string.format("Reading Time: %d minutes", analysis.reading_time_minutes)) if #analysis.headings > 0 then table.insert(lines, "") table.insert(lines, "### Headings") for _, heading in ipairs(analysis.headings) do local indent = string.rep(" ", heading.level) table.insert(lines, indent .. heading.title) end end if #analysis.links > 0 then table.insert(lines, "") table.insert(lines, "### Links") for i, link in ipairs(analysis.links) do if i <= 5 then -- Limit to 5 links table.insert(lines, string.format("• %s → %s", link.text, link.url)) end end if #analysis.links > 5 then table.insert(lines, string.format("... and %d more", #analysis.links - 5)) end end end table.insert(lines, "") table.insert(lines, "Press to open document, e to edit, q to close") return lines end -- Switch view type function M.switch_view_type(new_view_type) local current_buffer = vim.api.nvim_get_current_buf() local success, result = view.switch_view_type(current_buffer, new_view_type) if success then vim.notify("Switched to " .. new_view_type .. " view", vim.log.levels.INFO) else vim.notify("Failed to switch view: " .. result, vim.log.levels.ERROR) end end -- Show view type menu function M.show_view_type_menu() local view_types = view.get_available_view_types() local choices = {} for _, view_type in ipairs(view_types) do table.insert(choices, string.format("%s %s - %s", view_type.icon, view_type.name, view_type.description)) end vim.ui.select(choices, { prompt = "Select view type:", format_item = function(item) return item end }, function(choice) if choice then -- Extract view type name from choice local view_type = choice:match("%s(%w+)%s-") or choice:match("(%w+)%s-") if view_type then M.switch_view_type(view_type) end end end) end -- Export view function M.export_view(format) local current_buffer = vim.api.nvim_get_current_buf() local success, result = view.export_view(current_buffer, format) if success then -- Ask user for file location local default_filename = "notex_export." .. format vim.ui.input({ prompt = "Export to file: ", default = default_filename }, function(filename) if filename and filename ~= "" then local write_ok, write_err = utils.write_file(filename, result) if write_ok then vim.notify("Exported to " .. filename, vim.log.levels.INFO) else vim.notify("Failed to export: " .. write_err, vim.log.levels.ERROR) end end end) else vim.notify("Failed to export: " .. result, vim.log.levels.ERROR) end end -- Show export menu function M.show_export_menu() local formats = { {name = "Markdown", extension = "md", description = "Markdown format"}, {name = "CSV", extension = "csv", description = "Comma-separated values"}, {name = "JSON", extension = "json", description = "JSON format"} } local choices = {} for _, format in ipairs(formats) do table.insert(choices, string.format("%s (%s) - %s", format.name, format.extension, format.description)) end vim.ui.select(choices, { prompt = "Select export format:" }, function(choice) if choice then local format_name = choice:match("(%w+)%s+%(") if format_name then M.export_view(format_name:lower()) end end end) end -- Show query prompt function M.show_query_prompt(initial_query) initial_query = initial_query or "" vim.ui.input({ prompt = "Query: ", default = initial_query, completion = "customlist,require('notex.ui').get_query_completions" }, function(query_string) if query_string and query_string ~= "" then M.execute_query_and_show_results(query_string) end end) end -- Execute query and show results function M.execute_query_and_show_results(query_string, options) options = options or {} vim.notify("Executing query...", vim.log.levels.INFO) -- Execute query local result = query_engine.execute_query(query_string, options) if result.success then -- Show results local view_config = M.show_query_results(result, options) if view_config then utils.log("INFO", "Query executed successfully", { document_count = #result.documents, execution_time_ms = result.execution_time_ms }) end else M.show_error("Query Failed", result.errors, options) end end -- Get query completions function M.get_query_completions() local suggestions = query_engine.get_suggestions("", 0) local completions = {} -- Property suggestions for _, prop in ipairs(suggestions.properties or {}) do table.insert(completions, prop .. ":") end -- Value suggestions for common properties for prop, values in pairs(suggestions.values or {}) do for _, value in ipairs(values) do table.insert(completions, prop .. ' = "' .. value .. '"') end end -- Operator suggestions for _, op in ipairs(suggestions.operators or {}) do table.insert(completions, "WHERE " .. op) end table.sort(completions) return completions end -- Setup view cleanup function M.setup_view_cleanup(view_config) local group = vim.api.nvim_create_augroup("NotexViewCleanup_" .. view_config.buffer_id, {clear = true}) vim.api.nvim_create_autocmd({"BufLeave", "WinLeave"}, { buffer = view_config.buffer_id, once = true, callback = function() -- Give user time to interact, then cleanup vim.defer_fn(function() if vim.api.nvim_buf_is_valid(view_config.buffer_id) then ui_state.active_views[view_config.buffer_id] = nil end end, 1000) end }) end -- Get UI status function M.get_ui_status() local active_views = buffer_manager.get_active_buffers() local active_editors = editor.get_active_editors() return { active_views_count = vim.tbl_count(active_views), active_editors_count = vim.tbl_count(active_editors), total_windows = vim.tbl_count(vim.api.nvim_list_wins()), current_buffer = vim.api.nvim_get_current_buf(), current_window = vim.api.nvim_get_current_win() } end -- Cleanup all UI components function M.cleanup_all() -- Close all notex buffers local buffers = vim.api.nvim_list_bufs() for _, buf in ipairs(buffers) do local buf_name = vim.api.nvim_buf_get_name(buf) if buf_name:match("^notex://") then if vim.api.nvim_buf_is_valid(buf) then vim.api.nvim_buf_delete(buf, {force = true}) end end end -- Clear state ui_state.active_views = {} buffer_manager.cleanup_buffers() utils.log("INFO", "Cleaned up all UI components") end -- Initialize UI system function M.init() -- Set up global keymaps if not already set local global_keymaps = { ["nq"] = { callback = function() M.show_query_prompt() end, description = "New query" }, ["nv"] = { callback = function() M.show_view_type_menu() end, description = "Switch view type" }, ["ne"] = { callback = function() M.show_export_menu() end, description = "Export view" } } for key, action in pairs(global_keymaps) do if not vim.fn.hasmapto(key, "n") then vim.keymap.set("n", key, action.callback, { noremap = true, silent = true, desc = action.description }) end end utils.log("INFO", "UI system initialized") return true, "UI system initialized successfully" end return M