AI & workflows
Ai Loop Panel code
Copy the implementation or inspect every file that belongs to this component unit.
resources/views/components/ui/ai-loop-panel.blade.php
@props([
'title',
'description' => null,
'open' => false,
'badge' => null,
'badgeVariant' => 'gray',
'mobileCollapsible' => false,
])
@php
/*
| Expandable panel for AI loop workspaces. Wrap embedded AI components
| with `:embedded="true"` to avoid duplicate headers and borders.
|
| mobileCollapsible — collapsible below lg; always expanded on lg+ screens
|
| Usage:
| <x-ui.ai-loop-panel title="Loop history" description="2 iterations" :open="true">
| <x-ui.ai-loop-history :embedded="true" ... />
| </x-ui.ai-loop-panel>
*/
@endphp
<div
x-data="{
isLarge: window.matchMedia('(min-width: 1024px)').matches,
open: @js($mobileCollapsible)
? (window.matchMedia('(min-width: 1024px)').matches || @js((bool) $open))
: @js((bool) $open),
init() {
if (! @js($mobileCollapsible)) {
return;
}
const mediaQuery = window.matchMedia('(min-width: 1024px)');
mediaQuery.addEventListener('change', (event) => {
this.isLarge = event.matches;
if (event.matches) {
this.open = true;
}
});
},
toggle() {
if (@js($mobileCollapsible) && this.isLarge) {
return;
}
this.open = ! this.open;
},
isContentVisible() {
return @js($mobileCollapsible) ? (this.isLarge || this.open) : this.open;
},
}"
{{ $attributes->merge(['class' => 'min-w-0 overflow-hidden rounded-2xl border border-gray-200 bg-white shadow-sm dark:border-gray-800 dark:bg-gray-900']) }}
>
<button
type="button"
@click="toggle()"
:aria-expanded="isContentVisible()"
@class([
'flex w-full items-center justify-between gap-3 px-4 py-3 text-left transition',
'hover:bg-gray-50 dark:hover:bg-gray-800/40' => ! $mobileCollapsible,
'hover:bg-gray-50 dark:hover:bg-gray-800/40 lg:cursor-default lg:hover:bg-transparent' => $mobileCollapsible,
])
>
<div class="min-w-0 shrink-0">
<p class="whitespace-nowrap text-sm font-semibold text-gray-900 dark:text-white">{{ $title }}</p>
@if ($description)
<p class="truncate text-xs text-gray-500 dark:text-gray-400">{{ $description }}</p>
@endif
</div>
<div class="flex shrink-0 flex-wrap items-center justify-end gap-1.5">
@isset($badges)
{{ $badges }}
@endisset
@if ($badge !== null)
<x-ui.badge :variant="$badgeVariant" size="sm">{{ $badge }}</x-ui.badge>
@endif
<x-ui.icon
name="caret-down"
weight="bold"
size="base"
::class="open ? 'rotate-180' : ''"
@class([
'shrink-0 text-gray-400 transition-transform duration-200',
'lg:hidden' => $mobileCollapsible,
])
/>
</div>
</button>
<div x-show="isContentVisible()" x-collapse x-cloak>
{{ $slot }}
</div>
</div>