From 87dabbe8b0ec3b5eacea05aa2389d091a7cf1f0f Mon Sep 17 00:00:00 2001 From: mojie126 Date: Sat, 22 Aug 2026 02:01:55 +0800 Subject: [PATCH 1/4] Toolbox install: report progress and explain an empty install list Fixes #43 - Call aegisub.progress.task before crawling feeds and before loading feed data, so the background script progress dialog no longer sits blank for the whole crawl. - When nothing new can be installed, show a notice dialog instead of the script list dialog with only a Cancel button. --- macros/l0.DependencyControl.Toolbox.moon | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/macros/l0.DependencyControl.Toolbox.moon b/macros/l0.DependencyControl.Toolbox.moon index 3c7b2f6..740afa2 100644 --- a/macros/l0.DependencyControl.Toolbox.moon +++ b/macros/l0.DependencyControl.Toolbox.moon @@ -26,6 +26,9 @@ logger.usePrefixWindow = false msgs = { install: { scanning: "Scanning %d available feeds...", + scanningTask: "Scanning available feeds..." + loadingTask: "Loading feed data..." + empty: "All available scripts are already installed; nothing new to install." createScriptUpdateRecordFailed: "Failed to create an update record for %s '%s' from feed %s: %s" } uninstall: { @@ -261,8 +264,10 @@ install = -> -- FeedInventory crawls the known feeds, which are trust-gated and bounded. The shared feed loader then -- serves each reachable feed's data from the cache the crawl just populated. macros, modules = {}, {} + aegisub.progress.task msgs.install.scanningTask entries = crawlWithPrompt buildFeedInventory! + aegisub.progress.task msgs.install.loadingTask logger\log msgs.install.scanning, #entries for entry in *entries continue unless entry.fetched @@ -270,6 +275,11 @@ install = -> continue unless feed.data addAvailableToInstall macros, modules, feed + unless next(modules) or next(macros) + aegisub.dialog.display {{class: "label", x: 0, y: 0, width: 1, height: 1, label: msgs.install.empty}}, + {buttons.close}, {ok: buttons.close, cancel: buttons.close} + return + moduleList, moduleMap = buildDlgList modules macroList, macroMap = buildDlgList macros From 0b2d72a67f1a90ae160a4d155e95e8581c1c44cc Mon Sep 17 00:00:00 2001 From: mojie126 Date: Sat, 22 Aug 2026 06:26:27 +0800 Subject: [PATCH 2/4] Address review: check the built dialog lists, not the namespace tables addAvailableToInstall creates a (possibly empty) table for every known package regardless of install state, so next(modules)/next(macros) is almost always truthy and the empty notice could never fire. Check the flattened moduleList/macroList lengths instead, and when some feeds weren't fetched say so, since that may be why there is nothing to install. Also drop the redundant second clause of the empty message. --- macros/l0.DependencyControl.Toolbox.moon | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/macros/l0.DependencyControl.Toolbox.moon b/macros/l0.DependencyControl.Toolbox.moon index 740afa2..be637e7 100644 --- a/macros/l0.DependencyControl.Toolbox.moon +++ b/macros/l0.DependencyControl.Toolbox.moon @@ -28,7 +28,8 @@ msgs = { scanning: "Scanning %d available feeds...", scanningTask: "Scanning available feeds..." loadingTask: "Loading feed data..." - empty: "All available scripts are already installed; nothing new to install." + empty: "All available scripts are already installed." + emptyUnfetched: "No scripts are available to install (%d of %d known feeds could not be fetched)." createScriptUpdateRecordFailed: "Failed to create an update record for %s '%s' from feed %s: %s" } uninstall: { @@ -275,14 +276,16 @@ install = -> continue unless feed.data addAvailableToInstall macros, modules, feed - unless next(modules) or next(macros) - aegisub.dialog.display {{class: "label", x: 0, y: 0, width: 1, height: 1, label: msgs.install.empty}}, - {buttons.close}, {ok: buttons.close, cancel: buttons.close} - return - moduleList, moduleMap = buildDlgList modules macroList, macroMap = buildDlgList macros + if #moduleList == 0 and #macroList == 0 + unfetched = #[entry for entry in *entries when not entry.fetched] + message = unfetched > 0 and msgs.install.emptyUnfetched\format(unfetched, #entries) or msgs.install.empty + aegisub.dialog.display {{class: "label", x: 0, y: 0, width: 1, height: 1, label: message}}, + {buttons.close}, {ok: buttons.close, cancel: buttons.close} + return + btn, res = aegisub.dialog.display getScriptListDlg macroList, moduleList return unless btn From ff02a113c8c75cbbf1508c9a279879e9d5782c71 Mon Sep 17 00:00:00 2001 From: mojie126 Date: Sat, 22 Aug 2026 07:07:04 +0800 Subject: [PATCH 3/4] FeedInventory: optional onProgress callback reporting crawl progress Called after each feed is fetched, with the running fetch count and the number of feeds currently known to the crawl. The crawl discovers more feeds as it goes, so the known count may grow over time. --- modules/l0/DependencyControl/FeedInventory.moon | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/modules/l0/DependencyControl/FeedInventory.moon b/modules/l0/DependencyControl/FeedInventory.moon index b04ba76..912f141 100644 --- a/modules/l0/DependencyControl/FeedInventory.moon +++ b/modules/l0/DependencyControl/FeedInventory.moon @@ -242,11 +242,12 @@ class FeedInventory ---Fetches feeds and discovers transitively-advertised ones by crawling the `knownFeeds` graph out from the ---config-derived feeds; untrusted expansion is bounded, so check `stats.truncated` for incomplete results. + ---@param onProgress? fun(fetched: integer, known: integer) Called after each feed is fetched, with the running fetch count and the number of feeds currently known to the crawl (more are discovered as it goes, so `known` may grow). ---@return FeedInventoryEntry[] feeds The known feeds, enriched with what the crawl discovered. ---@return FeedCrawlStats stats What the crawl explored and where it stopped short. - crawl: => + crawl: (onProgress) => inventoryEntriesByUrl = @__collectConfigFeeds! - stats = @__crawlKnownFeeds inventoryEntriesByUrl + stats = @__crawlKnownFeeds inventoryEntriesByUrl, onProgress return @__finalize(inventoryEntriesByUrl), stats ---Breadth-first crawl of the `knownFeeds` graph, extending inventoryEntriesByUrl in place with the transitively-discovered @@ -254,8 +255,9 @@ class FeedInventory ---malicious subtree can't starve the others. ---@private ---@param inventoryEntriesByUrl table The config-derived feeds to start from; extended in place. + ---@param onProgress? fun(fetched: integer, known: integer) Called after each feed is fetched (see `crawl`). ---@return FeedCrawlStats stats - __crawlKnownFeeds: (inventoryEntriesByUrl) => + __crawlKnownFeeds: (inventoryEntriesByUrl, onProgress) => c = @config.c limits = c.feeds.crawlLimits or {} defaults = @@defaultCrawlLimits @@ -300,6 +302,7 @@ class FeedInventory continue unless knownFeeds stats.fetched += 1 inventoryEntriesByUrl[feedUrl].fetched = true + onProgress stats.fetched, #queue - head + 1 + stats.fetched if onProgress perFeedUntrusted = 0 perFeedDrops, perRootDrops = newDrops!, newDrops! From 8cbd650c9f61834cc73b2c1fe0b4df4a8f59c286 Mon Sep 17 00:00:00 2001 From: mojie126 Date: Sat, 22 Aug 2026 07:07:06 +0800 Subject: [PATCH 4/4] Toolbox install: moving progress bar and cancellation during the crawl Drive aegisub.progress.set from the new crawl callback so the scan shows actual progress, and abort the crawl when the user cancels: the callback raises, crawlWithPrompt traps it (resetting the prompter), re-raises real errors, and install returns quietly on cancellation. --- macros/l0.DependencyControl.Toolbox.moon | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/macros/l0.DependencyControl.Toolbox.moon b/macros/l0.DependencyControl.Toolbox.moon index be637e7..fd43203 100644 --- a/macros/l0.DependencyControl.Toolbox.moon +++ b/macros/l0.DependencyControl.Toolbox.moon @@ -222,11 +222,16 @@ promptUntrustedFeed = (url, ft) -> -- Crawls the feed inventory with the untrusted-feed prompter active (so the `prompt` policy asks), scoped -- so the prompter never leaks into background fetches. Shared by install discovery and Manage Feeds. -crawlWithPrompt = (inventory) -> +-- The pcall keeps a cancellation raised from onProgress from skipping the prompter reset; +-- returns nil when the crawl was cancelled and re-raises anything else. +crawlWithPrompt = (inventory, onProgress) -> feedTrust = DepCtrl.updater.feedTrust feedTrust\setPrompter promptUntrustedFeed - entries = inventory\crawl! + ok, entries = pcall inventory.crawl, inventory, onProgress feedTrust\setPrompter nil + if not ok and entries != "cancelled" + error entries, 0 + return nil unless ok entries -- Macros @@ -266,7 +271,11 @@ install = -> -- serves each reachable feed's data from the cache the crawl just populated. macros, modules = {}, {} aegisub.progress.task msgs.install.scanningTask - entries = crawlWithPrompt buildFeedInventory! + entries = crawlWithPrompt buildFeedInventory!, (fetched, known) -> + aegisub.progress.set math.floor fetched * 100 / known + error "cancelled", 0 if aegisub.progress.is_cancelled! + aegisub.progress.set 100 + return unless entries aegisub.progress.task msgs.install.loadingTask logger\log msgs.install.scanning, #entries