launcher 0.2.0: inline status icons + classified update-check + token auth wire

- Async update-check per module on init (git.start_ls_remote_tags),
  polled per frame. Result classified into available / no-update /
  fail / local-only via the new kind field from lib-core.git 0.2.0.
- Pre-emptive dep-check per module (depf.ensure_for_module) on init
  populates a dep_check_result whose ok/conflicts/warnings/errors
  drive the icon strip.
- Inline icon strip per module-row (icons.lua): rotating ASCII
  spinner while pending, yellow warn for auth/network failures or
  dirty deps, blue update-badge with target version (clickable to
  trigger fetch+checkout), red error for dep-check conflicts, gray
  L for repos that genuinely don't exist on the server. Tooltip on
  hover shows the specific reason per icon.
- Click handler splits launch-vs-update by hit-testing the badge
  rect before the row rect. Launch is blocked when dep-check failed.
- On init, reads engine.config("gitea_token") and forwards via
  git.set_token so users can persist their token in their user-data
  engine.json layer instead of using an env var.
- New deps: lib-core.git 0.1.0, lib-management.dep-fetcher 0.1.2.
This commit is contained in:
Calic
2026-05-31 14:09:39 +02:00
parent 4d91a5ffde
commit 8b833bbb35
3 changed files with 434 additions and 31 deletions

84
icons.lua Normal file
View File

@@ -0,0 +1,84 @@
-- icons.lua — inline status-icon primitives for the launcher.
--
-- The engine ships text + rect bindings; no sprites, no draw_circle yet,
-- so every icon is composed from draw_rect + draw_text. Each helper
-- returns the rect it occupied so the caller can stash it for hit-tests
-- (tooltip + update-badge click).
local M = {}
M.SPINNER_COLOR = 0xC0C0C0FF
M.WARN_COLOR = 0xFFD000FF
M.UPDATE_COLOR = 0x4090FFFF
M.ERROR_COLOR = 0xFF4040FF
M.LOCAL_COLOR = 0x707070FF -- muted gray for "local-only" repos
M.ICON_W = 16
M.ICON_H = 16
local SPINNER_GLYPHS = { "|", "/", "-", "\\" }
-- Spinner: rotating ASCII glyph cycled at ~7.5 Hz at 60 fps (every 8
-- frames). Returns its bounding rect so the caller can hit-test for
-- tooltip ("checking for updates...").
function M.draw_spinner(x, y, frame)
local glyph = SPINNER_GLYPHS[(math.floor(frame / 8) % 4) + 1]
engine.render.draw_text(glyph, x + 4, y, 12, M.SPINNER_COLOR)
return { x = x, y = y, w = M.ICON_W, h = M.ICON_H }
end
-- Warn: yellow square with "!" centered.
function M.draw_warn(x, y)
engine.render.draw_rect(x, y, M.ICON_W, M.ICON_H, M.WARN_COLOR)
engine.render.draw_text("!", x + 5, y, 14, 0x000000FF)
return { x = x, y = y, w = M.ICON_W, h = M.ICON_H }
end
-- Update badge: blue pill "^v0.5.5". Width depends on the version
-- string; returned rect carries the dynamic width so callers can do
-- right-to-left layout and click-hit-tests.
function M.draw_update_badge(x, y, version_str)
local label = "^" .. version_str
local tw, _ = engine.render.measure_text(label, 12)
local w = tw + 10
engine.render.draw_rect(x, y, w, M.ICON_H, M.UPDATE_COLOR)
engine.render.draw_text(label, x + 5, y + 1, 12, 0xFFFFFFFF)
return { x = x, y = y, w = w, h = M.ICON_H }
end
-- Error: red square with white "!" — same shape as warn but different
-- color so colorblind users still see the shape difference via the
-- background tint contrast.
function M.draw_error(x, y)
engine.render.draw_rect(x, y, M.ICON_W, M.ICON_H, M.ERROR_COLOR)
engine.render.draw_text("!", x + 5, y, 14, 0xFFFFFFFF)
return { x = x, y = y, w = M.ICON_W, h = M.ICON_H }
end
-- Local-only: muted-gray pill with "L" — module exists in the local
-- install but the remote returns 404 even with credentials, so the
-- update channel doesn't apply. Informational, not a warning.
function M.draw_local(x, y)
engine.render.draw_rect(x, y, M.ICON_W, M.ICON_H, M.LOCAL_COLOR)
engine.render.draw_text("L", x + 5, y, 12, 0xFFFFFFFF)
return { x = x, y = y, w = M.ICON_W, h = M.ICON_H }
end
-- Tooltip: small dark panel anchored to the right of (mx, my).
-- Centered vertically on the cursor. Clamped to screen via the
-- screen-size args.
function M.draw_tooltip(mx, my, text, screen_w, screen_h)
if not text or text == "" then return end
local tw, th = engine.render.measure_text(text, 12)
local pad_x, pad_y = 6, 3
local w, h = tw + 2 * pad_x, th + 2 * pad_y
local x = mx + 14
local y = my - h / 2
if x + w > screen_w then x = mx - w - 8 end
if y < 0 then y = 0 end
if y + h > screen_h then y = screen_h - h end
engine.render.draw_rect(x, y, w, h, 0x202020E0)
engine.render.draw_text(text, x + pad_x, y + pad_y, 12, 0xFFFFFFFF)
end
return M