Wire admin studio SSR and search infrastructure

This commit is contained in:
2026-05-01 11:46:06 +02:00
parent 257b0dbef6
commit 18cea8b0f0
329 changed files with 197465 additions and 2741 deletions

View File

@@ -145,20 +145,32 @@ class NovaCardRenderService
private function paintOverlay($image, array $project, int $width, int $height): void
{
$style = (string) Arr::get($project, 'background.overlay_style', 'dark-soft');
$alpha = match ($style) {
'dark-strong' => 72,
'dark-soft' => 92,
'light-soft' => 108,
default => null,
};
if ($alpha === null) {
if ($style === 'none') {
return;
}
// Respect the opacity slider (0100 %) that the CSS preview applies to the overlay div.
$opacityPct = max(0, min(100, (int) Arr::get($project, 'background.opacity', 50)));
$scale = $opacityPct / 100.0;
// Top/bottom gradient stop opacities — matches overlayStyle() in NovaCardCanvasPreview.jsx:
// dark-soft: linear-gradient(180deg, rgba(2,6,23,0.18), rgba(2,6,23,0.48))
// dark-strong: linear-gradient(180deg, rgba(2,6,23,0.38), rgba(2,6,23,0.68))
// light-soft: linear-gradient(180deg, rgba(255,255,255,0.08), rgba(255,255,255,0.22))
[$topA, $botA] = match ($style) {
'dark-strong' => [0.38, 0.68],
'light-soft' => [0.08, 0.22],
default => [0.18, 0.48], // dark-soft
};
$rgb = $style === 'light-soft' ? [255, 255, 255] : [0, 0, 0];
$overlay = imagecolorallocatealpha($image, $rgb[0], $rgb[1], $rgb[2], $alpha);
imagefilledrectangle($image, 0, 0, $width, $height, $overlay);
// Draw a scanline gradient to match the CSS linear-gradient overlay.
for ($y = 0; $y < $height; $y++) {
$alpha = ($topA + ($botA - $topA) * ($y / $height)) * $scale;
$gdAlpha = max(0, min(127, (int) round((1.0 - $alpha) * 127)));
$color = imagecolorallocatealpha($image, $rgb[0], $rgb[1], $rgb[2], $gdAlpha);
imageline($image, 0, $y, $width - 1, $y, $color);
}
}
// ─── Text rendering (FreeType / GD fallback) ─────────────────────────────
@@ -167,10 +179,15 @@ class NovaCardRenderService
{
$fontPreset = (string) Arr::get($project, 'typography.font_preset', 'modern-sans');
$fontFile = $this->resolveFont($fontPreset);
$textColor = $this->allocateHex($image, (string) Arr::get($project, 'typography.text_color', '#ffffff'));
$accentColor = $this->allocateHex($image, (string) Arr::get($project, 'typography.accent_color', Arr::get($project, 'typography.text_color', '#ffffff')));
// Apply text_opacity (10100 %) to both text and accent colours, matching CSS blockStyle().
$textOpacityPct = max(10, min(100, (int) Arr::get($project, 'typography.text_opacity', 100)));
$textAlpha = (int) round((1.0 - $textOpacityPct / 100.0) * 127);
[$tr, $tg, $tb] = $this->hexToRgb((string) Arr::get($project, 'typography.text_color', '#ffffff'));
[$ar, $ag, $ab] = $this->hexToRgb((string) Arr::get($project, 'typography.accent_color', Arr::get($project, 'typography.text_color', '#ffffff')));
$textColor = imagecolorallocatealpha($image, $tr, $tg, $tb, $textAlpha);
$accentColor = imagecolorallocatealpha($image, $ar, $ag, $ab, $textAlpha);
$alignment = (string) Arr::get($project, 'layout.alignment', 'center');
$lhMulti = (float) Arr::get($project, 'typography.line_height', 1.35);
$lhMulti = (float) Arr::get($project, 'typography.line_height', 1.2);
$shadow = (string) Arr::get($project, 'typography.shadow_preset', 'soft');
$paddingRatio = match ((string) Arr::get($project, 'layout.padding', 'comfortable')) {
@@ -400,15 +417,22 @@ class NovaCardRenderService
foreach (array_slice($decorations, 0, (int) config('nova_cards.validation.max_decorations', 6)) as $index => $decoration) {
$glyph = (string) Arr::get($decoration, 'glyph', '•');
// pos_x / pos_y are stored as percentages (0100); fall back to sensible defaults.
// pos_x / pos_y are stored as percentages (0100); when absent, fall back to
// `placement` field — mirroring placementStyles in NovaCardCanvasPreview.jsx.
$xPct = Arr::get($decoration, 'pos_x');
$yPct = Arr::get($decoration, 'pos_y');
$x = $xPct !== null
? (int) round((float) $xPct / 100 * $width)
: (int) round(($index % 2 === 0 ? 0.12 : 0.82) * $width);
$y = $yPct !== null
? (int) round((float) $yPct / 100 * $height)
: (int) round((0.14 + ($index * 0.1)) * $height);
if ($xPct !== null && $yPct !== null) {
$x = (int) round((float) $xPct / 100 * $width);
$y = (int) round((float) $yPct / 100 * $height);
} else {
$placement = (string) Arr::get($decoration, 'placement', 'top-right');
$x = str_contains($placement, 'left') ? (int) round(0.12 * $width)
: (str_contains($placement, 'right') ? (int) round(0.88 * $width)
: (int) round(0.50 * $width));
$y = str_contains($placement, 'top') ? (int) round(0.12 * $height)
: (str_contains($placement, 'bottom') ? (int) round(0.88 * $height)
: (int) round(0.50 * $height));
}
// Canvas clamp: max(18, min(size, 64)) matching NovaCardCanvasPreview.
$rawSize = max(18, min((int) Arr::get($decoration, 'size', 28), 64));