@php $dirs = collect($entries)->where('type', 'dir')->count(); $files = collect($entries)->where('type', 'file')->count(); // Human-readable byte sizes for the live SFTP listing. $fmtSize = function (?int $bytes): string { if ($bytes === null) { return '—'; } $units = ['B', 'KB', 'MB', 'GB', 'TB']; $i = 0; $val = (float) $bytes; while ($val >= 1024 && $i < count($units) - 1) { $val /= 1024; $i++; } return ($i === 0 ? (int) $val : number_format($val, 1)).' '.$units[$i]; }; @endphp
{{-- Header --}}

{{ __('files.eyebrow') }}

{{ __('files.heading') }}

{{ $connected ? __('files.sftp_connected') : __('files.sftp_disconnected') }}
{{-- Breadcrumb / path --}}
@foreach ($this->crumbs as $crumb) @if (! $loop->first) / @endif @endforeach
{{-- Listing --}} {{-- Column header (wide desktop only — below xl the rows render as stacked cards) --}}
@if (! $ready) @for ($i = 0; $i < 8; $i++)
@endfor @else @foreach ($entries as $e) @php $isDir = $e['type'] === 'dir'; @endphp {{-- Stable wire:key per row: without it Livewire's morph clones nodes by position and the inline Alpine x-data (modalTrigger) loses its scope → "pending is not defined". --}} {{-- Below xl: a stacked card (name on its own full-width line, meta + actions beneath) so the filename is never squeezed by the meta/action columns. xl+: a real table grid. --}}
{{-- Name + icon (directories look clickable) --}}
$isDir, 'bg-raised text-ink-3' => ! $isDir, ])> @if ($isDir) @else {{-- Filename opens the editor modal too — inline modalTrigger keeps the link layout but adds the pending state + timeout error toast. --}} @endif
{{-- Meta as real table cells (xl only) --}} {{-- Folded meta line under the name (mobile + tablet) --}}

{{ $e['perms'] }} · {{ $fmtSize($e['size']) }} · {{ $e['modified'] }}

{{-- Row actions --}}
@unless ($isDir) {{ __('files.download') }} {{ __('common.edit') }} @endunless {{ __('common.delete') }}
@endforeach @endif