feat(launcher): render detail panel content (title/desc/deps)

panels/detail.lua now renders the selected module's title, version,
author, markdown description, and per-dep status list with OK/WARN/ERR
glyphs. Dep-list order follows manifest.module::deps[] (L-Q6 load-
order, no re-sort). Whole-list bg-tint is bubbled to ctx.list_tint so
the list panel picks up a yellow/red ambient cue for warn/error
states. Hero strip remains a placeholder until L.5.
This commit is contained in:
Calic
2026-06-11 02:09:04 +02:00
parent b360ee6230
commit 0a96b009f1
2 changed files with 147 additions and 21 deletions

View File

@@ -123,21 +123,57 @@ local function consume_update_check(m)
end
end
-- Composes a per-dep status list keyed by lib_id from the dep-fetcher
-- result's `errors` and `warnings` arrays. Each input dep entry is
-- `{id, version}` (from manifest.module::deps[]); each output entry
-- adds `status = "ok" | "warn" | "error"`. Load-order preserved
-- (L-Q6: no re-sort).
local function compose_dep_status_list(result, module_deps)
local out = {}
local err_ids, warn_ids = {}, {}
for _, e in ipairs(result.errors or {}) do
if e.lib_id then err_ids[e.lib_id] = true end
end
for _, w in ipairs(result.warnings or {}) do
if w.lib_id then warn_ids[w.lib_id] = true end
end
for _, c in ipairs(result.conflicts or {}) do
if c.lib_id then err_ids[c.lib_id] = true end
end
for _, d in ipairs(module_deps) do
local status = "ok"
if err_ids[d.id] then status = "error"
elseif warn_ids[d.id] then status = "warn"
end
out[#out + 1] = { id = d.id, version = d.version, status = status }
end
return out
end
-- Derives a roll-up kind ("ok" | "warn" | "error") from the dep-fetcher
-- result. Drives the list-panel's bg-tint cue via ctx.list_tint.
local function derive_result_kind(result)
if not result.ok then return "error" end
if (result.warnings and #result.warnings > 0) then return "warn" end
return "ok"
end
-- Lazy: only re-check dep-fetcher when the consumer flow asks for it
-- (init + after-update). Heavy-lift in init() is acceptable per the
-- plan; an async dep-check is future work.
local function run_dep_check(m)
local ok, result = pcall(depf.ensure_for_module, m.id)
if ok and type(result) == "table" then
m.status.dep_check_result = result
else
m.status.dep_check_result = {
if not (ok and type(result) == "table") then
result = {
ok = false, conflicts = {}, warnings = {},
errors = {{ lib_id = "<launcher>", kind = "internal",
message = tostring(result) }},
closure = {},
}
end
result.deps = compose_dep_status_list(result, m._raw_deps or {})
result.kind = derive_result_kind(result)
m.status.dep_check_result = result
end
-- ----- lifecycle -----------------------------------------------------
@@ -172,6 +208,7 @@ function init(ctx)
modal_open = nil, -- L.7 sets to "quit" | "conflict"
carousel_state = {}, -- per-module-id state, L.5 populates
manifest_cache = {}, -- L.3 populates
launcher_dir = MODULE_DIR, -- L.4: detail.lua dofiles markdown.lua via this
}
-- Window split: 1/3 list, 2/3 detail. Margin 12px between panels.
@@ -207,6 +244,15 @@ function init(ctx)
local meta, blocks = manifest_loader.load_for(entry.id)
ctx_panels.manifest_cache[entry.id] = { meta = meta, blocks = blocks }
entry.summary = meta.summary
-- L.4: also surface manifest.module::deps[] verbatim so the
-- detail-panel can render per-dep status glyphs in load order
-- (no re-sort, per L-Q6).
local raw, _ = engine.module.read_file(entry.id, "manifest.module")
if raw then
local data = depf._json_decode(raw)
if data and data.deps then entry._raw_deps = data.deps end
end
end
local wy, hy = engine.render.measure_text(YES_LABEL, FONT_BUTTON)