diff --git a/druid/editor_scripts/core/asset_store.lua b/druid/editor_scripts/core/asset_store.lua deleted file mode 100644 index 300a5cc..0000000 --- a/druid/editor_scripts/core/asset_store.lua +++ /dev/null @@ -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 diff --git a/druid/editor_scripts/core/asset_store/asset_store_internal.lua b/druid/editor_scripts/core/asset_store/asset_store_internal.lua deleted file mode 100644 index 068eb6f..0000000 --- a/druid/editor_scripts/core/asset_store/asset_store_internal.lua +++ /dev/null @@ -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 - diff --git a/druid/editor_scripts/core/asset_store/base64.lua b/druid/editor_scripts/core/asset_store/base64.lua deleted file mode 100644 index ebc174f..0000000 --- a/druid/editor_scripts/core/asset_store/base64.lua +++ /dev/null @@ -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 diff --git a/druid/editor_scripts/core/asset_store/installer.lua b/druid/editor_scripts/core/asset_store/installer.lua deleted file mode 100644 index 910575c..0000000 --- a/druid/editor_scripts/core/asset_store/installer.lua +++ /dev/null @@ -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 diff --git a/druid/editor_scripts/core/asset_store/path_replacer.lua b/druid/editor_scripts/core/asset_store/path_replacer.lua deleted file mode 100644 index 791020c..0000000 --- a/druid/editor_scripts/core/asset_store/path_replacer.lua +++ /dev/null @@ -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 diff --git a/druid/editor_scripts/core/asset_store/ui/dialog.lua b/druid/editor_scripts/core/asset_store/ui/dialog.lua deleted file mode 100644 index 7b6dfac..0000000 --- a/druid/editor_scripts/core/asset_store/ui/dialog.lua +++ /dev/null @@ -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 - diff --git a/druid/editor_scripts/core/asset_store/ui/filters.lua b/druid/editor_scripts/core/asset_store/ui/filters.lua deleted file mode 100644 index efa750d..0000000 --- a/druid/editor_scripts/core/asset_store/ui/filters.lua +++ /dev/null @@ -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 - diff --git a/druid/editor_scripts/core/asset_store/ui/search.lua b/druid/editor_scripts/core/asset_store/ui/search.lua deleted file mode 100644 index 223b0d2..0000000 --- a/druid/editor_scripts/core/asset_store/ui/search.lua +++ /dev/null @@ -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 - diff --git a/druid/editor_scripts/core/asset_store/ui/settings.lua b/druid/editor_scripts/core/asset_store/ui/settings.lua deleted file mode 100644 index 6731f52..0000000 --- a/druid/editor_scripts/core/asset_store/ui/settings.lua +++ /dev/null @@ -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 - diff --git a/druid/editor_scripts/core/asset_store/ui/widget_card.lua b/druid/editor_scripts/core/asset_store/ui/widget_card.lua deleted file mode 100644 index b6b5ac6..0000000 --- a/druid/editor_scripts/core/asset_store/ui/widget_card.lua +++ /dev/null @@ -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 diff --git a/druid/editor_scripts/core/asset_store/ui/widget_list.lua b/druid/editor_scripts/core/asset_store/ui/widget_list.lua deleted file mode 100644 index f6a570b..0000000 --- a/druid/editor_scripts/core/asset_store/ui/widget_list.lua +++ /dev/null @@ -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 diff --git a/druid/editor_scripts/druid.editor_script b/druid/editor_scripts/druid.editor_script index 2431208..bff6596 100644 --- a/druid/editor_scripts/druid.editor_script +++ b/druid/editor_scripts/druid.editor_script @@ -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" }, diff --git a/game.project b/game.project index 2a3093d..6d0bd17 100644 --- a/game.project +++ b/game.project @@ -25,6 +25,7 @@ dependencies#3 = https://github.com/Insality/panthera/archive/refs/tags/runtime. dependencies#4 = https://github.com/Insality/defold-lang/archive/refs/tags/3.zip dependencies#5 = https://github.com/Insality/defold-event/archive/refs/tags/13.zip dependencies#6 = https://github.com/subsoap/defos/archive/refs/tags/v2.8.0.zip +dependencies#7 = https://github.com/Insality/core/archive/refs/heads/main.zip [library] include_dirs = druid