PHPackages                             sugarcraft/candy-mouse - 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. [Utility &amp; Helpers](/categories/utility)
4. /
5. sugarcraft/candy-mouse

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

sugarcraft/candy-mouse
======================

Self-contained Mark/Scan/Get mouse hit-testing (bubblezone pattern) plus ZoneClickTracker for press/release deduplication. No external Manager wiring needed.

01.3kPHP

Since Jul 11Pushed 1w agoCompare

[ Source](https://github.com/sugarcraft/candy-mouse)[ Packagist](https://packagist.org/packages/sugarcraft/candy-mouse)[ RSS](/packages/sugarcraft-candy-mouse/feed)WikiDiscussions master Synced 1w ago

READMEChangelogDependenciesVersions (1)Used By (0)

CandyMouse
==========

[](#candymouse)

Self-contained Mark/Scan/Get mouse hit-testing (bubblezone pattern) plus `ZoneClickTracker` for press/release deduplication. No external Manager wiring needed.

```
composer require sugarcraft/candy-mouse: dev-master

```

Role
----

[](#role)

Replaces the model where consumers wire `candy-zone`'s `Manager` externally. Each consumer owns its own `Scanner` instance — click handling stays local, no shared global state.

Quickstart
----------

[](#quickstart)

```
use SugarCraft\Mouse\Mark;
use SugarCraft\Mouse\Scanner;
use SugarCraft\Mouse\ZoneClickTracker;
use SugarCraft\Mouse\MouseEvent;

// 1. Wrap interactive content with invisible zone markers.
$rendered = Mark::zone('btn-ok', '  OK  ')
              . Mark::zone('btn-cancel', 'Cancel');

// 2. Scan after rendering to populate the zone registry.
$scanner = Scanner::new()->scan($rendered);

// 3. Reverse-lookup on mouse events.
$zone = $scanner->hit($mouseX, $mouseY); // ?Zone

// 4. Deduplicate clicks so each press+release pair emits one click.
$tracker = new ZoneClickTracker();
$result = $tracker->track(new MouseEvent(5, 1, 0, MouseAction::Release));
if ($result !== null) {
    echo "Clicked zone: " . $result->zone->id;
}
```

Key classes
-----------

[](#key-classes)

ClassRole`Mark`Wrap content with invisible Unicode sentinel markers`Scanner`Parse sentinels; `get(id)` and `hit(col, row)` lookups`Zone`Readonly bounding box (start/end col/row)`ZoneClickTracker`Press+Release dedup per button`MouseEvent`Immutable event (x, y, button, action enum)`MouseAction``Press` / `Release` / `Drag` / `Scroll` enumSentinel design
---------------

[](#sentinel-design)

Sentinels use private-use codepoints U+E000 (open) and U+E001 (close) — they never collide with ANSI SGR sequences or regular text. Scanning strips them from output.

Multi-row zones
---------------

[](#multi-row-zones)

A zone spanning multiple rows (e.g. `"line1\nline2"`) is stored as the **smallest axis-aligned bounding box** that contains all marked cells. The `inBounds()` check uses this rectangle, so an interior cell that was never part of the original content may still report as inside the zone. Callers wrapping reflowed or indented text should be aware that the hit-test is approximate for multi-line content.

See also
--------

[](#see-also)

[candy-zone](../candy-zone) is the TEA-facing façade (`Manager`, hover / drag / multi-click trackers, package-level `Zones`) layered over this low-level Mark/Scan/Zone primitive. It delegates its marker wrapping and bounding-box scanning to `SugarCraft\Mouse` — reach for candy-zone when you want the Bubble Tea-style zone workflow, and for candy-mouse directly when you just need self-contained hit-testing.

Coverage
--------

[](#coverage)

[![codecov](https://camo.githubusercontent.com/1b2e3688c3922ee50ec0ddbad2e8007dc27463a72cffa1996f968553bb609623/68747470733a2f2f636f6465636f762e696f2f67682f737567617263726166742f63616e64792d6d6f7573652f6272616e63682f6d61737465722f67726170682f62616467652e7376673f666c61673d63616e64792d6d6f757365)](https://codecov.io/gh/sugarcraft/candy-mouse)

Upstream
--------

[](#upstream)

Inspired by [lrstanley/bubblezone](https://github.com/lrstanley/bubblezone) — the Mark/Scan/Get pattern mirrors bubblezone's API. `ZoneClickTracker` addresses [bubblezone issue #10](https://github.com/lrstanley/bubblezone/issues/10).

###  Health Score

27

—

LowBetter than 46% of packages

Maintenance64

Regular maintenance activity

Popularity21

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 (18 commits)")

### Embed Badge

![Health badge](/badges/sugarcraft-candy-mouse/health.svg)

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

###  Alternatives

[illuminate/broadcasting

The Illuminate Broadcasting package.

7127.2M221](/packages/illuminate-broadcasting)

PHPackages © 2026

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