mirror of
https://github.com/Insality/druid
synced 2025-11-26 10:50:54 +01:00
Up
This commit is contained in:
@@ -2,43 +2,88 @@
|
||||
--- Handles fetching widget data, displaying the store interface, and managing installations
|
||||
|
||||
local installer = require("druid.editor_scripts.core.installer")
|
||||
local ui_components = require("druid.editor_scripts.core.ui_components")
|
||||
local internal = require("druid.editor_scripts.core.asset_store_internal")
|
||||
local internal = require("druid.editor_scripts.core.asset_store.data")
|
||||
local dialog_ui = require("druid.editor_scripts.core.asset_store.ui.dialog")
|
||||
local filters_ui = require("druid.editor_scripts.core.asset_store.ui.filters")
|
||||
local search_ui = require("druid.editor_scripts.core.asset_store.ui.search")
|
||||
local settings_ui = require("druid.editor_scripts.core.asset_store.ui.settings")
|
||||
local widget_list_ui = require("druid.editor_scripts.core.asset_store.ui.widget_list")
|
||||
|
||||
local M = {}
|
||||
|
||||
|
||||
---Build type options array
|
||||
---@return table
|
||||
local function build_type_options()
|
||||
return {"All", "Installed", "Not Installed"}
|
||||
end
|
||||
local INFO_RESULT = "asset_store_open_info"
|
||||
local DEFAULT_INSTALL_PREF_KEY = "druid.asset_install_folder"
|
||||
local DEFAULT_INSTALL_FOLDER = "/widget"
|
||||
local DEFAULT_TITLE = "Asset Store"
|
||||
local DEFAULT_INFO_BUTTON = "Info"
|
||||
local DEFAULT_CLOSE_BUTTON = "Close"
|
||||
local DEFAULT_EMPTY_SEARCH_MESSAGE = "No widgets found matching '%s'."
|
||||
local DEFAULT_EMPTY_FILTER_MESSAGE = "No widgets found matching the current filters."
|
||||
local DEFAULT_SEARCH_LABELS = {
|
||||
search_tooltip = "Search for widgets by title, author, or description"
|
||||
}
|
||||
|
||||
|
||||
---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)
|
||||
local function normalize_config(input)
|
||||
if type(input) == "string" then
|
||||
input = { store_url = input }
|
||||
end
|
||||
return options
|
||||
end
|
||||
|
||||
assert(type(input) == "table", "asset_store.open expects a string URL or config table")
|
||||
assert(input.store_url, "asset_store.open requires a store_url")
|
||||
|
||||
---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)
|
||||
local config = {
|
||||
store_url = input.store_url,
|
||||
info_url = input.info_url,
|
||||
title = input.title or DEFAULT_TITLE,
|
||||
info_button_label = input.info_button_label or DEFAULT_INFO_BUTTON,
|
||||
close_button_label = input.close_button_label or DEFAULT_CLOSE_BUTTON,
|
||||
empty_search_message = input.empty_search_message or DEFAULT_EMPTY_SEARCH_MESSAGE,
|
||||
empty_filter_message = input.empty_filter_message or DEFAULT_EMPTY_FILTER_MESSAGE,
|
||||
install_prefs_key = input.install_prefs_key,
|
||||
default_install_folder = input.default_install_folder or DEFAULT_INSTALL_FOLDER,
|
||||
labels = input.labels or {},
|
||||
info_action = input.info_action,
|
||||
}
|
||||
|
||||
if config.install_prefs_key == nil then
|
||||
config.install_prefs_key = DEFAULT_INSTALL_PREF_KEY
|
||||
elseif config.install_prefs_key == false then
|
||||
config.install_prefs_key = nil
|
||||
end
|
||||
return options
|
||||
|
||||
config.labels.search = config.labels.search or {}
|
||||
for key, value in pairs(DEFAULT_SEARCH_LABELS) do
|
||||
if config.labels.search[key] == nil then
|
||||
config.labels.search[key] = value
|
||||
end
|
||||
end
|
||||
|
||||
return config
|
||||
end
|
||||
|
||||
|
||||
|
||||
local function get_initial_install_folder(config)
|
||||
if not config.install_prefs_key then
|
||||
return config.default_install_folder
|
||||
end
|
||||
|
||||
return editor.prefs.get(config.install_prefs_key) or config.default_install_folder
|
||||
end
|
||||
|
||||
|
||||
|
||||
local function persist_install_folder(config, folder)
|
||||
if not config.install_prefs_key then
|
||||
return
|
||||
end
|
||||
|
||||
editor.prefs.set(config.install_prefs_key, folder)
|
||||
end
|
||||
|
||||
|
||||
|
||||
---Handle widget installation
|
||||
---@param item table - Widget item to install
|
||||
---@param install_folder string - Installation folder
|
||||
@@ -60,50 +105,38 @@ local function handle_install(item, install_folder, all_items, on_success, on_er
|
||||
end
|
||||
|
||||
|
||||
---Open the asset store dialog
|
||||
function M.open_asset_store(store_url)
|
||||
print("Opening Druid Asset Store from:", store_url)
|
||||
function M.open(config_input)
|
||||
local config = normalize_config(config_input)
|
||||
|
||||
-- Fetch data synchronously before creating the dialog
|
||||
local store_data, fetch_error = internal.download_json(store_url)
|
||||
print("Opening " .. config.title .. " from:", config.store_url)
|
||||
|
||||
local store_data, fetch_error = internal.download_json(config.store_url)
|
||||
if not store_data then
|
||||
print("Failed to load widgets:", fetch_error)
|
||||
print("Failed to load store items:", fetch_error)
|
||||
return
|
||||
end
|
||||
print("Successfully loaded", #store_data.items, "widgets")
|
||||
print("Successfully loaded", #store_data.items, "items")
|
||||
|
||||
local initial_items = store_data.items
|
||||
local initial_install_folder = get_initial_install_folder(config)
|
||||
local filter_overrides = config.labels.filters and { labels = config.labels.filters } or nil
|
||||
|
||||
local dialog_component = editor.ui.component(function(props)
|
||||
-- State management
|
||||
local all_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 install_folder, set_install_folder = editor.ui.use_state(initial_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)
|
||||
local type_options = editor.ui.use_memo(filters_ui.build_type_options, filter_overrides)
|
||||
local author_options = editor.ui.use_memo(filters_ui.build_author_options, authors, filter_overrides)
|
||||
local tag_options = editor.ui.use_memo(filters_ui.build_tag_options, tags, filter_overrides)
|
||||
|
||||
-- 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,
|
||||
@@ -114,7 +147,6 @@ function M.open_asset_store(store_url)
|
||||
install_folder
|
||||
)
|
||||
|
||||
-- Installation handlers
|
||||
local function on_install(item)
|
||||
handle_install(item, install_folder, all_items,
|
||||
function(message)
|
||||
@@ -126,116 +158,57 @@ function M.open_asset_store(store_url)
|
||||
)
|
||||
end
|
||||
|
||||
-- Build UI content
|
||||
local content_children = {}
|
||||
|
||||
-- Settings section
|
||||
table.insert(content_children, editor.ui.horizontal({
|
||||
spacing = editor.ui.SPACING.MEDIUM,
|
||||
children = {
|
||||
editor.ui.label({
|
||||
spacing = editor.ui.SPACING.MEDIUM,
|
||||
text = "Installation Folder:",
|
||||
color = editor.ui.COLOR.TEXT
|
||||
}),
|
||||
|
||||
editor.ui.string_field({
|
||||
value = install_folder,
|
||||
on_value_changed = function(new_folder)
|
||||
table.insert(content_children, settings_ui.create({
|
||||
install_folder = install_folder,
|
||||
on_install_folder_changed = function(new_folder)
|
||||
set_install_folder(new_folder)
|
||||
editor.prefs.set("druid.asset_install_folder", new_folder)
|
||||
persist_install_folder(config, new_folder)
|
||||
end,
|
||||
title = "Installation Folder:",
|
||||
tooltip = "The folder to install the assets to",
|
||||
}),
|
||||
}
|
||||
labels = config.labels.settings
|
||||
}))
|
||||
|
||||
-- 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
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
table.insert(content_children, filters_ui.create({
|
||||
filter_type = filter_type,
|
||||
filter_author = filter_author,
|
||||
filter_tag = filter_tag,
|
||||
type_options = type_options,
|
||||
author_options = author_options,
|
||||
tag_options = tag_options,
|
||||
on_type_change = set_filter_type,
|
||||
on_author_change = set_filter_author,
|
||||
on_tag_change = set_filter_tag,
|
||||
labels = config.labels.filters,
|
||||
}))
|
||||
|
||||
-- Search section
|
||||
table.insert(content_children, editor.ui.horizontal({
|
||||
spacing = editor.ui.SPACING.MEDIUM,
|
||||
children = {
|
||||
editor.ui.label({
|
||||
text = "Search:",
|
||||
color = editor.ui.COLOR.TEXT
|
||||
}),
|
||||
editor.ui.string_field({
|
||||
value = search_query,
|
||||
on_value_changed = set_search_query,
|
||||
title = "Search:",
|
||||
tooltip = "Search for widgets by title, author, or description",
|
||||
grow = true
|
||||
})
|
||||
},
|
||||
table.insert(content_children, search_ui.create({
|
||||
search_query = search_query,
|
||||
on_search = set_search_query,
|
||||
labels = config.labels.search,
|
||||
}))
|
||||
|
||||
-- Main content area
|
||||
if #filtered_items == 0 then
|
||||
local message = search_query ~= "" and
|
||||
"No widgets found matching '" .. search_query .. "'." or
|
||||
"No widgets found matching the current filters."
|
||||
local message = config.empty_filter_message
|
||||
if search_query ~= "" then
|
||||
message = string.format(config.empty_search_message, search_query)
|
||||
end
|
||||
table.insert(content_children, editor.ui.label({
|
||||
text = message,
|
||||
color = editor.ui.COLOR.HINT,
|
||||
alignment = editor.ui.ALIGNMENT.CENTER
|
||||
}))
|
||||
else
|
||||
table.insert(content_children, ui_components.create_widget_list(filtered_items, on_install))
|
||||
table.insert(content_children, widget_list_ui.create(filtered_items, {
|
||||
on_install = on_install,
|
||||
open_url = internal.open_url,
|
||||
is_installed = function(item)
|
||||
return installer.is_widget_installed(item, install_folder)
|
||||
end,
|
||||
labels = config.labels.widget_card,
|
||||
}))
|
||||
end
|
||||
|
||||
-- Install status message
|
||||
if install_status ~= "" then
|
||||
table.insert(content_children, editor.ui.label({
|
||||
text = install_status,
|
||||
@@ -244,31 +217,33 @@ function M.open_asset_store(store_url)
|
||||
}))
|
||||
end
|
||||
|
||||
return editor.ui.dialog({
|
||||
title = "Druid Asset Store",
|
||||
content = editor.ui.vertical({
|
||||
spacing = editor.ui.SPACING.MEDIUM,
|
||||
padding = editor.ui.PADDING.SMALL,
|
||||
grow = true,
|
||||
children = content_children
|
||||
}),
|
||||
buttons = {
|
||||
editor.ui.dialog_button({
|
||||
text = "Info",
|
||||
result = "info_assets_store",
|
||||
}),
|
||||
editor.ui.dialog_button({
|
||||
text = "Close",
|
||||
local buttons = {}
|
||||
if config.info_url or config.info_action then
|
||||
table.insert(buttons, editor.ui.dialog_button({
|
||||
text = config.info_button_label,
|
||||
result = INFO_RESULT,
|
||||
}))
|
||||
end
|
||||
table.insert(buttons, editor.ui.dialog_button({
|
||||
text = config.close_button_label,
|
||||
cancel = true
|
||||
})
|
||||
}
|
||||
}))
|
||||
|
||||
return dialog_ui.build({
|
||||
title = config.title,
|
||||
children = content_children,
|
||||
buttons = buttons
|
||||
})
|
||||
end)
|
||||
|
||||
local result = editor.ui.show_dialog(dialog_component({}))
|
||||
|
||||
if result and result == "info_assets_store" then
|
||||
editor.browse("https://github.com/Insality/core/blob/main/druid_widget_store.md")
|
||||
if result and result == INFO_RESULT then
|
||||
if config.info_action then
|
||||
config.info_action()
|
||||
elseif config.info_url then
|
||||
internal.open_url(config.info_url)
|
||||
end
|
||||
end
|
||||
|
||||
return {}
|
||||
|
||||
196
druid/editor_scripts/core/asset_store/data.lua
Normal file
196
druid/editor_scripts/core/asset_store/data.lua
Normal file
@@ -0,0 +1,196 @@
|
||||
local installer = require("druid.editor_scripts.core.installer")
|
||||
|
||||
|
||||
local M = {}
|
||||
|
||||
|
||||
local function normalize_query(query)
|
||||
if not query or query == "" then
|
||||
return nil
|
||||
end
|
||||
|
||||
return string.lower(query)
|
||||
end
|
||||
|
||||
|
||||
local function is_unlisted_visible(item, lower_query)
|
||||
if not item.unlisted then
|
||||
return true
|
||||
end
|
||||
|
||||
if not lower_query or not item.id then
|
||||
return false
|
||||
end
|
||||
|
||||
return string.lower(item.id) == lower_query
|
||||
end
|
||||
|
||||
|
||||
function M.download_json(json_url)
|
||||
local response = http.request(json_url, { as = "json" })
|
||||
|
||||
if response.status ~= 200 then
|
||||
return nil, "Failed to fetch store data. HTTP status: " .. response.status
|
||||
end
|
||||
|
||||
if not response.body or not response.body.items then
|
||||
return nil, "Invalid store data format"
|
||||
end
|
||||
|
||||
return response.body, nil
|
||||
end
|
||||
|
||||
|
||||
function M.filter_items(items, query)
|
||||
if query == "" or query == nil then
|
||||
return items
|
||||
end
|
||||
|
||||
local filtered = {}
|
||||
local lower_query = string.lower(query)
|
||||
|
||||
for _, item in ipairs(items) do
|
||||
local matches = false
|
||||
if item.id and string.find(string.lower(item.id), lower_query, 1, true) then
|
||||
matches = true
|
||||
elseif item.title and string.find(string.lower(item.title), lower_query, 1, true) then
|
||||
matches = true
|
||||
elseif item.author and string.find(string.lower(item.author), lower_query, 1, true) then
|
||||
matches = true
|
||||
elseif item.description and string.find(string.lower(item.description), lower_query, 1, true) then
|
||||
matches = true
|
||||
end
|
||||
|
||||
if not matches and item.tags then
|
||||
for _, tag in ipairs(item.tags) do
|
||||
if string.find(string.lower(tag), lower_query, 1, true) then
|
||||
matches = true
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if not matches and item.depends then
|
||||
for _, dep in ipairs(item.depends) do
|
||||
if string.find(string.lower(dep), lower_query, 1, true) then
|
||||
matches = true
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if matches then
|
||||
table.insert(filtered, item)
|
||||
end
|
||||
end
|
||||
|
||||
return filtered
|
||||
end
|
||||
|
||||
|
||||
function M.extract_authors(items)
|
||||
local authors = {}
|
||||
local author_set = {}
|
||||
|
||||
for _, item in ipairs(items) do
|
||||
if not item.unlisted and 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
|
||||
|
||||
|
||||
function M.extract_tags(items)
|
||||
local tags = {}
|
||||
local tag_set = {}
|
||||
|
||||
for _, item in ipairs(items) do
|
||||
if not item.unlisted and 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
|
||||
|
||||
|
||||
function M.filter_items_by_filters(items, search_query, filter_type, filter_author, filter_tag, install_folder)
|
||||
local lower_query = normalize_query(search_query)
|
||||
local visible_items = {}
|
||||
|
||||
for _, item in ipairs(items) do
|
||||
if is_unlisted_visible(item, lower_query) then
|
||||
table.insert(visible_items, item)
|
||||
end
|
||||
end
|
||||
|
||||
local filtered = visible_items
|
||||
|
||||
if lower_query then
|
||||
filtered = M.filter_items(filtered, search_query)
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
|
||||
function M.open_url(url)
|
||||
if not url then
|
||||
print("No URL available for:", url)
|
||||
end
|
||||
|
||||
editor.browse(url)
|
||||
end
|
||||
|
||||
|
||||
return M
|
||||
|
||||
19
druid/editor_scripts/core/asset_store/ui/dialog.lua
Normal file
19
druid/editor_scripts/core/asset_store/ui/dialog.lua
Normal file
@@ -0,0 +1,19 @@
|
||||
local M = {}
|
||||
|
||||
|
||||
function M.build(params)
|
||||
return editor.ui.dialog({
|
||||
title = params.title or "Asset Store",
|
||||
content = editor.ui.vertical({
|
||||
spacing = params.spacing or editor.ui.SPACING.MEDIUM,
|
||||
padding = params.padding or editor.ui.PADDING.SMALL,
|
||||
grow = true,
|
||||
children = params.children or {}
|
||||
}),
|
||||
buttons = params.buttons or {}
|
||||
})
|
||||
end
|
||||
|
||||
|
||||
return M
|
||||
|
||||
119
druid/editor_scripts/core/asset_store/ui/filters.lua
Normal file
119
druid/editor_scripts/core/asset_store/ui/filters.lua
Normal file
@@ -0,0 +1,119 @@
|
||||
local DEFAULT_LABELS = {
|
||||
type_label = "Type:",
|
||||
author_label = "Author:",
|
||||
tag_label = "Tag:",
|
||||
all_types = "All",
|
||||
installed = "Installed",
|
||||
not_installed = "Not Installed",
|
||||
all_authors = "All Authors",
|
||||
all_tags = "All Tags",
|
||||
}
|
||||
|
||||
|
||||
local M = {}
|
||||
|
||||
|
||||
local function build_labels(overrides)
|
||||
if not overrides then
|
||||
return DEFAULT_LABELS
|
||||
end
|
||||
|
||||
local labels = {}
|
||||
for key, value in pairs(DEFAULT_LABELS) do
|
||||
labels[key] = overrides[key] or value
|
||||
end
|
||||
|
||||
return labels
|
||||
end
|
||||
|
||||
|
||||
function M.build_type_options(overrides)
|
||||
local labels = build_labels(overrides and overrides.labels)
|
||||
|
||||
return {
|
||||
labels.all_types,
|
||||
labels.installed,
|
||||
labels.not_installed,
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
function M.build_author_options(authors, overrides)
|
||||
local labels = build_labels(overrides and overrides.labels)
|
||||
local options = {labels.all_authors}
|
||||
|
||||
for _, author in ipairs(authors or {}) do
|
||||
table.insert(options, author)
|
||||
end
|
||||
|
||||
return options
|
||||
end
|
||||
|
||||
|
||||
function M.build_tag_options(tags, overrides)
|
||||
local labels = build_labels(overrides and overrides.labels)
|
||||
local options = {labels.all_tags}
|
||||
|
||||
for _, tag in ipairs(tags or {}) do
|
||||
table.insert(options, tag)
|
||||
end
|
||||
|
||||
return options
|
||||
end
|
||||
|
||||
|
||||
function M.create(params)
|
||||
local labels = build_labels(params and params.labels)
|
||||
|
||||
return editor.ui.horizontal({
|
||||
spacing = editor.ui.SPACING.MEDIUM,
|
||||
children = {
|
||||
editor.ui.horizontal({
|
||||
spacing = editor.ui.SPACING.SMALL,
|
||||
children = {
|
||||
editor.ui.label({
|
||||
text = labels.type_label,
|
||||
color = editor.ui.COLOR.TEXT
|
||||
}),
|
||||
editor.ui.select_box({
|
||||
value = params.filter_type,
|
||||
options = params.type_options,
|
||||
on_value_changed = params.on_type_change
|
||||
})
|
||||
}
|
||||
}),
|
||||
editor.ui.horizontal({
|
||||
spacing = editor.ui.SPACING.SMALL,
|
||||
children = {
|
||||
editor.ui.label({
|
||||
text = labels.author_label,
|
||||
color = editor.ui.COLOR.TEXT
|
||||
}),
|
||||
editor.ui.select_box({
|
||||
value = params.filter_author,
|
||||
options = params.author_options,
|
||||
on_value_changed = params.on_author_change
|
||||
})
|
||||
}
|
||||
}),
|
||||
editor.ui.horizontal({
|
||||
spacing = editor.ui.SPACING.SMALL,
|
||||
children = {
|
||||
editor.ui.label({
|
||||
text = labels.tag_label,
|
||||
color = editor.ui.COLOR.TEXT
|
||||
}),
|
||||
editor.ui.select_box({
|
||||
value = params.filter_tag,
|
||||
options = params.tag_options,
|
||||
on_value_changed = params.on_tag_change
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
|
||||
return M
|
||||
|
||||
49
druid/editor_scripts/core/asset_store/ui/search.lua
Normal file
49
druid/editor_scripts/core/asset_store/ui/search.lua
Normal file
@@ -0,0 +1,49 @@
|
||||
local DEFAULT_LABELS = {
|
||||
search_label = "Search:",
|
||||
search_title = "Search:",
|
||||
search_tooltip = "Search for items",
|
||||
}
|
||||
|
||||
|
||||
local M = {}
|
||||
|
||||
|
||||
local function build_labels(overrides)
|
||||
if not overrides then
|
||||
return DEFAULT_LABELS
|
||||
end
|
||||
|
||||
local labels = {}
|
||||
for key, value in pairs(DEFAULT_LABELS) do
|
||||
labels[key] = overrides[key] or value
|
||||
end
|
||||
|
||||
return labels
|
||||
end
|
||||
|
||||
|
||||
|
||||
function M.create(params)
|
||||
local labels = build_labels(params and params.labels)
|
||||
|
||||
return editor.ui.horizontal({
|
||||
spacing = editor.ui.SPACING.MEDIUM,
|
||||
children = {
|
||||
editor.ui.label({
|
||||
text = labels.search_label,
|
||||
color = editor.ui.COLOR.TEXT
|
||||
}),
|
||||
editor.ui.string_field({
|
||||
value = params.search_query or "",
|
||||
on_value_changed = params.on_search,
|
||||
title = labels.search_title,
|
||||
tooltip = labels.search_tooltip,
|
||||
grow = true
|
||||
})
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
|
||||
return M
|
||||
|
||||
49
druid/editor_scripts/core/asset_store/ui/settings.lua
Normal file
49
druid/editor_scripts/core/asset_store/ui/settings.lua
Normal file
@@ -0,0 +1,49 @@
|
||||
local DEFAULT_LABELS = {
|
||||
install_label = "Installation Folder:",
|
||||
install_title = "Installation Folder:",
|
||||
install_tooltip = "The folder to install the assets to",
|
||||
}
|
||||
|
||||
|
||||
local M = {}
|
||||
|
||||
|
||||
local function build_labels(overrides)
|
||||
if not overrides then
|
||||
return DEFAULT_LABELS
|
||||
end
|
||||
|
||||
local labels = {}
|
||||
for key, value in pairs(DEFAULT_LABELS) do
|
||||
labels[key] = overrides[key] or value
|
||||
end
|
||||
|
||||
return labels
|
||||
end
|
||||
|
||||
|
||||
|
||||
function M.create(params)
|
||||
local labels = build_labels(params and params.labels)
|
||||
|
||||
return editor.ui.horizontal({
|
||||
spacing = editor.ui.SPACING.MEDIUM,
|
||||
children = {
|
||||
editor.ui.label({
|
||||
spacing = editor.ui.SPACING.MEDIUM,
|
||||
text = labels.install_label,
|
||||
color = editor.ui.COLOR.TEXT
|
||||
}),
|
||||
editor.ui.string_field({
|
||||
value = params.install_folder,
|
||||
on_value_changed = params.on_install_folder_changed,
|
||||
title = labels.install_title,
|
||||
tooltip = labels.install_tooltip,
|
||||
}),
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
|
||||
return M
|
||||
|
||||
167
druid/editor_scripts/core/asset_store/ui/widget_card.lua
Normal file
167
druid/editor_scripts/core/asset_store/ui/widget_card.lua
Normal file
@@ -0,0 +1,167 @@
|
||||
local DEFAULT_LABELS = {
|
||||
install_button = "Install",
|
||||
api_button = "API",
|
||||
example_button = "Example",
|
||||
author_caption = "Author",
|
||||
installed_tag = "✓ Installed",
|
||||
tags_prefix = "Tags: ",
|
||||
depends_prefix = "Depends: ",
|
||||
size_separator = "• ",
|
||||
unknown_size = "Unknown size",
|
||||
unknown_version = "Unknown version",
|
||||
}
|
||||
|
||||
|
||||
local M = {}
|
||||
|
||||
|
||||
local function format_size(size_bytes)
|
||||
if not size_bytes then
|
||||
return DEFAULT_LABELS.unknown_size
|
||||
end
|
||||
|
||||
if size_bytes < 1024 then
|
||||
return size_bytes .. " B"
|
||||
elseif size_bytes < 1024 * 1024 then
|
||||
return math.floor(size_bytes / 1024) .. " KB"
|
||||
end
|
||||
|
||||
return math.floor(size_bytes / (1024 * 1024)) .. " MB"
|
||||
end
|
||||
|
||||
|
||||
|
||||
local function build_labels(overrides)
|
||||
if not overrides then
|
||||
return DEFAULT_LABELS
|
||||
end
|
||||
|
||||
local labels = {}
|
||||
for key, value in pairs(DEFAULT_LABELS) do
|
||||
labels[key] = overrides[key] or value
|
||||
end
|
||||
|
||||
return labels
|
||||
end
|
||||
|
||||
|
||||
|
||||
function M.create(item, context)
|
||||
local labels = build_labels(context and context.labels)
|
||||
local open_url = context and context.open_url or function(_) end
|
||||
local on_install = context and context.on_install or function(...) end
|
||||
local is_installed = context and context.is_installed or false
|
||||
|
||||
local size_text = format_size(item.size)
|
||||
local version_text = item.version and ("v" .. item.version) or labels.unknown_version
|
||||
local tags_text = item.tags and #item.tags > 0 and labels.tags_prefix .. table.concat(item.tags, ", ") or ""
|
||||
local deps_text = item.depends and #item.depends > 0 and labels.depends_prefix .. table.concat(item.depends, ", ") or ""
|
||||
|
||||
local widget_details_children = {
|
||||
editor.ui.horizontal({
|
||||
spacing = editor.ui.SPACING.SMALL,
|
||||
children = {
|
||||
editor.ui.label({
|
||||
text = item.title or item.id,
|
||||
color = editor.ui.COLOR.OVERRIDE
|
||||
}),
|
||||
editor.ui.label({
|
||||
text = version_text,
|
||||
color = editor.ui.COLOR.WARNING
|
||||
}),
|
||||
editor.ui.label({
|
||||
text = labels.size_separator .. size_text,
|
||||
color = editor.ui.COLOR.HINT
|
||||
}),
|
||||
}
|
||||
}),
|
||||
editor.ui.paragraph({
|
||||
text = item.description or "No description available",
|
||||
color = editor.ui.COLOR.TEXT
|
||||
})
|
||||
}
|
||||
|
||||
if tags_text ~= "" then
|
||||
table.insert(widget_details_children, editor.ui.label({
|
||||
text = tags_text,
|
||||
color = editor.ui.COLOR.HINT
|
||||
}))
|
||||
end
|
||||
|
||||
if deps_text ~= "" then
|
||||
table.insert(widget_details_children, editor.ui.label({
|
||||
text = deps_text,
|
||||
color = editor.ui.COLOR.HINT
|
||||
}))
|
||||
end
|
||||
|
||||
if is_installed then
|
||||
table.insert(widget_details_children, editor.ui.label({
|
||||
text = labels.installed_tag,
|
||||
color = editor.ui.COLOR.WARNING
|
||||
}))
|
||||
end
|
||||
|
||||
local button_children = {
|
||||
editor.ui.button({
|
||||
text = labels.install_button,
|
||||
on_pressed = on_install,
|
||||
enabled = not is_installed
|
||||
})
|
||||
}
|
||||
|
||||
if item.api then
|
||||
table.insert(button_children, editor.ui.button({
|
||||
text = labels.api_button,
|
||||
on_pressed = function() open_url(item.api) end,
|
||||
enabled = item.api ~= nil
|
||||
}))
|
||||
end
|
||||
|
||||
if item.example_url then
|
||||
table.insert(button_children, editor.ui.button({
|
||||
text = labels.example_button,
|
||||
on_pressed = function() open_url(item.example_url) end,
|
||||
enabled = item.example_url ~= nil
|
||||
}))
|
||||
end
|
||||
|
||||
table.insert(button_children, editor.ui.horizontal({ grow = true }))
|
||||
|
||||
if item.author_url then
|
||||
table.insert(button_children, editor.ui.label({
|
||||
text = labels.author_caption,
|
||||
color = editor.ui.COLOR.HINT
|
||||
}))
|
||||
table.insert(button_children, editor.ui.button({
|
||||
text = item.author or labels.author_caption,
|
||||
on_pressed = function() open_url(item.author_url) end,
|
||||
enabled = item.author_url ~= nil
|
||||
}))
|
||||
end
|
||||
|
||||
table.insert(widget_details_children, editor.ui.horizontal({
|
||||
spacing = editor.ui.SPACING.SMALL,
|
||||
children = button_children
|
||||
}))
|
||||
|
||||
return editor.ui.horizontal({
|
||||
spacing = editor.ui.SPACING.NONE,
|
||||
padding = editor.ui.PADDING.SMALL,
|
||||
children = {
|
||||
editor.ui.label({
|
||||
text = "•••",
|
||||
color = editor.ui.COLOR.HINT
|
||||
}),
|
||||
editor.ui.vertical({
|
||||
spacing = editor.ui.SPACING.SMALL,
|
||||
grow = true,
|
||||
children = widget_details_children
|
||||
}),
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
|
||||
return M
|
||||
|
||||
51
druid/editor_scripts/core/asset_store/ui/widget_list.lua
Normal file
51
druid/editor_scripts/core/asset_store/ui/widget_list.lua
Normal file
@@ -0,0 +1,51 @@
|
||||
local widget_card = require("druid.editor_scripts.core.asset_store.ui.widget_card")
|
||||
|
||||
|
||||
local M = {}
|
||||
|
||||
|
||||
local function noop(...)
|
||||
end
|
||||
|
||||
|
||||
|
||||
local function build_context(overrides)
|
||||
return {
|
||||
on_install = overrides.on_install or noop,
|
||||
open_url = overrides.open_url or noop,
|
||||
labels = overrides.labels,
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
|
||||
function M.create(items, overrides)
|
||||
local card_context = build_context(overrides or {})
|
||||
local is_installed = overrides and overrides.is_installed or function(_)
|
||||
return false
|
||||
end
|
||||
|
||||
local widget_items = {}
|
||||
for _, item in ipairs(items) do
|
||||
local context = {
|
||||
on_install = function()
|
||||
card_context.on_install(item)
|
||||
end,
|
||||
open_url = card_context.open_url,
|
||||
labels = card_context.labels,
|
||||
is_installed = is_installed(item),
|
||||
}
|
||||
|
||||
table.insert(widget_items, widget_card.create(item, context))
|
||||
end
|
||||
|
||||
return editor.ui.scroll({
|
||||
content = editor.ui.vertical({
|
||||
children = widget_items
|
||||
})
|
||||
})
|
||||
end
|
||||
|
||||
|
||||
return M
|
||||
|
||||
@@ -1,213 +1,39 @@
|
||||
local installer = require("druid.editor_scripts.core.installer")
|
||||
local data = require("druid.editor_scripts.core.asset_store.data")
|
||||
|
||||
|
||||
local M = {}
|
||||
|
||||
---Download a JSON file from a URL
|
||||
---@param json_url string - The URL to download the JSON from
|
||||
---@return table|nil, string|nil - The JSON data or nil, error message or nil
|
||||
|
||||
function M.download_json(json_url)
|
||||
local response = http.request(json_url, { as = "json" })
|
||||
|
||||
if response.status ~= 200 then
|
||||
return nil, "Failed to fetch store data. HTTP status: " .. response.status
|
||||
end
|
||||
|
||||
if not response.body or not response.body.items then
|
||||
return nil, "Invalid store data format"
|
||||
end
|
||||
|
||||
return response.body, nil
|
||||
return data.download_json(json_url)
|
||||
end
|
||||
|
||||
|
||||
local function is_unlisted_visible(item, lower_query)
|
||||
if not item.unlisted then
|
||||
return true
|
||||
end
|
||||
|
||||
if not lower_query or lower_query == "" or not item.id then
|
||||
return false
|
||||
end
|
||||
|
||||
return string.lower(item.id) == lower_query
|
||||
end
|
||||
|
||||
|
||||
---Filter items based on search query
|
||||
---Filter items based on search query
|
||||
---@param items table - List of widget items
|
||||
---@param query string - Search query
|
||||
---@return table - Filtered items
|
||||
function M.filter_items(items, query)
|
||||
if query == "" then
|
||||
return items
|
||||
end
|
||||
|
||||
local filtered = {}
|
||||
local lower_query = string.lower(query)
|
||||
|
||||
for _, item in ipairs(items) do
|
||||
-- Search in title, author, description
|
||||
local matches = false
|
||||
if item.id and string.find(string.lower(item.id), lower_query, 1, true) then
|
||||
matches = true
|
||||
elseif item.title and string.find(string.lower(item.title), lower_query, 1, true) then
|
||||
matches = true
|
||||
elseif item.author and string.find(string.lower(item.author), lower_query, 1, true) then
|
||||
matches = true
|
||||
elseif item.description and string.find(string.lower(item.description), lower_query, 1, true) then
|
||||
matches = true
|
||||
end
|
||||
|
||||
-- Search in tags
|
||||
if not matches and item.tags then
|
||||
for _, tag in ipairs(item.tags) do
|
||||
if string.find(string.lower(tag), lower_query, 1, true) then
|
||||
matches = true
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Search in dependencies
|
||||
if not matches and item.depends then
|
||||
for _, dep in ipairs(item.depends) do
|
||||
if string.find(string.lower(dep), lower_query, 1, true) then
|
||||
matches = true
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if matches then
|
||||
table.insert(filtered, item)
|
||||
end
|
||||
end
|
||||
|
||||
return filtered
|
||||
return data.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 not item.unlisted and 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
|
||||
return data.extract_authors(items)
|
||||
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 not item.unlisted and 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
|
||||
return data.extract_tags(items)
|
||||
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 lower_query = nil
|
||||
if search_query and search_query ~= "" then
|
||||
lower_query = string.lower(search_query)
|
||||
end
|
||||
|
||||
local visible_items = {}
|
||||
for _, item in ipairs(items) do
|
||||
if is_unlisted_visible(item, lower_query) then
|
||||
table.insert(visible_items, item)
|
||||
end
|
||||
end
|
||||
|
||||
local filtered = visible_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
|
||||
return data.filter_items_by_filters(items, search_query, filter_type, filter_author, filter_tag, install_folder)
|
||||
end
|
||||
|
||||
|
||||
---Open a URL in the default browser
|
||||
---@param url string - The URL to open
|
||||
function M.open_url(url)
|
||||
if not url then
|
||||
print("No URL available for:", url)
|
||||
end
|
||||
|
||||
editor.browse(url)
|
||||
end
|
||||
|
||||
|
||||
@@ -1,303 +0,0 @@
|
||||
--- Module for reusable UI components in the asset store
|
||||
--- Contains component builders for filters, widget items, and lists
|
||||
|
||||
local internal = require("druid.editor_scripts.core.asset_store_internal")
|
||||
local installer = require("druid.editor_scripts.core.installer")
|
||||
|
||||
local M = {}
|
||||
|
||||
|
||||
---Create a settings section with installation folder input
|
||||
---@param install_path string - Current installation path
|
||||
---@param on_change function - Callback when path changes
|
||||
---@return userdata - UI component
|
||||
function M.create_settings_section(install_path, on_change)
|
||||
return editor.ui.vertical({
|
||||
spacing = editor.ui.SPACING.SMALL,
|
||||
children = {
|
||||
editor.ui.label({
|
||||
text = "Installation Folder:",
|
||||
color = editor.ui.COLOR.TEXT
|
||||
}),
|
||||
editor.ui.label({
|
||||
text = install_path,
|
||||
color = editor.ui.COLOR.TEXT,
|
||||
grow = true
|
||||
})
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
|
||||
---Extract unique authors from items list
|
||||
---@param items table - List of widget items
|
||||
---@return table - Sorted list of unique authors
|
||||
local function 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
|
||||
local function 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
|
||||
|
||||
|
||||
---Create filter section with author and tag dropdowns
|
||||
---@param items table - List of all widget items
|
||||
---@param author_filter string - Current author filter
|
||||
---@param tag_filter string - Current tag filter
|
||||
---@param on_author_change function - Callback for author filter change
|
||||
---@param on_tag_change function - Callback for tag filter change
|
||||
---@return userdata - UI component
|
||||
function M.create_filter_section(items, author_filter, tag_filter, on_author_change, on_tag_change)
|
||||
local authors = extract_authors(items)
|
||||
local tags = extract_tags(items)
|
||||
|
||||
-- Build author options
|
||||
local author_options = {"All Authors"}
|
||||
for _, author in ipairs(authors) do
|
||||
table.insert(author_options, author)
|
||||
end
|
||||
|
||||
-- Build tag options
|
||||
local tag_options = {"All Categories"}
|
||||
for _, tag in ipairs(tags) do
|
||||
table.insert(tag_options, tag)
|
||||
end
|
||||
|
||||
return editor.ui.horizontal({
|
||||
spacing = editor.ui.SPACING.MEDIUM,
|
||||
children = {
|
||||
editor.ui.vertical({
|
||||
spacing = editor.ui.SPACING.SMALL,
|
||||
children = {
|
||||
editor.ui.label({
|
||||
text = "Author:",
|
||||
color = editor.ui.COLOR.TEXT
|
||||
}),
|
||||
editor.ui.label({
|
||||
text = author_filter,
|
||||
color = editor.ui.COLOR.TEXT
|
||||
})
|
||||
}
|
||||
}),
|
||||
editor.ui.vertical({
|
||||
spacing = editor.ui.SPACING.SMALL,
|
||||
children = {
|
||||
editor.ui.label({
|
||||
text = "Category:",
|
||||
color = editor.ui.COLOR.TEXT
|
||||
}),
|
||||
editor.ui.label({
|
||||
text = tag_filter,
|
||||
color = editor.ui.COLOR.TEXT
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
|
||||
---Format file size for display
|
||||
---@param size_bytes number - Size in bytes
|
||||
---@return string - Formatted size string
|
||||
local function format_size(size_bytes)
|
||||
if size_bytes < 1024 then
|
||||
return size_bytes .. " B"
|
||||
elseif size_bytes < 1024 * 1024 then
|
||||
return math.floor(size_bytes / 1024) .. " KB"
|
||||
else
|
||||
return math.floor(size_bytes / (1024 * 1024)) .. " MB"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
---Create a widget item card
|
||||
---@param item table - Widget item data
|
||||
---@param is_installed boolean - Whether widget is already installed
|
||||
---@param on_install function - Callback for install button
|
||||
---@return userdata - UI component
|
||||
function M.create_widget_item(item, is_installed, on_install)
|
||||
local size_text = item.size and format_size(item.size) or "Unknown size"
|
||||
local version_text = item.version and "v" .. item.version or "Unknown version"
|
||||
|
||||
-- Create tags display
|
||||
local tags_text = ""
|
||||
if item.tags and #item.tags > 0 then
|
||||
tags_text = "Tags: " .. table.concat(item.tags, ", ")
|
||||
end
|
||||
|
||||
-- Create dependencies display
|
||||
local deps_text = ""
|
||||
if item.depends and #item.depends > 0 then
|
||||
deps_text = "Depends: " .. table.concat(item.depends, ", ")
|
||||
end
|
||||
|
||||
local widget_details_children = {
|
||||
editor.ui.horizontal({
|
||||
spacing = editor.ui.SPACING.SMALL,
|
||||
children = {
|
||||
editor.ui.label({
|
||||
text = item.title or item.id,
|
||||
color = editor.ui.COLOR.OVERRIDE
|
||||
}),
|
||||
editor.ui.label({
|
||||
text = version_text,
|
||||
color = editor.ui.COLOR.WARNING
|
||||
}),
|
||||
editor.ui.label({
|
||||
text = "• " .. size_text,
|
||||
color = editor.ui.COLOR.HINT
|
||||
}),
|
||||
}
|
||||
}),
|
||||
|
||||
-- Description
|
||||
editor.ui.paragraph({
|
||||
text = item.description or "No description available",
|
||||
color = editor.ui.COLOR.TEXT
|
||||
}),
|
||||
}
|
||||
|
||||
if tags_text ~= "" then
|
||||
table.insert(widget_details_children, editor.ui.label({
|
||||
text = tags_text,
|
||||
color = editor.ui.COLOR.HINT
|
||||
}))
|
||||
end
|
||||
|
||||
if deps_text ~= "" then
|
||||
table.insert(widget_details_children, editor.ui.label({
|
||||
text = deps_text,
|
||||
color = editor.ui.COLOR.HINT
|
||||
}))
|
||||
end
|
||||
|
||||
if is_installed then
|
||||
table.insert(widget_details_children, editor.ui.label({
|
||||
text = "✓ Installed",
|
||||
color = editor.ui.COLOR.WARNING
|
||||
}))
|
||||
end
|
||||
|
||||
-- Create button row at the bottom
|
||||
local button_children = {
|
||||
editor.ui.button({
|
||||
text = "Install",
|
||||
on_pressed = on_install,
|
||||
enabled = not is_installed
|
||||
}),
|
||||
}
|
||||
|
||||
if item.api ~= nil then
|
||||
table.insert(button_children, editor.ui.button({
|
||||
text = "API",
|
||||
on_pressed = function() internal.open_url(item.api) end,
|
||||
enabled = item.api ~= nil
|
||||
}))
|
||||
end
|
||||
|
||||
if item.example_url ~= nil then
|
||||
table.insert(button_children, editor.ui.button({
|
||||
text = "Example",
|
||||
on_pressed = function() internal.open_url(item.example_url) end,
|
||||
enabled = item.example_url ~= nil
|
||||
}))
|
||||
end
|
||||
|
||||
-- Add spacer to push Author button to the right
|
||||
table.insert(button_children, editor.ui.horizontal({ grow = true }))
|
||||
|
||||
if item.author_url ~= nil then
|
||||
table.insert(button_children, editor.ui.label({
|
||||
text = "Author",
|
||||
color = editor.ui.COLOR.HINT
|
||||
}))
|
||||
table.insert(button_children, editor.ui.button({
|
||||
text = item.author or "Author",
|
||||
on_pressed = function() internal.open_url(item.author_url) end,
|
||||
enabled = item.author_url ~= nil
|
||||
}))
|
||||
end
|
||||
|
||||
-- Add button row to widget details
|
||||
table.insert(widget_details_children, editor.ui.horizontal({
|
||||
spacing = editor.ui.SPACING.SMALL,
|
||||
children = button_children
|
||||
}))
|
||||
|
||||
return editor.ui.horizontal({
|
||||
spacing = editor.ui.SPACING.NONE,
|
||||
padding = editor.ui.PADDING.SMALL,
|
||||
children = {
|
||||
-- Widget icon placeholder
|
||||
editor.ui.label({
|
||||
text = "•••",
|
||||
color = editor.ui.COLOR.HINT
|
||||
}),
|
||||
|
||||
-- Widget details
|
||||
editor.ui.vertical({
|
||||
spacing = editor.ui.SPACING.SMALL,
|
||||
grow = true,
|
||||
children = widget_details_children
|
||||
}),
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
|
||||
---Create a scrollable list of widget items
|
||||
---@param items table - List of widget items to display
|
||||
---@param on_install function - Callback for install button
|
||||
---@return userdata - UI component
|
||||
function M.create_widget_list(items, on_install)
|
||||
local widget_items = {}
|
||||
local install_folder = editor.prefs.get("druid.asset_install_folder") or installer.get_install_folder()
|
||||
|
||||
for _, item in ipairs(items) do
|
||||
local is_installed = installer.is_widget_installed(item, install_folder)
|
||||
table.insert(widget_items, M.create_widget_item(item, is_installed,
|
||||
function() on_install(item) end
|
||||
))
|
||||
end
|
||||
|
||||
return editor.ui.scroll({
|
||||
content = editor.ui.vertical({
|
||||
children = widget_items
|
||||
})
|
||||
})
|
||||
end
|
||||
|
||||
|
||||
return M
|
||||
@@ -3,6 +3,14 @@ local create_druid_widget = require("druid.editor_scripts.create_druid_widget")
|
||||
local create_druid_gui_script = require("druid.editor_scripts.create_druid_gui_script")
|
||||
local druid_settings = require("druid.editor_scripts.druid_settings")
|
||||
local asset_store = require("druid.editor_scripts.core.asset_store")
|
||||
-- Reuse tip: copy the snippet below into another editor script to open a custom store.
|
||||
-- asset_store.open({
|
||||
-- store_url = "https://example.com/store.json",
|
||||
-- info_url = "https://example.com/docs",
|
||||
-- title = "My Library Store",
|
||||
-- install_prefs_key = "my_lib.asset_install_folder",
|
||||
-- default_install_folder = "/my_widgets",
|
||||
-- })
|
||||
|
||||
local M = {}
|
||||
|
||||
@@ -75,7 +83,13 @@ function M.get_commands()
|
||||
label = "[Druid] Asset Store",
|
||||
locations = { "Edit" },
|
||||
run = function()
|
||||
return asset_store.open_asset_store("https://insality.github.io/core/druid_widget_store.json")
|
||||
return asset_store.open({
|
||||
store_url = "https://insality.github.io/core/druid_widget_store.json",
|
||||
info_url = "https://github.com/Insality/core/blob/main/druid_widget_store.md",
|
||||
title = "Druid Asset Store",
|
||||
install_prefs_key = "druid.asset_install_folder",
|
||||
default_install_folder = DEFAULT_ASSET_INSTALL_FOLDER,
|
||||
})
|
||||
end
|
||||
},
|
||||
|
||||
|
||||
Reference in New Issue
Block a user