sugarcraft/sugar-gallery
Composer 安装命令:
composer require sugarcraft/sugar-gallery
包简介
Poster grids and rails for media TUIs — a 2-D virtualized PosterGrid with sparse range paging, a horizontal Rail carousel, and a PosterCard tile. Renderer-agnostic (holds pre-rendered poster cells).
README 文档
README
Poster grids and rails for media TUIs — a 2-D virtualized PosterGrid
for large libraries, a horizontal Rail carousel for browse rows, and a
PosterCard tile. The widgets are renderer-agnostic: a card holds
already-rendered poster bytes (produce them however you like — e.g.
candy-mosaic), so this lib pulls
in no image decoder.
Install
composer require sugarcraft/sugar-gallery
PosterGrid — virtualized, sparse, owner-paged
The grid knows the total item count up front but holds only the cards that have been fetched, keyed by their absolute index; missing indices render as skeletons. Only the rows inside the viewport are drawn, so a 50,000-item library renders as cheaply as a 50-item one.
use SugarCraft\Gallery\PosterGrid; use SugarCraft\Gallery\PosterCard; $grid = PosterGrid::new(cardWidth: 16, posterHeight: 9) ->withViewport($cols, $rows) ->reset(total: 5000); // a fresh result set // Keyboard nav (map your keys to these — all clamp + keep the cursor on screen): $grid = $grid->right(); // ← → move within a row $grid = $grid->down(); // ↑ ↓ move between rows $grid = $grid->pageDown(); // PgUp / PgDn $grid = $grid->home()->end(); // Home / End $grid = $grid->moveTo(2600); // jump (e.g. an A–Z letter offset)
Owner-driven paging (the need-range pattern)
After each move, read the visible window and fetch the page(s) covering it, then splice the results back in at their absolute index:
[$start, $end] = $grid->visibleRange(overscanRows: 1); if ($start <= $end && $start !== $lastFetchedStart) { // fetch items [$start, $end] from your API, build cards keyed by index… $grid = $grid->withItems([$start => $card0, $start + 1 => $card1, /* … */]); }
Async poster arrived for one cell? ->withItem($index, $card->withPoster($ansi)).
Render
echo $grid->render(focused: true); // cursor shown only when the grid is focused
Pass a candy-zone Manager to make
cells mouse-clickable — each is wrapped as zone id cell:<index>:
$frame = $grid->render(true, $zones); $clean = $zones->scan($frame); // strip markers, record bounds $zone = $zones->anyInBounds($mouseMsg); // → "cell:42"
Rail — horizontal carousel
use SugarCraft\Gallery\Rail; $rail = new Rail('Continue Watching', $cards); $rail = $rail->moveCursor(+1, Rail::perRow($railWidth, $cardWidth)); echo $rail->render($railWidth, focused: true, cardWidth: 16, posterHeight: 9);
PosterCard — one tile
use SugarCraft\Gallery\PosterCard; $card = new PosterCard(id: '42', title: 'The Matrix', posterUrl: $url); $card = $card->withPoster($renderedAnsi); // attach when the async render lands $card = $card->withProgress(0.6); // optional continue-watching bar echo $card->render(focused: true, width: 16, posterHeight: 9);
Every card row is exactly width cells wide and the grid normalizes each cell
to cardWidth × (posterHeight + 2), so columns and rows always line up whether
or not a card carries a progress bar.
License
MIT © Joe Huss
统计信息
- 总下载量: 0
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 0
- 点击次数: 2
- 依赖项目数: 1
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2026-06-22