mirror of
https://github.com/TemujinCalidius/SurvivorCore.git
synced 2026-08-14 09:02:29 +00:00
Engine: quest overrides with objective*/reward* fields on nested code quests now WARN at boot (they merge nothing — QuestData prefers nested tables); EngineConfig + ConfigAdmin reject non-finite numbers (inf/nan). Plugin: Config group panels auto-size (fixed-height math clipped the last row of big groups — Theme group lost its Bold font row); Stats panels get the gap math right; the Engine Config explainer page now REBUILDS the window when the engine appears (the old hint was impossible — pages were assembled once at plugin load); create() refuses ids that already have an override (mirror guard); failed Create/Override reasons render under the create row; rejected config edits report in the footer; explicit navigation cancels a pending debounced search jump; the mount-time page restore no longer clobbers the saved last-page setting during an engine-less session; undo/redo refresh skips while typing in one of the plugin's own text boxes; search results past the 50-cap no longer render empty category headers. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
105 lines
2.8 KiB
Luau
105 lines
2.8 KiB
Luau
--!nonstrict
|
|
--[[
|
|
SearchPage — global content search results. A hidden (rail-less) page the plugin main routes
|
|
to while the rail search box has text. Matches ids + `name` attributes across every category
|
|
(ContentAdminUi.matchEntry) and renders full EDITABLE entry panels grouped by category — you
|
|
fix the thing right where you found it.
|
|
]]
|
|
|
|
local Theme = require(script.Parent.Theme)
|
|
|
|
local MAX_RESULTS = 50
|
|
|
|
local SearchPage = {}
|
|
|
|
-- deps = { ContentAdmin, ContentAdminUi, getQuery(), actions }
|
|
function SearchPage.mount(container: Frame, deps: any): { refresh: () -> () }
|
|
local header = Theme.header(container, "Search")
|
|
local scroll = Theme.scroll(32, 0, 8)
|
|
scroll.Parent = container
|
|
|
|
local function refresh()
|
|
for _, child in scroll:GetChildren() do
|
|
if not child:IsA("UIBase") then
|
|
child:Destroy()
|
|
end
|
|
end
|
|
|
|
local query = tostring(deps.getQuery() or "")
|
|
if string.match(query, "^%s*$") then
|
|
Theme.label({
|
|
Size = UDim2.new(1, 0, 0, 40),
|
|
Text = "Type in the sidebar box to search content by id or name.",
|
|
TextColor3 = Theme.COLOR.DIM,
|
|
TextWrapped = true,
|
|
}).Parent =
|
|
scroll
|
|
return
|
|
end
|
|
local lowered = string.lower(query)
|
|
|
|
local order = 0
|
|
local shown = 0
|
|
local total = 0
|
|
for _, catKey in deps.ContentAdmin.ORDER do
|
|
local cat = deps.ContentAdmin.CATEGORIES[catKey]
|
|
local matches = {}
|
|
for _, entry in deps.ContentAdmin.listRoster(catKey) do
|
|
if deps.ContentAdminUi.matchEntry(deps.ContentAdmin, catKey, entry, lowered) then
|
|
table.insert(matches, entry)
|
|
end
|
|
end
|
|
total += #matches
|
|
if #matches == 0 or shown >= MAX_RESULTS then
|
|
continue -- past the cap, count the matches but don't render an empty header
|
|
end
|
|
order += 1
|
|
Theme.label({
|
|
Size = UDim2.new(1, 0, 0, 20),
|
|
Text = `{cat.title} — {#matches} match{if #matches == 1 then "" else "es"}`,
|
|
TextColor3 = Theme.COLOR.ACCENT,
|
|
TextSize = 13,
|
|
Font = Theme.FONT_BOLD,
|
|
LayoutOrder = order,
|
|
}).Parent =
|
|
scroll
|
|
for _, entry in matches do
|
|
if shown >= MAX_RESULTS then
|
|
break
|
|
end
|
|
shown += 1
|
|
order += 1
|
|
local panel = deps.ContentAdminUi.buildRosterEntry(catKey, entry, deps.ContentAdmin, deps.actions)
|
|
panel.LayoutOrder = order
|
|
panel.Parent = scroll
|
|
end
|
|
end
|
|
|
|
if total == 0 then
|
|
Theme.label({
|
|
Size = UDim2.new(1, 0, 0, 40),
|
|
Text = `No content matches '{query}'.`,
|
|
TextColor3 = Theme.COLOR.DIM,
|
|
TextWrapped = true,
|
|
}).Parent =
|
|
scroll
|
|
elseif total > shown then
|
|
order += 1
|
|
Theme.label({
|
|
Size = UDim2.new(1, 0, 0, 22),
|
|
Text = `Showing {shown} of {total} — refine your search.`,
|
|
TextColor3 = Theme.COLOR.DIM,
|
|
TextSize = 12,
|
|
LayoutOrder = order,
|
|
}).Parent =
|
|
scroll
|
|
end
|
|
end
|
|
|
|
header.refreshBtn.MouseButton1Click:Connect(refresh)
|
|
refresh()
|
|
return { refresh = refresh }
|
|
end
|
|
|
|
return SearchPage
|