Skip to main content

AI & workflows

Ai Prompter code

Copy the implementation or inspect every file that belongs to this component unit.

resources/views/components/ui/ai-prompter.blade.php

@props([
    'action' => '#',
    'method' => 'POST',
    'title' => null,
    'promptName' => 'prompt',
    'promptPlaceholder' => 'Ask the agent to build, fix, or explain something...',
    'submitLabel' => 'Send prompt',
    'models' => [
        'gpt-5.5-medium' => '5.5 Medium',
        'gpt-5.5-high' => '5.5 High',
        'gpt-5-mini' => '5 Mini',
    ],
    'selectedModel' => 'gpt-5.5-medium',
    'approvalModes' => [
        'approve-for-me' => 'Approve for me',
        'ask-first' => 'Ask first',
        'read-only' => 'Read only',
    ],
    'selectedApprovalMode' => 'approve-for-me',
    'projects' => [
        'dzc' => 'dzc',
        'components' => 'components',
        'gorge' => 'gorge',
    ],
    'selectedProject' => 'dzc',
    'workModes' => [
        'local' => 'Work locally',
        'cloud' => 'Cloud task',
        'review' => 'Review only',
    ],
    'selectedWorkMode' => 'local',
    'branches' => [
        'main' => 'main',
        'develop' => 'develop',
        'feature/ai-prompter' => 'feature/ai-prompter',
    ],
    'selectedBranch' => 'main',
    'fileName' => 'attachments[]',
    'multiple' => true,
    'contained' => true,
])

@php
    /*
    | Prompt composer for AI coding workflows. It keeps the primary textarea
    | prominent and exposes model, approval, project, work mode, branch and file
    | controls as standard form fields.
    */
    $httpMethod = strtoupper($method);
    $formMethod = in_array($httpMethod, ['GET', 'POST'], true) ? $httpMethod : 'POST';
    $usesMethodSpoofing = ! in_array($httpMethod, ['GET', 'POST'], true);

    $footerSelectClass = 'h-9 w-full appearance-none rounded-lg border border-gray-200 bg-white pe-8 ps-9 text-sm font-medium text-gray-700 shadow-sm transition hover:border-gray-300 hover:bg-gray-50 focus:border-brand-500 focus:ring-1 focus:ring-brand-500 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-200 dark:hover:border-gray-600 dark:hover:bg-gray-800';

    $approvalModeStyles = [
        'approve-for-me' => [
            'icon' => 'shield-check',
            'iconClass' => 'text-brand-500 dark:text-brand-300',
            'selectClass' => 'border-brand-300 focus:border-brand-500 focus:ring-brand-500 dark:border-brand-700',
        ],
        'ask-first' => [
            'icon' => 'chat-circle-dots',
            'iconClass' => 'text-amber-500 dark:text-amber-400',
            'selectClass' => 'border-amber-300 focus:border-amber-500 focus:ring-amber-500 dark:border-amber-700',
        ],
        'read-only' => [
            'icon' => 'eye',
            'iconClass' => 'text-gray-500 dark:text-gray-400',
            'selectClass' => 'border-gray-300 focus:border-gray-500 focus:ring-gray-500 dark:border-gray-600',
        ],
    ];
@endphp

<form
    method="{{ $formMethod }}"
    action="{{ $action }}"
    enctype="multipart/form-data"
    {{ $attributes->merge(['class' => $contained ? 'mx-auto w-full max-w-4xl' : 'w-full']) }}
>
    @if ($usesMethodSpoofing)
        @method($httpMethod)
    @endif

    @if ($formMethod !== 'GET')
        @csrf
    @endif

    @if ($title)
        <h2 class="mb-6 text-center text-2xl font-semibold tracking-tight text-gray-900 dark:text-gray-100 sm:text-3xl">
            {{ $title }}
        </h2>
    @endif

    <div class="min-w-0 overflow-hidden rounded-2xl border border-gray-200 bg-white shadow-sm dark:border-gray-800 dark:bg-gray-900">
        <div class="bg-white p-4 dark:bg-gray-900 sm:p-5">
            <label for="{{ $promptName }}" class="sr-only">Prompt</label>
            <textarea
                id="{{ $promptName }}"
                name="{{ $promptName }}"
                rows="4"
                placeholder="{{ $promptPlaceholder }}"
                class="block min-h-28 w-full resize-none border-0 bg-transparent p-0 text-base text-gray-900 placeholder-gray-400 shadow-none focus:ring-0 dark:text-gray-100 dark:placeholder-gray-500"
            >{{ $slot }}</textarea>

            <div class="mt-4 flex flex-col gap-3 sm:flex-row sm:items-end sm:justify-between">
                <div class="flex w-full min-w-0 flex-col gap-2 sm:w-auto sm:flex-row sm:flex-wrap sm:items-center">
                    <label class="inline-flex h-10 w-full shrink-0 cursor-pointer items-center justify-center rounded-lg border border-gray-300 bg-white px-3 text-sm font-medium text-gray-700 shadow-sm transition hover:bg-gray-50 sm:w-auto dark:border-gray-700 dark:bg-gray-900 dark:text-gray-200 dark:hover:bg-gray-800">
                        <x-ui.icon name="plus" size="lg" class="me-2 text-gray-400" />
                        <span>Attach files</span>
                        <input type="file" name="{{ $fileName }}" @if ($multiple) multiple @endif class="sr-only">
                    </label>

                    <label
                        class="relative min-w-0 w-full sm:w-auto"
                        x-data="{
                            mode: @js($selectedApprovalMode),
                            styles: @js($approvalModeStyles),
                            icon() {
                                return this.styles[this.mode]?.icon ?? 'shield-check';
                            },
                            iconClass() {
                                return this.styles[this.mode]?.iconClass ?? 'text-gray-400 dark:text-gray-500';
                            },
                            selectClass() {
                                return this.styles[this.mode]?.selectClass ?? 'border-gray-300 focus:border-brand-500 focus:ring-brand-500 dark:border-gray-700';
                            },
                        }"
                    >
                        <span class="sr-only">Approval mode</span>
                        <i
                            class="ph pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-base leading-none transition-colors duration-200"
                            :class="`ph-${icon()} ${iconClass()}`"
                            aria-hidden="true"
                        ></i>
                        <select
                            name="approval_mode"
                            x-model="mode"
                            class="h-10 w-full appearance-none rounded-lg border bg-white pe-8 ps-9 text-sm font-medium text-gray-700 shadow-sm transition duration-200 sm:w-auto dark:bg-gray-900 dark:text-gray-200"
                            :class="selectClass()"
                        >
                            @foreach ($approvalModes as $value => $label)
                                <option value="{{ $value }}" @selected($selectedApprovalMode === $value)>{{ $label }}</option>
                            @endforeach
                        </select>
                        <x-ui.icon name="caret-down" weight="bold" size="sm" class="pointer-events-none absolute right-3 top-1/2 -translate-y-1/2 text-gray-400" />
                    </label>
                </div>

                <div class="flex w-full min-w-0 items-center gap-2 sm:w-auto sm:justify-end">
                    <label class="relative min-w-0 flex-1 sm:flex-none">
                        <span class="sr-only">Model</span>
                        <select name="model" class="h-10 w-full appearance-none rounded-lg border border-gray-300 bg-white pe-8 ps-3 text-sm font-medium text-gray-700 shadow-sm transition focus:border-brand-500 focus:ring-brand-500 sm:w-auto dark:border-gray-700 dark:bg-gray-900 dark:text-gray-200">
                            @foreach ($models as $value => $label)
                                <option value="{{ $value }}" @selected($selectedModel === $value)>{{ $label }}</option>
                            @endforeach
                        </select>
                        <x-ui.icon name="caret-down" weight="bold" size="sm" class="pointer-events-none absolute right-3 top-1/2 -translate-y-1/2 text-gray-400" />
                    </label>

                    <button type="submit" class="inline-flex h-11 w-11 shrink-0 items-center justify-center rounded-lg bg-brand-600 text-white shadow-sm transition-[box-shadow,border-color,color,transform] hover:bg-brand-500 focus:outline-none focus:ring-2 focus:ring-brand-500 focus:ring-offset-2 active:bg-brand-700 dark:focus:ring-offset-gray-900" aria-label="{{ $submitLabel }}">
                        <x-ui.icon name="arrow-up" weight="bold" size="xl" />
                    </button>
                </div>
            </div>
        </div>

        <div class="flex flex-col gap-2 border-t border-gray-200 bg-gray-50 px-4 py-3 dark:border-gray-800 dark:bg-gray-950 sm:flex-row sm:flex-wrap sm:items-center sm:gap-3 sm:px-5">
            <label class="relative w-full min-w-0 sm:w-44">
                <span class="sr-only">Project</span>
                <x-ui.icon name="folder" size="base" class="pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-gray-400" />
                <select name="project" class="{{ $footerSelectClass }}">
                    @foreach ($projects as $value => $label)
                        <option value="{{ $value }}" @selected($selectedProject === $value)>{{ $label }}</option>
                    @endforeach
                </select>
                <x-ui.icon name="caret-down" weight="bold" size="sm" class="pointer-events-none absolute right-3 top-1/2 -translate-y-1/2 text-gray-400" />
            </label>

            <label class="relative w-full min-w-0 sm:w-48">
                <span class="sr-only">Work mode</span>
                <x-ui.icon name="laptop" size="base" class="pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-gray-400" />
                <select name="work_mode" class="{{ $footerSelectClass }}">
                    @foreach ($workModes as $value => $label)
                        <option value="{{ $value }}" @selected($selectedWorkMode === $value)>{{ $label }}</option>
                    @endforeach
                </select>
                <x-ui.icon name="caret-down" weight="bold" size="sm" class="pointer-events-none absolute right-3 top-1/2 -translate-y-1/2 text-gray-400" />
            </label>

            <label class="relative w-full min-w-0 sm:w-56">
                <span class="sr-only">Branch</span>
                <x-ui.icon name="git-branch" size="base" class="pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-gray-400" />
                <select name="branch" class="{{ $footerSelectClass }}">
                    @foreach ($branches as $value => $label)
                        <option value="{{ $value }}" @selected($selectedBranch === $value)>{{ $label }}</option>
                    @endforeach
                </select>
                <x-ui.icon name="caret-down" weight="bold" size="sm" class="pointer-events-none absolute right-3 top-1/2 -translate-y-1/2 text-gray-400" />
            </label>
        </div>
    </div>
</form>