/_sdk/kit/. They are imported
with paths that start with /_sdk/…. Your own files use relative paths
(./util.js). For the grid we will use these pieces from dom.js and library.js:
h(tag, props, ...children)creates elements without writingcreateElementevery time.classsets the class,onclickadds the event, and the rest (title,data-…) is set as an attribute.stylecan be a string ("--tono: 120") or an object ({ opacity: "0.5" }). For CSS variables, use the string.img(url)creates an<img>with lazy loading (loading="lazy"). It adds the classimg-loadedwhen it loads,img-errorif it fails, andimg-emptyif there was no URL, so CSS decides what to show in each case.keyed(container, list, key, paintOne)redraws by reusing the nodes that already existed.paintOne(game, previous)receives that game’s previous node (or nothing) and returns the one to use. With hundreds of games it stays fast, and focus does not jump.visible(games)removes hidden games, missing games, emulators and console games, whichlibrary.alldoes include (those belong in Homebrew), andsort(games, "title")sorts ("title","recent","playtime","added","release"or"rating").
main.js with this:
- Thumbnails in the grid.
coverThumbis about 40 KB.coveris the large image. In lists and grids, always use thumbnails. - The “signature” is the trick for not rebuilding cards. If the name and the cover are the same, the previous card is reused and only the classes change (favorite). When the cover for a newly added game arrives, the signature changes and a new card is created.
- With no cover you see the initials (
initials("Hollow Knight")→HK) on a gradient whose color comes from the name (hueOf()), and the image stays invisible until it loads. ejg.game.launch(id)launches the game (ejGames plays the launch sound). If it fails, the promise rejects and we show it withejg.ui.toast(text, "error").ejg.ui.open("add-folder")opens the launcher screen for adding game folders. There are more:"settings","search","menu","stats","profiles","collections","theme"…
style.css):

