PHPackages                             sugarcraft/sugar-gallery - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Image &amp; Media](/categories/media)
4. /
5. sugarcraft/sugar-gallery

ActiveLibrary[Image &amp; Media](/categories/media)

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).

00PHP

Since Jun 22Pushed todayCompare

[ Source](https://github.com/sugarcraft/sugar-gallery)[ Packagist](https://packagist.org/packages/sugarcraft/sugar-gallery)[ RSS](/packages/sugarcraft-sugar-gallery/feed)WikiDiscussions master Synced today

READMEChangelogDependenciesVersions (1)Used By (0)

sugar-gallery
=============

[](#sugar-gallery)

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](https://github.com/sugarcraft/candy-mosaic)), so this lib pulls in no image decoder.

Install
-------

[](#install)

```
composer require sugarcraft/sugar-gallery
```

PosterGrid — virtualized, sparse, owner-paged
---------------------------------------------

[](#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)

[](#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 withItems([$start => $card0, $start + 1 => $card1, /* … */]);
}
```

Async poster arrived for one cell? `->withItem($index, $card->withPoster($ansi))`.

### Render

[](#render)

```
echo $grid->render(focused: true);     // cursor shown only when the grid is focused
```

Pass a [candy-zone](https://github.com/sugarcraft/candy-zone) `Manager` to make cells mouse-clickable — each is wrapped as zone id `cell:`:

```
$frame = $grid->render(true, $zones);
$clean = $zones->scan($frame);                 // strip markers, record bounds
$zone  = $zones->anyInBounds($mouseMsg);       // → "cell:42"
```

Rail — horizontal carousel
--------------------------

[](#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
---------------------

[](#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
-------

[](#license)

MIT © Joe Huss

###  Health Score

20

—

LowBetter than 13% of packages

Maintenance65

Regular maintenance activity

Popularity0

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity11

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

### Community

Maintainers

![](https://www.gravatar.com/avatar/b1036e0717211b8030b83cbe729e8ba6ba442fdbd5285fb97a39d7dcfe339342?d=identicon)[detain](/maintainers/detain)

---

Top Contributors

[![detain](https://avatars.githubusercontent.com/u/1364504?v=4)](https://github.com/detain "detain (2 commits)")

### Embed Badge

![Health badge](/badges/sugarcraft-sugar-gallery/health.svg)

```
[![Health](https://phpackages.com/badges/sugarcraft-sugar-gallery/health.svg)](https://phpackages.com/packages/sugarcraft-sugar-gallery)
```

###  Alternatives

[char0n/ffmpeg-php

PHP wrapper for FFmpeg application

495236.3k1](/packages/char0n-ffmpeg-php)[goat1000/svggraph

Generates SVG graphs

133890.0k3](/packages/goat1000-svggraph)[imagekit/imagekit

PHP library for Imagekit

46877.3k10](/packages/imagekit-imagekit)[gravatarphp/gravatar

Gravatar URL builder which is most commonly called as a Gravatar library

12644.1k2](/packages/gravatarphp-gravatar)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
