Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- feat: add validated collector contracts, lifecycle coordination, and isolated typed snapshot capture for adapters.
- fix: guard the user-switch panel against concurrent submits so a double-click (or set + reset in the same tick) sends a single identity-switch request instead of racing two session regenerations.
- test: strengthen mutation coverage and clear PHPStan's result cache before static mutation analysis.
- feat(router): implement Router panel with Current Route and Rules sections.
2 changes: 1 addition & 1 deletion resources/assets/dist/css/debug.min.css

Large diffs are not rendered by default.

29 changes: 20 additions & 9 deletions resources/src/styles/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -1669,16 +1669,19 @@
text-align: right;
}

/* Sort-icons rendered by GridView sort links. */
/* Sort-icons rendered by GridView sort links (`a.asc`/`a.desc` or appended marker-span dialect). */
a.asc::after,
a.desc::after {
a.desc::after,
.yii-debug-sort-asc::after,
.yii-debug-sort-desc::after {
margin-left: 4px;
content: "↑";
font-size: 0.85em;
opacity: 0.78;
}

a.desc::after {
a.desc::after,
.yii-debug-sort-desc::after {
content: "↓";
}

Expand Down Expand Up @@ -1819,8 +1822,9 @@
resize: vertical;
}

/* GridView filter-row inputs. */
.yii-debug-grid .filters td {
/* GridView filter-row inputs (`.filters` row or per-cell `.yii-debug-filter-cell` dialect). */
.yii-debug-grid .filters td,
.yii-debug-grid td.yii-debug-filter-cell {
padding: 6px 12px;
border-bottom: var(--yii-debug-border);
background: color-mix(
Expand All @@ -1832,7 +1836,10 @@

.yii-debug-grid .filters input,
.yii-debug-grid .filters select,
.yii-debug-grid .filters textarea {
.yii-debug-grid .filters textarea,
.yii-debug-grid td.yii-debug-filter-cell input,
.yii-debug-grid td.yii-debug-filter-cell select,
.yii-debug-grid td.yii-debug-filter-cell textarea {
width: 100%;
min-width: 0;
min-height: 28px;
Expand Down Expand Up @@ -2122,7 +2129,8 @@
}

/* Filter inputs grow with their cell instead of clinging to a fixed width. */
tbody tr.filters td input {
tbody tr.filters td input,
td.yii-debug-filter-cell input {
width: 100%;
min-width: 0;
}
Expand Down Expand Up @@ -4777,7 +4785,9 @@
* so this wins regardless of source order.
*/
.yii-debug-grid .filters input.yii-debug-col-id-input,
.yii-debug-grid .filters input.yii-debug-col-queue-id-input {
.yii-debug-grid .filters input.yii-debug-col-queue-id-input,
.yii-debug-grid td.yii-debug-filter-cell input.yii-debug-col-id-input,
.yii-debug-grid td.yii-debug-filter-cell input.yii-debug-col-queue-id-input {
box-sizing: border-box;
width: 104px;
}
Expand Down Expand Up @@ -4840,7 +4850,8 @@
padding: 8px 10px;
}

.filters td {
.filters td,
td.yii-debug-filter-cell {
padding: 6px 10px;
}
}
Expand Down
119 changes: 17 additions & 102 deletions resources/views/snapshot.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,105 +4,18 @@

use UIAwesome\Html\Flow\{Div, Pre};
use UIAwesome\Html\Heading\{H1, H2};
use UIAwesome\Html\List\{Dd, Dl, Dt};
use UIAwesome\Html\Phrasing\{Code, Small, Span, Strong};
use UIAwesome\Html\Root\Header;
use UIAwesome\Html\Sectioning\Section;

/**
* @var bool $ajax Whether the captured request was an AJAX request.
* @var string $capturedAt Formatted capture timestamp.
* @var string $clientIp Formatted client address.
* @var string $duration Formatted request duration.
* @var array{exception: string, stage: string}|null $failure Captured panel failure.
* @var string $memory Formatted peak memory.
* @var string $method HTTP request method.
* @var string $methodVariant Semantic method class suffix.
* @var string $panelLabel Selected panel label.
* @var string|null $panelContent Trusted adapter panel markup or `null` to render the JSON fallback.
* @var string $payload Formatted, encoded panel payload.
* @var string|null $renderError Panel renderer error message or `null` when rendering succeeded.
* @var int $statusCode HTTP response status.
* @var string $statusVariant Semantic response status class suffix.
* @var string $tag Captured request tag.
* @var string $url Captured request URL.
*/
$requestMeta = Div::tag()
->class('yii-debug-request-overview-meta')
->html(
Span::tag()
->class('yii-debug-request-overview-meta-item')
->html(
Small::tag()->class('yii-debug-request-overview-meta-label')->content('Captured'),
Span::tag()->content($capturedAt),
),
Span::tag()
->class('yii-debug-request-overview-meta-item')
->html(
Small::tag()->class('yii-debug-request-overview-meta-label')->content('Request ID'),
Span::tag()->class('yii-debug-request-overview-tag')->content($tag)->title($tag),
),
);

if ($ajax) {
$requestMeta = $requestMeta->html(
Span::tag()->class('yii-debug-request-overview-flag')->content('AJAX'),
);
}

$requestMetrics = Dl::tag()
->class('yii-debug-request-overview-metrics')
->html(
Div::tag()
->class('yii-debug-request-overview-metric')
->html(
Dt::tag()->content('Duration'),
Dd::tag()->content($duration),
),
Div::tag()
->class('yii-debug-request-overview-metric')
->html(
Dt::tag()->content('Peak memory'),
Dd::tag()->content($memory),
),
Div::tag()
->class('yii-debug-request-overview-metric')
->html(
Dt::tag()->content('Client IP'),
Dd::tag()->content($clientIp),
),
);
$requestIdentity = Div::tag()
->class('yii-debug-request-overview-identity')
->html(
Small::tag()->class('yii-debug-request-overview-label')->content('Captured request'),
Div::tag()
->class('yii-debug-request-hero-line')
->html(
Span::tag()
->class('yii-debug-request-hero-method yii-debug-verb-' . $methodVariant)
->content($method),
Span::tag()->class('yii-debug-request-hero-url')->content($url)->title($url),
),
);
$responseStatus = Div::tag()
->class('yii-debug-request-overview-status')
->html(
Small::tag()->class('yii-debug-request-overview-status-label')->content('Response'),
Strong::tag()
->class('yii-debug-request-overview-status-value yii-debug-snapshot-status yii-debug-status-' . $statusVariant)
->content((string) $statusCode),
);
$overview = Section::tag()
->addAriaAttribute('label', 'Request overview')
->class('yii-debug-request-overview yii-debug-status-' . $statusVariant)
->html(
Header::tag()
->class('yii-debug-request-overview-header')
->html($requestIdentity, $responseStatus),
$requestMetrics,
$requestMeta,
);
$failureCallout = $failure === null
? ''
: Div::tag()
Expand All @@ -127,19 +40,22 @@
Span::tag()->content($renderError),
),
);
$panelHeader = Header::tag()
->class('yii-debug-panel-heading')
->html(
Div::tag()
->class('yii-debug-panel-heading-copy')
->html(
Small::tag()->class('yii-debug-panel-heading-eyebrow')->content('Selected panel'),
H2::tag()->id('yii-debug-panel-title')->content($panelLabel),
),
Span::tag()
->class('yii-debug-panel-heading-kind')
->content($panelContent === null ? 'JSON snapshot' : 'Rendered view'),
);
$panelHeader = $panelContent !== null
? ''
: Header::tag()
->class('yii-debug-panel-heading')
->html(
Div::tag()
->class('yii-debug-panel-heading-copy')
->html(
Small::tag()->class('yii-debug-panel-heading-eyebrow')->content('Selected panel'),
H2::tag()->id('yii-debug-panel-title')->content($panelLabel),
),
Span::tag()
->class('yii-debug-panel-heading-kind')
->content('JSON snapshot'),
)
->render();
$panelBody = $panelContent ?? Pre::tag()
->class('yii-debug-panel-payload')
->html(Code::tag()->content($payload))
Expand All @@ -150,8 +66,7 @@
->content($method . ' ' . $url)
->render()
?>
<?= $overview->render() ?>
<?= $panelHeader->render() ?>
<?= $panelHeader ?>
<?= $failureCallout ?>
<?= $renderCallout ?>
<?= $panelBody;
64 changes: 64 additions & 0 deletions src/Panel/Router/RouterCurrentView.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<?php

declare(strict_types=1);

namespace PHPForge\Debug\Panel\Router;

use function count;

/**
* Typed view-model for the Current Route section of the Router panel.
*
* Plain surface over the hydrated {@see RouterSnapshot}: the matched action, the resolved route, the rules tried,
* and whether the router matched.
*/
final readonly class RouterCurrentView
{
public function __construct(
/**
* Resolved action descriptor logged for the current request; empty when unresolved.
*/
public string $action = '',
/**
* Number of rules inspected before a match (or until the trace ended).
*/
public int $count = 0,
/**
* Whether any inspected rule reported a successful match.
*/
public bool $hasMatch = false,
/**
* @var list<CurrentRouteLogRow> Rules inspected during routing, in inspection order.
*/
public array $logs = [],
/**
* Trace-level info message captured for the routing pass, when present.
*/
public string|null $message = null,
/**
* Resolved request route logged for the current request; empty when unresolved.
*/
public string $route = '',
) {}

/**
* Builds the view model from a hydrated router snapshot, or an empty one when the panel captured nothing.
*/
public static function fromSnapshot(RouterSnapshot|null $snapshot): self
{
if ($snapshot === null) {
return new self();
}

$logs = $snapshot->entries();

return new self(
action: $snapshot->action ?? '',
count: count($logs),
hasMatch: $snapshot->hasMatch(),
logs: $logs,
message: $snapshot->message,
route: $snapshot->route,
);
}
}
Loading
Loading