Initial vibecoded proof of concept

This commit is contained in:
Alex Selimov 2025-10-05 20:16:33 -04:00
parent 74812459af
commit 461318a656
Signed by: aselimov
GPG key ID: 3DDB9C3E023F1F31
61 changed files with 13306 additions and 0 deletions

480
lua/notex/ui/init.lua Normal file
View file

@ -0,0 +1,480 @@
-- 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 = {
["<CR>"] = {
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 <Enter> 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 = {
["<leader>nq"] = {
callback = function() M.show_query_prompt() end,
description = "New query"
},
["<leader>nv"] = {
callback = function() M.show_view_type_menu() end,
description = "Switch view type"
},
["<leader>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