diff --git a/druid/editor_scripts/core/asset_store.lua b/druid/editor_scripts/core/asset_store.lua index 621ccb4..be492c0 100644 --- a/druid/editor_scripts/core/asset_store.lua +++ b/druid/editor_scripts/core/asset_store.lua @@ -8,6 +8,37 @@ local internal = require("druid.editor_scripts.core.asset_store_internal") local M = {} +---Build type options array +---@return table +local function build_type_options() + return {"All", "Installed", "Not Installed"} +end + + +---Build author options array +---@param authors table +---@return table +local function build_author_options(authors) + local options = {"All Authors"} + for _, author in ipairs(authors) do + table.insert(options, author) + end + return options +end + + +---Build tag options array +---@param tags table +---@return table +local function build_tag_options(tags) + local options = {"All Tags"} + for _, tag in ipairs(tags) do + table.insert(options, tag) + end + return options +end + + ---Handle widget installation ---@param item table - Widget item to install ---@param install_folder string - Installation folder @@ -44,11 +75,44 @@ function M.open_asset_store(store_url) local dialog_component = editor.ui.component(function(props) -- State management local all_items = editor.ui.use_state(initial_items) - local filtered_items, set_filtered_items = editor.ui.use_state(initial_items) local install_folder, set_install_folder = editor.ui.use_state(editor.prefs.get("druid.asset_install_folder") or installer.get_install_folder()) local search_query, set_search_query = editor.ui.use_state("") + local filter_type, set_filter_type = editor.ui.use_state("All") + local filter_author, set_filter_author = editor.ui.use_state("All Authors") + local filter_tag, set_filter_tag = editor.ui.use_state("All Tags") local install_status, set_install_status = editor.ui.use_state("") + -- Extract unique authors and tags for dropdown options + local authors = editor.ui.use_memo(internal.extract_authors, all_items) + local tags = editor.ui.use_memo(internal.extract_tags, all_items) + + -- Build dropdown options (memoized to avoid recreation on each render) + local type_options = editor.ui.use_memo(build_type_options) + local author_options = editor.ui.use_memo(build_author_options, authors) + local tag_options = editor.ui.use_memo(build_tag_options, tags) + + -- Debug output + if #type_options > 0 then + print("Type options count:", #type_options, "first:", type_options[1]) + end + if #author_options > 0 then + print("Author options count:", #author_options, "first:", author_options[1]) + end + if #tag_options > 0 then + print("Tag options count:", #tag_options, "first:", tag_options[1]) + end + + -- Filter items based on all filters + local filtered_items = editor.ui.use_memo( + internal.filter_items_by_filters, + all_items, + search_query, + filter_type, + filter_author, + filter_tag, + install_folder + ) + -- Installation handlers local function on_install(item) handle_install(item, install_folder, @@ -86,6 +150,58 @@ function M.open_asset_store(store_url) } })) + -- Filter dropdowns section + table.insert(content_children, editor.ui.horizontal({ + spacing = editor.ui.SPACING.MEDIUM, + children = { + -- Type filter dropdown + editor.ui.horizontal({ + spacing = editor.ui.SPACING.SMALL, + children = { + editor.ui.label({ + text = "Type:", + color = editor.ui.COLOR.TEXT + }), + editor.ui.select_box({ + value = filter_type, + options = type_options, + on_value_changed = set_filter_type + }) + } + }), + -- Author filter dropdown + editor.ui.horizontal({ + spacing = editor.ui.SPACING.SMALL, + children = { + editor.ui.label({ + text = "Author:", + color = editor.ui.COLOR.TEXT + }), + editor.ui.select_box({ + value = filter_author, + options = author_options, + on_value_changed = set_filter_author + }) + } + }), + -- Tag filter dropdown + editor.ui.horizontal({ + spacing = editor.ui.SPACING.SMALL, + children = { + editor.ui.label({ + text = "Tag:", + color = editor.ui.COLOR.TEXT + }), + editor.ui.select_box({ + value = filter_tag, + options = tag_options, + on_value_changed = set_filter_tag + }) + } + }) + } + })) + -- Search section table.insert(content_children, editor.ui.horizontal({ spacing = editor.ui.SPACING.MEDIUM, @@ -96,10 +212,7 @@ function M.open_asset_store(store_url) }), editor.ui.string_field({ value = search_query, - on_value_changed = function(new_query) - set_search_query(new_query) - set_filtered_items(internal.filter_items(all_items, new_query)) - end, + on_value_changed = set_search_query, title = "Search:", tooltip = "Search for widgets by title, author, or description", grow = true @@ -133,8 +246,9 @@ function M.open_asset_store(store_url) return editor.ui.dialog({ title = "Druid Asset Store", content = editor.ui.vertical({ - spacing = editor.ui.SPACING.SMALL, + spacing = editor.ui.SPACING.MEDIUM, padding = editor.ui.PADDING.SMALL, + grow = true, children = content_children }), buttons = { diff --git a/druid/editor_scripts/core/asset_store_internal.lua b/druid/editor_scripts/core/asset_store_internal.lua index 41b8569..cbefb85 100644 --- a/druid/editor_scripts/core/asset_store_internal.lua +++ b/druid/editor_scripts/core/asset_store_internal.lua @@ -1,3 +1,5 @@ +local installer = require("druid.editor_scripts.core.installer") + local M = {} ---Download a JSON file from a URL @@ -71,6 +73,108 @@ function M.filter_items(items, query) end +---Extract unique authors from items list +---@param items table - List of widget items +---@return table - Sorted list of unique authors +function M.extract_authors(items) + local authors = {} + local author_set = {} + + for _, item in ipairs(items) do + if item.author and not author_set[item.author] then + author_set[item.author] = true + table.insert(authors, item.author) + end + end + + table.sort(authors) + return authors +end + + +---Extract unique tags from items list +---@param items table - List of widget items +---@return table - Sorted list of unique tags +function M.extract_tags(items) + local tags = {} + local tag_set = {} + + for _, item in ipairs(items) do + if item.tags then + for _, tag in ipairs(item.tags) do + if not tag_set[tag] then + tag_set[tag] = true + table.insert(tags, tag) + end + end + end + end + + table.sort(tags) + return tags +end + + +---Filter items based on all filters (search, type, author, tag) +---@param items table - List of widget items +---@param search_query string - Search query +---@param filter_type string - Type filter: "All", "Installed", "Not Installed" +---@param filter_author string - Author filter: "All Authors" or specific author +---@param filter_tag string - Tag filter: "All Tags" or specific tag +---@param install_folder string - Installation folder to check installed status +---@return table - Filtered items +function M.filter_items_by_filters(items, search_query, filter_type, filter_author, filter_tag, install_folder) + local filtered = items + + -- Filter by search query + if search_query and search_query ~= "" then + filtered = M.filter_items(filtered, search_query) + end + + -- Filter by type (Installed/Not Installed) + if filter_type and filter_type ~= "All" then + local type_filtered = {} + for _, item in ipairs(filtered) do + local is_installed = installer.is_widget_installed(item, install_folder) + if (filter_type == "Installed" and is_installed) or + (filter_type == "Not Installed" and not is_installed) then + table.insert(type_filtered, item) + end + end + filtered = type_filtered + end + + -- Filter by author + if filter_author and filter_author ~= "All Authors" then + local author_filtered = {} + for _, item in ipairs(filtered) do + if item.author == filter_author then + table.insert(author_filtered, item) + end + end + filtered = author_filtered + end + + -- Filter by tag + if filter_tag and filter_tag ~= "All Tags" then + local tag_filtered = {} + for _, item in ipairs(filtered) do + if item.tags then + for _, tag in ipairs(item.tags) do + if tag == filter_tag then + table.insert(tag_filtered, item) + break + end + end + end + end + filtered = tag_filtered + end + + return filtered +end + + ---Open a URL in the default browser ---@param url string - The URL to open function M.open_url(url)