This commit is contained in:
Insality
2025-10-25 22:47:09 +03:00
parent 1f0075d124
commit 9fba1899da
4 changed files with 245 additions and 221 deletions

View File

@@ -134,7 +134,7 @@ function M.open_asset_store()
end end
local dialog_component = editor.ui.component(function(props) local dialog_component = editor.ui.component(function(props)
editor.prefs.set("druid.asset_install_folder", "./widget") editor.prefs.set("druid.asset_install_folder", "/widget")
-- State management -- State management
local items, set_items = editor.ui.use_state(initial_items) local items, set_items = editor.ui.use_state(initial_items)
local loading, set_loading = editor.ui.use_state(initial_loading) local loading, set_loading = editor.ui.use_state(initial_loading)

View File

@@ -1,10 +1,11 @@
local base64 = require("druid.editor_scripts.core.base64") local base64 = require("druid.editor_scripts.core.base64")
local path_replacer = require("druid.editor_scripts.core.path_replacer")
--- Module for handling widget installation from zip files --- Module for handling widget installation from zip files
--- Downloads zip files and extracts them to the specified folder --- Downloads zip files and extracts them to the specified folder
local M = {} local M = {}
local DEFAULT_INSTALL_FOLDER = "./widget" local DEFAULT_INSTALL_FOLDER = "/widget"
---@class druid.core.item_info ---@class druid.core.item_info
---@field id string ---@field id string
@@ -57,7 +58,7 @@ function M.install_widget(item, install_folder)
end end
local zip_file_path = install_folder .. "/" .. filename local zip_file_path = "." .. install_folder .. "/" .. filename
local zip_file = io.open(zip_file_path, "wb") local zip_file = io.open(zip_file_path, "wb")
if not zip_file then if not zip_file then
return false, "Failed to open zip file: " .. zip_file_path return false, "Failed to open zip file: " .. zip_file_path
@@ -68,14 +69,23 @@ function M.install_widget(item, install_folder)
print("Zip written to file: " .. zip_file_path) print("Zip written to file: " .. zip_file_path)
-- Unzip the zip file -- Unzip the zip file
local folder_name = item.id .. "-" .. item.version local folder_path = "." .. install_folder .. "/" .. item.id
zip.unpack(zip_file_path, install_folder .. "/" .. folder_name)
zip.unpack(zip_file_path, folder_path)
print("Widget unpacked successfully") print("Widget unpacked successfully")
-- Remove the zip file -- Remove the zip file
os.remove(zip_file_path) os.remove(zip_file_path)
print("Zip file removed successfully") print("Zip file removed successfully")
-- Process paths within the extracted widget
local files_in_folder = path_replacer.get_all_files(folder_path)
pprint(files_in_folder)
--if not path_replacer.process_widget_paths(install_folder .. "/" .. folder_name, new_base_path) then
-- return false, "Failed to process widget paths"
--end
return true, "Widget installed successfully" return true, "Widget installed successfully"
end end

View File

@@ -0,0 +1,22 @@
--- Module for replacing paths in extracted widget files
--- Handles updating require statements and file paths to match the installation location
local M = {}
---Recursively get all files in a directory
---@param dir_path string - Directory path to scan
---@param extension string|nil - Optional file extension filter (e.g., ".lua")
---@return string[] - List of file paths
function M.get_all_files(dir_path, extension)
local attributes = editor.resource_attributes(dir_path)
local files = io.popen("ls -R " .. dir_path)
for line in files:lines() do
print(line)
end
pprint(files)
return files
end
return M

View File

@@ -9,20 +9,20 @@ local M = {}
---@param on_change function - Callback when path changes ---@param on_change function - Callback when path changes
---@return userdata - UI component ---@return userdata - UI component
function M.create_settings_section(install_path, on_change) function M.create_settings_section(install_path, on_change)
return editor.ui.vertical({ return editor.ui.vertical({
spacing = editor.ui.SPACING.SMALL, spacing = editor.ui.SPACING.SMALL,
children = { children = {
editor.ui.label({ editor.ui.label({
text = "Installation Folder:", text = "Installation Folder:",
color = editor.ui.COLOR.TEXT color = editor.ui.COLOR.TEXT
}), }),
editor.ui.label({ editor.ui.label({
text = install_path, text = install_path,
color = editor.ui.COLOR.TEXT, color = editor.ui.COLOR.TEXT,
grow = true grow = true
}) })
} }
}) })
end end
@@ -30,18 +30,18 @@ end
---@param items table - List of widget items ---@param items table - List of widget items
---@return table - Sorted list of unique authors ---@return table - Sorted list of unique authors
local function extract_authors(items) local function extract_authors(items)
local authors = {} local authors = {}
local author_set = {} local author_set = {}
for _, item in ipairs(items) do for _, item in ipairs(items) do
if item.author and not author_set[item.author] then if item.author and not author_set[item.author] then
author_set[item.author] = true author_set[item.author] = true
table.insert(authors, item.author) table.insert(authors, item.author)
end end
end end
table.sort(authors) table.sort(authors)
return authors return authors
end end
@@ -49,22 +49,22 @@ end
---@param items table - List of widget items ---@param items table - List of widget items
---@return table - Sorted list of unique tags ---@return table - Sorted list of unique tags
local function extract_tags(items) local function extract_tags(items)
local tags = {} local tags = {}
local tag_set = {} local tag_set = {}
for _, item in ipairs(items) do for _, item in ipairs(items) do
if item.tags then if item.tags then
for _, tag in ipairs(item.tags) do for _, tag in ipairs(item.tags) do
if not tag_set[tag] then if not tag_set[tag] then
tag_set[tag] = true tag_set[tag] = true
table.insert(tags, tag) table.insert(tags, tag)
end end
end end
end end
end end
table.sort(tags) table.sort(tags)
return tags return tags
end end
@@ -76,52 +76,52 @@ end
---@param on_tag_change function - Callback for tag filter change ---@param on_tag_change function - Callback for tag filter change
---@return userdata - UI component ---@return userdata - UI component
function M.create_filter_section(items, author_filter, tag_filter, on_author_change, on_tag_change) function M.create_filter_section(items, author_filter, tag_filter, on_author_change, on_tag_change)
local authors = extract_authors(items) local authors = extract_authors(items)
local tags = extract_tags(items) local tags = extract_tags(items)
-- Build author options -- Build author options
local author_options = {"All Authors"} local author_options = {"All Authors"}
for _, author in ipairs(authors) do for _, author in ipairs(authors) do
table.insert(author_options, author) table.insert(author_options, author)
end end
-- Build tag options -- Build tag options
local tag_options = {"All Categories"} local tag_options = {"All Categories"}
for _, tag in ipairs(tags) do for _, tag in ipairs(tags) do
table.insert(tag_options, tag) table.insert(tag_options, tag)
end end
return editor.ui.horizontal({ return editor.ui.horizontal({
spacing = editor.ui.SPACING.MEDIUM, spacing = editor.ui.SPACING.MEDIUM,
children = { children = {
editor.ui.vertical({ editor.ui.vertical({
spacing = editor.ui.SPACING.SMALL, spacing = editor.ui.SPACING.SMALL,
children = { children = {
editor.ui.label({ editor.ui.label({
text = "Author:", text = "Author:",
color = editor.ui.COLOR.TEXT color = editor.ui.COLOR.TEXT
}), }),
editor.ui.label({ editor.ui.label({
text = author_filter, text = author_filter,
color = editor.ui.COLOR.TEXT color = editor.ui.COLOR.TEXT
}) })
} }
}), }),
editor.ui.vertical({ editor.ui.vertical({
spacing = editor.ui.SPACING.SMALL, spacing = editor.ui.SPACING.SMALL,
children = { children = {
editor.ui.label({ editor.ui.label({
text = "Category:", text = "Category:",
color = editor.ui.COLOR.TEXT color = editor.ui.COLOR.TEXT
}), }),
editor.ui.label({ editor.ui.label({
text = tag_filter, text = tag_filter,
color = editor.ui.COLOR.TEXT color = editor.ui.COLOR.TEXT
}) })
} }
}) })
} }
}) })
end end
@@ -129,13 +129,13 @@ end
---@param size_bytes number - Size in bytes ---@param size_bytes number - Size in bytes
---@return string - Formatted size string ---@return string - Formatted size string
local function format_size(size_bytes) local function format_size(size_bytes)
if size_bytes < 1024 then if size_bytes < 1024 then
return size_bytes .. " B" return size_bytes .. " B"
elseif size_bytes < 1024 * 1024 then elseif size_bytes < 1024 * 1024 then
return math.floor(size_bytes / 1024) .. " KB" return math.floor(size_bytes / 1024) .. " KB"
else else
return math.floor(size_bytes / (1024 * 1024)) .. " MB" return math.floor(size_bytes / (1024 * 1024)) .. " MB"
end end
end end
@@ -146,101 +146,99 @@ end
---@param on_open_api function - Callback for API docs button ---@param on_open_api function - Callback for API docs button
---@return userdata - UI component ---@return userdata - UI component
function M.create_widget_item(item, is_installed, on_install, on_open_api) function M.create_widget_item(item, is_installed, on_install, on_open_api)
local size_text = item.size and format_size(item.size) or "Unknown size" 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" local version_text = item.version and "v" .. item.version or "Unknown version"
-- Create tags display -- Create tags display
local tags_text = "" local tags_text = ""
if item.tags and #item.tags > 0 then if item.tags and #item.tags > 0 then
tags_text = "Tags: " .. table.concat(item.tags, ", ") tags_text = "Tags: " .. table.concat(item.tags, ", ")
end end
-- Create dependencies display -- Create dependencies display
local deps_text = "" local deps_text = ""
if item.depends and #item.depends > 0 then if item.depends and #item.depends > 0 then
deps_text = "Depends on: " .. table.concat(item.depends, ", ") deps_text = "Depends on: " .. table.concat(item.depends, ", ")
end end
return editor.ui.horizontal({ return editor.ui.horizontal({
spacing = editor.ui.SPACING.MEDIUM, spacing = editor.ui.SPACING.MEDIUM,
padding = editor.ui.PADDING.MEDIUM, padding = editor.ui.PADDING.MEDIUM,
children = { children = {
-- Widget icon placeholder -- Widget icon placeholder
editor.ui.label({ editor.ui.label({
text = "📦", text = "📦",
color = editor.ui.COLOR.HINT color = editor.ui.COLOR.HINT
}), }),
-- Widget details -- Widget details
editor.ui.vertical({ editor.ui.vertical({
spacing = editor.ui.SPACING.SMALL, spacing = editor.ui.SPACING.SMALL,
grow = true, grow = true,
children = { children = {
-- Title and author -- Title and author
editor.ui.horizontal({ editor.ui.horizontal({
spacing = editor.ui.SPACING.SMALL, spacing = editor.ui.SPACING.SMALL,
children = { children = {
editor.ui.label({ editor.ui.label({
text = item.title or item.id, text = item.title or item.id,
color = editor.ui.COLOR.TEXT color = editor.ui.COLOR.TEXT
}), }),
editor.ui.label({ editor.ui.label({
text = "by " .. (item.author or "Unknown"), text = "by " .. (item.author or "Unknown"),
color = editor.ui.COLOR.HINT color = editor.ui.COLOR.HINT
}) }),
} editor.ui.label({
}), text = version_text .. "" .. size_text,
color = editor.ui.COLOR.HINT
}),
}
}),
-- Version and size -- Description
editor.ui.label({ editor.ui.label({
text = version_text .. "" .. size_text, text = item.description or "No description available",
color = editor.ui.COLOR.HINT color = editor.ui.COLOR.TEXT
}), }),
-- Description -- Tags
editor.ui.label({ tags_text ~= "" and editor.ui.label({
text = item.description or "No description available", text = tags_text,
color = editor.ui.COLOR.TEXT color = editor.ui.COLOR.HINT
}), }) or nil,
-- Tags -- Dependencies
tags_text ~= "" and editor.ui.label({ deps_text ~= "" and editor.ui.label({
text = tags_text, text = deps_text,
color = editor.ui.COLOR.HINT color = editor.ui.COLOR.WARNING
}) or nil, }) or nil,
-- Dependencies -- Installation status
deps_text ~= "" and editor.ui.label({ is_installed and editor.ui.label({
text = deps_text, text = "✓ Already installed",
color = editor.ui.COLOR.WARNING color = editor.ui.COLOR.HINT
}) or nil, }) or nil
}
}),
-- Installation status -- Action buttons
is_installed and editor.ui.label({ editor.ui.vertical({
text = "✓ Already installed", spacing = editor.ui.SPACING.SMALL,
color = editor.ui.COLOR.HINT children = {
}) or nil editor.ui.button({
} text = is_installed and "Reinstall" or "Install",
}), on_pressed = on_install,
enabled = true
-- Action buttons }),
editor.ui.vertical({ editor.ui.button({
spacing = editor.ui.SPACING.SMALL, text = "API",
children = { on_pressed = on_open_api,
editor.ui.button({ enabled = item.api ~= nil
text = is_installed and "Reinstall" or "Install", })
on_pressed = on_install, }
enabled = true })
}), }
editor.ui.button({ })
text = "API Docs",
on_pressed = on_open_api,
enabled = item.api ~= nil
})
}
})
}
})
end end
@@ -251,29 +249,23 @@ end
---@param on_open_api function - Callback for API docs button ---@param on_open_api function - Callback for API docs button
---@return userdata - UI component ---@return userdata - UI component
function M.create_widget_list(items, is_installed_func, on_install, on_open_api) function M.create_widget_list(items, is_installed_func, on_install, on_open_api)
local widget_items = {} local widget_items = {}
for _, item in ipairs(items) do for index = 1, 9 do
local is_installed = is_installed_func and is_installed_func(item) or false for _, item in ipairs(items) do
local is_installed = is_installed_func and is_installed_func(item) or false
table.insert(widget_items, M.create_widget_item(item, is_installed, table.insert(widget_items, M.create_widget_item(item, is_installed,
function() on_install(item) end, function() on_install(item) end,
function() on_open_api(item) end function() on_open_api(item) end
)) ))
end
end
-- Add separator between items (except for the last one) return editor.ui.vertical({
if _ < #items then spacing = editor.ui.SPACING.SMALL,
table.insert(widget_items, editor.ui.label({ children = widget_items
text = "---", })
color = editor.ui.COLOR.HINT
}))
end
end
return editor.ui.vertical({
spacing = editor.ui.SPACING.SMALL,
children = widget_items
})
end end
@@ -281,17 +273,17 @@ end
---@param message string - Loading message ---@param message string - Loading message
---@return userdata - UI component ---@return userdata - UI component
function M.create_loading_indicator(message) function M.create_loading_indicator(message)
return editor.ui.vertical({ return editor.ui.vertical({
spacing = editor.ui.SPACING.MEDIUM, spacing = editor.ui.SPACING.MEDIUM,
alignment = editor.ui.ALIGNMENT.CENTER, alignment = editor.ui.ALIGNMENT.CENTER,
children = { children = {
editor.ui.label({ editor.ui.label({
text = message or "Loading...", text = message or "Loading...",
color = editor.ui.COLOR.TEXT, color = editor.ui.COLOR.TEXT,
alignment = editor.ui.ALIGNMENT.CENTER alignment = editor.ui.ALIGNMENT.CENTER
}) })
} }
}) })
end end
@@ -299,17 +291,17 @@ end
---@param message string - Error message ---@param message string - Error message
---@return userdata - UI component ---@return userdata - UI component
function M.create_error_message(message) function M.create_error_message(message)
return editor.ui.vertical({ return editor.ui.vertical({
spacing = editor.ui.SPACING.MEDIUM, spacing = editor.ui.SPACING.MEDIUM,
alignment = editor.ui.ALIGNMENT.CENTER, alignment = editor.ui.ALIGNMENT.CENTER,
children = { children = {
editor.ui.label({ editor.ui.label({
text = "Error: " .. message, text = "Error: " .. message,
color = editor.ui.COLOR.ERROR, color = editor.ui.COLOR.ERROR,
alignment = editor.ui.ALIGNMENT.CENTER alignment = editor.ui.ALIGNMENT.CENTER
}) })
} }
}) })
end end