Move core to sep repo

This commit is contained in:
Insality
2025-11-15 20:42:10 +02:00
parent 5f2d4bd8dc
commit 817387b426
13 changed files with 1 additions and 1293 deletions

View File

@@ -1,253 +0,0 @@
--- Main asset store module for Druid widgets
--- Handles fetching widget data, displaying the store interface, and managing installations
local installer = require("druid.editor_scripts.core.asset_store.installer")
local internal = require("druid.editor_scripts.core.asset_store.asset_store_internal")
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 = {}
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"
}
local function normalize_config(input)
if type(input) == "string" then
input = { store_url = input }
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")
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
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
---@param all_items table - List of all widgets for dependency resolution
---@param on_success function - Success callback
---@param on_error function - Error callback
local function handle_install(item, install_folder, all_items, on_success, on_error)
print("Installing widget:", item.id)
local success, message = installer.install_widget(item, install_folder, all_items)
if success then
print("Installation successful:", message)
on_success(message)
else
print("Installation failed:", message)
on_error(message)
end
end
function M.open(config_input)
local config = normalize_config(config_input)
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 store items:", fetch_error)
return
end
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)
local all_items = editor.ui.use_state(initial_items)
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("")
local authors = editor.ui.use_memo(internal.extract_authors, all_items)
local tags = editor.ui.use_memo(internal.extract_tags, all_items)
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)
local filtered_items = editor.ui.use_memo(
internal.filter_items_by_filters,
all_items,
search_query,
filter_type,
filter_author,
filter_tag,
install_folder
)
local function on_install(item)
handle_install(item, install_folder, all_items,
function(message)
set_install_status("Success: " .. message)
end,
function(message)
set_install_status("Error: " .. message)
end
)
end
local content_children = {}
table.insert(content_children, settings_ui.create({
install_folder = install_folder,
on_install_folder_changed = function(new_folder)
set_install_folder(new_folder)
persist_install_folder(config, new_folder)
end,
labels = config.labels.settings
}))
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,
}))
table.insert(content_children, search_ui.create({
search_query = search_query,
on_search = set_search_query,
labels = config.labels.search,
}))
if #filtered_items == 0 then
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, 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
if install_status ~= "" then
table.insert(content_children, editor.ui.label({
text = install_status,
color = install_status:find("Success") and editor.ui.COLOR.TEXT or editor.ui.COLOR.ERROR,
alignment = editor.ui.ALIGNMENT.CENTER
}))
end
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_RESULT then
if config.info_action then
config.info_action()
elseif config.info_url then
internal.open_url(config.info_url)
end
end
return {}
end
return M

View File

@@ -1,196 +0,0 @@
local installer = require("druid.editor_scripts.core.asset_store.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

View File

@@ -1,35 +0,0 @@
-- base64 encode/decode (http://lua-users.org/wiki/BaseSixtyFour)
local M = {}
local b='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
function M.encode(data)
return ((data:gsub('.', function(x)
local r,byte_val='',x:byte()
for i=8,1,-1 do r=r..(byte_val%2^i-byte_val%2^(i-1)>0 and '1' or '0') end
return r;
end)..'0000'):gsub('%d%d%d?%d?%d?%d?', function(x)
if (#x < 6) then return '' end
local c=0
for i=1,6 do c=c+(x:sub(i,i)=='1' and 2^(6-i) or 0) end
return b:sub(c+1,c+1)
end)..({ '', '==', '=' })[#data%3+1])
end
function M.decode(data)
data = string.gsub(data, '[^'..b..'=]', '')
return (data:gsub('.', function(x)
if (x == '=') then return '' end
local r,f='',(b:find(x)-1)
for i=6,1,-1 do r=r..(f%2^i-f%2^(i-1)>0 and '1' or '0') end
return r;
end):gsub('%d%d%d?%d?%d?%d?%d?%d?', function(x)
if (#x ~= 8) then return '' end
local c=0
for i=1,8 do c=c+(x:sub(i,i)=='1' and 2^(8-i) or 0) end
return string.char(c)
end))
end
return M

View File

@@ -1,224 +0,0 @@
--- Module for handling widget installation from zip files
--- Downloads zip files and extracts them to the specified folder
local base64 = require("druid.editor_scripts.core.asset_store.base64")
local path_replacer = require("druid.editor_scripts.core.asset_store.path_replacer")
local M = {}
---@class druid.core.item_info
---@field id string
---@field version string
---@field title string
---@field author string
---@field description string
---@field api string
---@field author_url string
---@field image string
---@field manifest_url string
---@field zip_url string
---@field json_zip_url string
---@field sha256 string
---@field size number
---@field depends string[]
---@field tags string[]
---Download a file from URL
---@param url string - The URL to download from
---@return string|nil, string|nil, table|nil - Downloaded content or nil, filename or nil, content list or nil
local function download_file_zip_json(url)
local response = http.request(url, { as = "json" })
if response.status ~= 200 then
print("Failed to download file. HTTP status: " .. response.status)
return nil
end
local data = response.body
local content_list = data.content -- Array of file paths from zip
return base64.decode(data.data), data.filename, content_list
end
---Find widget by dependency string (format: "author:widget_id@version" or "author@widget_id" or "widget_id")
---@param dep_string string - Dependency string
---@param all_items table - List of all available widgets
---@return table|nil - Found widget item or nil
local function find_widget_by_dependency(dep_string, all_items)
if not dep_string or not all_items then
return nil
end
local author, widget_id
-- Try format: "author:widget_id@version" (e.g., "Insality:mini_graph@1")
author, widget_id = dep_string:match("^([^:]+):([^@]+)@")
if not author then
-- Try format: "author@widget_id" (e.g., "insality@mini_graph")
author, widget_id = dep_string:match("^([^@]+)@(.+)$")
if not author then
-- No author specified, search by widget_id only
widget_id = dep_string
end
end
for _, item in ipairs(all_items) do
if item.id == widget_id then
-- If author was specified, check it matches (case-insensitive)
if not author or string.lower(item.author or "") == string.lower(author) then
return item
end
end
end
return nil
end
---Install widget dependencies recursively
---@param item druid.core.item_info - Widget item
---@param all_items table - List of all available widgets
---@param install_folder string - Installation folder
---@param installing_set table - Set of widget IDs currently being installed (to prevent cycles)
---@return boolean, string|nil - Success status and message
local function install_dependencies(item, all_items, install_folder, installing_set)
if not item.depends or #item.depends == 0 then
return true, nil
end
installing_set = installing_set or {}
for _, dep_string in ipairs(item.depends) do
local dep_item = find_widget_by_dependency(dep_string, all_items)
if not dep_item then
print("Warning: Dependency not found:", dep_string)
-- Continue with other dependencies
else
-- Check if already installed
if M.is_widget_installed(dep_item, install_folder) then
print("Dependency already installed:", dep_item.id)
else
-- Check for circular dependencies
if installing_set[dep_item.id] then
print("Warning: Circular dependency detected:", dep_item.id)
-- Continue with other dependencies
else
print("Installing dependency:", dep_item.id)
local success, err = M.install_widget(dep_item, install_folder, all_items, installing_set)
if not success then
return false, "Failed to install dependency " .. dep_item.id .. ": " .. (err or "unknown error")
end
end
end
end
end
return true, nil
end
---Install a widget from a zip URL
---@param item druid.core.item_info - Widget item data containing zip_url and id
---@param install_folder string - Target folder to install to
---@param all_items table|nil - Optional list of all widgets for dependency resolution
---@param installing_set table|nil - Optional set of widget IDs currently being installed (to prevent cycles)
---@return boolean, string - Success status and message
function M.install_widget(item, install_folder, all_items, installing_set)
if not item.json_zip_url or not item.id then
return false, "Invalid widget data: missing json_zip_url or id"
end
-- Install dependencies first if all_items is provided
if all_items then
installing_set = installing_set or {}
if installing_set[item.id] then
return false, "Circular dependency detected: " .. item.id
end
installing_set[item.id] = true
local dep_success, dep_err = install_dependencies(item, all_items, install_folder, installing_set)
if not dep_success then
installing_set[item.id] = nil
return false, dep_err or "Failed to install dependencies"
end
end
-- Download the zip file
local zip_data, filename, content_list = download_file_zip_json(item.json_zip_url)
if not zip_data or not filename then
if installing_set then
installing_set[item.id] = nil
end
return false, "Failed to download widget: " .. (filename or "unknown error")
end
if content_list then
print("Got file list from JSON:", #content_list, "files")
else
print("Warning: No content list in JSON data")
end
local zip_file_path = "." .. install_folder .. "/" .. filename
local zip_file = io.open(zip_file_path, "wb")
if not zip_file then
if installing_set then
installing_set[item.id] = nil
end
print("Directory does not exist: " .. install_folder)
print("Please create the directory manually and try again.")
return false, "Directory does not exist: " .. install_folder
end
zip_file:write(zip_data)
zip_file:close()
print("Zip written to file: " .. zip_file_path)
-- Unzip the zip file
local folder_path = "." .. install_folder .. "/" .. item.id
zip.unpack(zip_file_path, folder_path)
print("Widget unpacked successfully")
-- Remove the zip file
os.remove(zip_file_path)
print("Zip file removed successfully")
-- Process paths within the extracted widget
if content_list and #content_list > 0 then
local success, err = path_replacer.process_widget_paths(folder_path, install_folder, item.id, item.author, content_list)
if not success then
print("Warning: Path replacement failed:", err)
-- Don't fail installation if path replacement fails, just warn
end
else
print("Warning: No file list available, skipping path replacement")
end
if installing_set then
installing_set[item.id] = nil
end
return true, "Widget installed successfully"
end
---Check if a widget is already installed
---@param item table - Widget item data containing id
---@param install_folder string - Install folder to check in
---@return boolean - True if widget is already installed
function M.is_widget_installed(item, install_folder)
local p = editor.resource_attributes(install_folder .. "/" .. item.id)
return p.exists
end
---Get installation folder
---@return string - Installation folder path
function M.get_install_folder()
return editor.prefs.get("druid.asset_install_folder")
end
return M

View File

@@ -1,121 +0,0 @@
--- Module for replacing widget paths in installed files
--- Handles path replacement from original widget structure to user's installation path
local system = require("druid.editor_scripts.defold_parser.system.parser_internal")
local M = {}
---Replace paths in file content
---@param content string - File content
---@param author string - Author name (e.g., "Insality")
---@param install_folder string - Installation folder (e.g., "widget")
---@return string - Modified content
local function replace_paths_in_content(content, author, install_folder)
if not content or not author then
return content
end
-- Escape special characters for literal string replacement
local function escape_pattern(str)
return str:gsub("[%^%$%(%)%%%.%[%]%*%+%-%?]", "%%%0")
end
-- Remove leading / from install_folder if present
local clean_install_folder = install_folder
if clean_install_folder:sub(1, 1) == "/" then
clean_install_folder = clean_install_folder:sub(2)
end
-- Replace all paths with author: widget/Insality/* -> widget/*
local author_path_pattern = escape_pattern(clean_install_folder .. "/" .. author .. "/")
local target_path_prefix = clean_install_folder .. "/"
content = content:gsub(author_path_pattern, target_path_prefix)
-- Replace all require statements with dots: widget.Insality.* -> widget.*
local author_dots_pattern = escape_pattern(clean_install_folder .. "." .. author .. ".")
local target_dots_prefix = clean_install_folder .. "."
content = content:gsub(author_dots_pattern, target_dots_prefix)
-- Also replace paths that start with author directly: Insality/widget -> widget
-- But only if they're in require statements or paths
local author_start_pattern = escape_pattern(author .. "/")
content = content:gsub(author_start_pattern, "")
local author_start_dots_pattern = escape_pattern(author .. ".")
content = content:gsub(author_start_dots_pattern, "")
return content
end
---Process widget paths in all files
---@param folder_path string - Path to the unpacked widget folder
---@param install_folder string - Installation folder (e.g., "widget")
---@param widget_id string - Widget ID (e.g., "fps_panel")
---@param author string|nil - Author name (e.g., "Insality")
---@param file_list table - Optional list of file paths from zip content
---@return boolean, string|nil - Success status and error message if any
function M.process_widget_paths(folder_path, install_folder, widget_id, author, file_list)
print("Processing widget paths in:", folder_path)
if not author then
print("Warning: Missing author, skipping path replacement")
return true, nil
end
print("Replacing all paths with author:", author, "in install folder:", install_folder)
-- Get absolute project path
local absolute_project_path = editor.external_file_attributes(".").path
if not absolute_project_path:match("[\\/]$") then
absolute_project_path = absolute_project_path .. "/"
end
-- Clean folder_path
local clean_folder_path = folder_path
if clean_folder_path:sub(1, 1) == "." then
clean_folder_path = clean_folder_path:sub(2)
end
if clean_folder_path:sub(1, 1) == "/" then
clean_folder_path = clean_folder_path:sub(2)
end
-- Process each file from the list
local processed_count = 0
for _, file_path_in_zip in ipairs(file_list) do
-- Build full path to the file after unpacking
local file_path = clean_folder_path .. "/" .. file_path_in_zip
-- Get absolute path
local clean_file_path = file_path
if clean_file_path:sub(1, 1) == "/" then
clean_file_path = clean_file_path:sub(2)
end
local absolute_file_path = absolute_project_path .. clean_file_path
-- Read file content
local content, err = system.read_file(absolute_file_path)
if not content then
print("Warning: Could not read file:", file_path, err)
else
-- Replace all paths with author
local modified_content = replace_paths_in_content(content, author, install_folder)
if modified_content ~= content then
-- Write modified content back
local success, write_err = system.write_file(absolute_file_path, modified_content)
if success then
processed_count = processed_count + 1
print("Processed:", file_path)
else
print("Warning: Could not write file:", file_path, write_err)
end
end
end
end
print("Path replacement complete. Processed", processed_count, "files")
return true, nil
end
return M

View File

@@ -1,19 +0,0 @@
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

View File

@@ -1,119 +0,0 @@
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

View File

@@ -1,49 +0,0 @@
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

View File

@@ -1,49 +0,0 @@
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

View File

@@ -1,165 +0,0 @@
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

View File

@@ -1,48 +0,0 @@
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

View File

@@ -2,7 +2,6 @@ local assign_layers = require("druid.editor_scripts.assign_layers")
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",
@@ -79,20 +78,6 @@ function M.get_commands()
end
},
{
label = "[Druid] Asset Store",
locations = { "Edit" },
run = function()
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
},
{
label = "[Druid] Settings",
locations = { "Edit" },