Skip to content
Brok UI

Loading…

No results

AI Tools

Open source

AI tool surfaces — text writer, image generator, code generator, an assistant chat, a full-height inbox rail, and a model playground, each with live output panels.

Preview

5 of 7

resources/views/blocks/ai-tools-inbox.blade.php Blade
@props([
    'title' => 'AI Inbox',
    // Shows a close/collapse icon button in the header that dispatches
    // `ai-tools-inbox:close` for the host page to act on (e.g. hide the rail).
    'dismissible' => false,
    // Conversation turns: [{ role: 'user'|'other'|'system', text: '…' }, …].
    // Without any, the panel shows a quiet empty state with suggestion chips.
    'messages' => null,
])

@php
    $hasMessages = is_array($messages) && ! empty($messages);
    $threadMessages = $hasMessages
        ? collect($messages)->map(fn ($m) => [
            'role' => $m['role'] ?? 'user',
            'name' => $m['name'] ?? null,
            'avatar' => $m['avatar'] ?? null,
            'content' => $m['text'] ?? ($m['content'] ?? ''),
        ])->all()
        : [];
@endphp

{{--
    AI Tools Inbox — a full-height assistant rail meant to sit beside an app
    (a right column), not a self-contained card like ai-tools-chat. A fixed
    header and composer bracket a scrollable conversation: an empty state
    with suggestion chips by default, or the supplied `messages` rendered
    through chat-thread. Picking a suggestion fills the composer and moves
    focus into it (suggestion-chips-select bubbles up to the root and writes
    straight into the ai-prompt textarea so its own Alpine state stays the
    single source of truth).
--}}
<section
    data-slot="block-ai-tools-inbox"
    x-data="{
        fillComposer(text) {
            const textarea = this.$el.querySelector('[data-slot=\'ai-prompt\'] textarea');
            if (! textarea) return;
            textarea.value = text;
            textarea.dispatchEvent(new Event('input', { bubbles: true }));
            textarea.focus();
        },
    }"
    @suggestion-chips-select="fillComposer($event.detail.value)"
    {{ $attributes->merge(['class' => 'flex h-full min-h-[640px] w-full flex-col bg-background text-foreground']) }}
>
    {{-- Header --}}
    <header class="flex shrink-0 items-center justify-between gap-2 px-4 py-4">
        <h2 class="truncate text-lg font-semibold text-foreground">{{ __($title) }}</h2>
        @if ($dismissible)
            <x-ui.button
                variant="ghost"
                size="sm"
                :icon-label="__('Close')"
                @click="$dispatch('ai-tools-inbox:close')"
                class="text-muted-foreground"
            >
                <svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="size-4"><path d="M18 6 6 18M6 6l12 12" /></svg>
            </x-ui.button>
        @endif
    </header>

    {{-- Conversation --}}
    <div class="min-h-0 flex-1 overflow-y-auto px-4 py-6" role="region" aria-label="{{ __('Conversation') }}">
        @if ($hasMessages)
            <x-ui.chat-thread :messages="$threadMessages" />
        @else
            <div class="flex h-full flex-col items-center justify-center gap-4 text-center">
                <span class="ui-ai-spark grid size-10 shrink-0 place-items-center rounded-full" aria-hidden="true">
                    <svg class="size-5" viewBox="0 0 24 24" fill="currentColor"><path d="M12 2l1.9 5.1L19 9l-5.1 1.9L12 16l-1.9-5.1L5 9l5.1-1.9L12 2z" /></svg>
                </span>
                <p class="max-w-[26ch] text-sm text-muted-foreground">{{ __('Ask about your spending, goals or transactions') }}</p>
                <x-ui.suggestion-chips
                    class="justify-center"
                    :items="[
                        __('Where did I overspend this month?'),
                        __('Am I on track for my goal?'),
                        __('Explain this transaction'),
                    ]"
                />
            </div>
        @endif
    </div>

    {{-- Composer --}}
    <footer class="shrink-0 px-4 py-4">
        <x-ui.ai.prompt
            placeholder="{{ __('Ask me anything') }}"
            :attachments="false"
            improve
        >
            <x-slot:actions>
                <button
                    type="button"
                    class="inline-flex size-8 items-center justify-center rounded-full text-muted-foreground transition-colors hover:bg-accent hover:text-foreground focus-visible:outline-none focus-visible:ring-[length:var(--ring-width)] focus-visible:ring-ring motion-reduce:transition-none"
                    title="{{ __('Add link') }}"
                >
                    <span class="sr-only">{{ __('Add link') }}</span>
                    <svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" class="size-4">
                        <path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" />
                        <path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" />
                    </svg>
                </button>
                <button
                    type="button"
                    class="inline-flex size-8 items-center justify-center rounded-full text-muted-foreground transition-colors hover:bg-accent hover:text-foreground focus-visible:outline-none focus-visible:ring-[length:var(--ring-width)] focus-visible:ring-ring motion-reduce:transition-none"
                    title="{{ __('Add image') }}"
                >
                    <span class="sr-only">{{ __('Add image') }}</span>
                    <svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" class="size-4">
                        <rect x="3" y="3" width="18" height="18" rx="2" />
                        <circle cx="9" cy="9" r="2" />
                        <path d="m21 15-5-5L5 21" />
                    </svg>
                </button>
            </x-slot:actions>

            <x-slot:footer>
                <span class="inline-flex min-w-0 items-center gap-2 truncate">
                    <svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" class="size-4 shrink-0"><rect x="3" y="3" width="7" height="7" rx="1.5" /><rect x="14" y="3" width="7" height="7" rx="1.5" /><rect x="3" y="14" width="7" height="7" rx="1.5" /><rect x="14" y="14" width="7" height="7" rx="1.5" /></svg>
                    {{ __('Connect apps') }}
                </span>
                <span class="flex shrink-0 items-center gap-2" aria-hidden="true">
                    @foreach ([
                        ['label' => __('Calendar'), 'path' => '<rect x="3" y="4" width="18" height="17" rx="2"/><path d="M8 2v4M16 2v4M3 10h18"/>'],
                        ['label' => __('Mail'), 'path' => '<rect x="3" y="5" width="18" height="14" rx="2"/><path d="m3 7 9 6 9-6"/>'],
                        ['label' => __('Files'), 'path' => '<path d="M4 4h6l2 3h8a1 1 0 0 1 1 1v10a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1Z"/>'],
                    ] as $app)
                        <span
                            class="grid size-6 place-items-center rounded-sm border border-border bg-muted text-muted-foreground"
                            title="{{ $app['label'] }}"
                        >
                            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="size-4">{!! $app['path'] !!}</svg>
                        </span>
                    @endforeach
                </span>
            </x-slot:footer>
        </x-ui.ai.prompt>
    </footer>
</section>

Installation

terminal
php artisan ui:add blocks/ai-tools-inbox

Registry contract

Install confidence

php artisan ui:add blocks/ai-tools-inbox writes only the generated targets below. The CLI validates each file hash before writing and prompts before replacing local changes unless --force is used.

Version
1.0.1
License
open
Stability
stable
Contract
v1
Foundation
≥ 1.0.0
Type Generated target
blade resources/views/blocks/ai-tools-inbox.blade.php

Registry dependencies

button chat-thread chat-bubble suggestion-chips ai-prompt

Package dependencies

composer: jml/brok:^0.2

npm: alpinejs

Use with AI

A brief for your coding agent: what the block is, the install command, how to render it, its props and the rules. Copy it, or open a prompt about this block in an assistant.

ai-tools-inbox.md
# Brok UI block: AI Tools Inbox (`ai-tools-inbox`)

A full-height assistant rail for the side of an app: a header, a scrollable conversation that starts with a quiet empty state and suggestion chips, and a composer pinned to the bottom with link/image actions and a "Connect apps" footer strip.

Brok UI is a Laravel Blade registry. `ui:add` copies this block into the app as plain Blade the app owns; it composes installed `<brok:*>` primitives and semantic design tokens.

## Install

```bash
php artisan ui:add blocks/ai-tools-inbox
```

## Render it

```blade
<x-blocks.ai-tools-inbox />
```

## Props

- `title` (string, default `AI Inbox`)
- `dismissible` (bool, default `false`) — Shows a close icon button in the header that dispatches ai-tools-inbox:close for the host page to act on.
- `messages` (array|null, default `null`) — Conversation turns: [{ role: 'user'|'other'|'system', text }]. Without any, the panel shows an empty state with suggestion chips.

## Use when

- Adding a persistent assistant column beside an app's main content — a dashboard, an inbox, a workspace — instead of a modal or a standalone chat page.
- Starting the panel empty with a short prompt and suggestion chips before any conversation exists.

## Avoid when

- Use ai-tools-chat for a self-contained chat panel that reads as its own card (a demo surface, a dedicated chat page) rather than a rail that fills a column.
- Use ai-tools-playground for a settings-plus-conversation layout.

## Rules

- Render the installed block with `<x-blocks.ai-tools-inbox />` and pass data through its props; edit the copied file only for structural changes.
- Keep the semantic design tokens (`bg-background`, `text-muted-foreground`); never swap in raw colour utilities.
- Keep the `data-slot` attributes and the logical (start/end) spacing so the markup still mirrors under `dir="rtl"`.

## Links

- Docs: https://brokui.dev/blocks/ai-tools-inbox
- Registry JSON (files, props, contract): https://brokui.dev/r/open/blocks/ai-tools-inbox.json

Working in Claude Code, Cursor or Codex? Give the agent the whole registry through the MCP server or the Brok UI skill.

Guidance

Use when

  • Adding a persistent assistant column beside an app's main content — a dashboard, an inbox, a workspace — instead of a modal or a standalone chat page.
  • Starting the panel empty with a short prompt and suggestion chips before any conversation exists.

Avoid when

  • Use ai-tools-chat for a self-contained chat panel that reads as its own card (a demo surface, a dedicated chat page) rather than a rail that fills a column.
  • Use ai-tools-playground for a settings-plus-conversation layout.

Anti-patterns

  • Do not replace semantic props with conflicting utility classes.
  • Do not remove labels, focus styles, or state attributes.
Anatomy
root block-ai-tools-inbox

Usage

Render the block as a component, passing data where useful:

resources/views/welcome.blade.php Blade
<x-blocks.ai-tools-inbox />

Built from primitives

Composes <brok:*> Semantic tokens Re-themeable RTL-ready

This block composes installed <brok:*> primitives and semantic design tokens only — it does not reimplement any primitive. Re-theme it (light, dark, admin, customer) by editing CSS variables; flip the preview to RTL to confirm it mirrors.

Source

The exact, editable file ui:add writes into your app. The preview above renders this same source — there are no preview-only blocks.

resources/views/blocks/ai-tools-inbox.blade.php Blade
@props([
    'title' => 'AI Inbox',
    // Shows a close/collapse icon button in the header that dispatches
    // `ai-tools-inbox:close` for the host page to act on (e.g. hide the rail).
    'dismissible' => false,
    // Conversation turns: [{ role: 'user'|'other'|'system', text: '…' }, …].
    // Without any, the panel shows a quiet empty state with suggestion chips.
    'messages' => null,
])

@php
    $hasMessages = is_array($messages) && ! empty($messages);
    $threadMessages = $hasMessages
        ? collect($messages)->map(fn ($m) => [
            'role' => $m['role'] ?? 'user',
            'name' => $m['name'] ?? null,
            'avatar' => $m['avatar'] ?? null,
            'content' => $m['text'] ?? ($m['content'] ?? ''),
        ])->all()
        : [];
@endphp

{{--
    AI Tools Inbox — a full-height assistant rail meant to sit beside an app
    (a right column), not a self-contained card like ai-tools-chat. A fixed
    header and composer bracket a scrollable conversation: an empty state
    with suggestion chips by default, or the supplied `messages` rendered
    through chat-thread. Picking a suggestion fills the composer and moves
    focus into it (suggestion-chips-select bubbles up to the root and writes
    straight into the ai-prompt textarea so its own Alpine state stays the
    single source of truth).
--}}
<section
    data-slot="block-ai-tools-inbox"
    x-data="{
        fillComposer(text) {
            const textarea = this.$el.querySelector('[data-slot=\'ai-prompt\'] textarea');
            if (! textarea) return;
            textarea.value = text;
            textarea.dispatchEvent(new Event('input', { bubbles: true }));
            textarea.focus();
        },
    }"
    @suggestion-chips-select="fillComposer($event.detail.value)"
    {{ $attributes->merge(['class' => 'flex h-full min-h-[640px] w-full flex-col bg-background text-foreground']) }}
>
    {{-- Header --}}
    <header class="flex shrink-0 items-center justify-between gap-2 px-4 py-4">
        <h2 class="truncate text-lg font-semibold text-foreground">{{ __($title) }}</h2>
        @if ($dismissible)
            <x-ui.button
                variant="ghost"
                size="sm"
                :icon-label="__('Close')"
                @click="$dispatch('ai-tools-inbox:close')"
                class="text-muted-foreground"
            >
                <svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="size-4"><path d="M18 6 6 18M6 6l12 12" /></svg>
            </x-ui.button>
        @endif
    </header>

    {{-- Conversation --}}
    <div class="min-h-0 flex-1 overflow-y-auto px-4 py-6" role="region" aria-label="{{ __('Conversation') }}">
        @if ($hasMessages)
            <x-ui.chat-thread :messages="$threadMessages" />
        @else
            <div class="flex h-full flex-col items-center justify-center gap-4 text-center">
                <span class="ui-ai-spark grid size-10 shrink-0 place-items-center rounded-full" aria-hidden="true">
                    <svg class="size-5" viewBox="0 0 24 24" fill="currentColor"><path d="M12 2l1.9 5.1L19 9l-5.1 1.9L12 16l-1.9-5.1L5 9l5.1-1.9L12 2z" /></svg>
                </span>
                <p class="max-w-[26ch] text-sm text-muted-foreground">{{ __('Ask about your spending, goals or transactions') }}</p>
                <x-ui.suggestion-chips
                    class="justify-center"
                    :items="[
                        __('Where did I overspend this month?'),
                        __('Am I on track for my goal?'),
                        __('Explain this transaction'),
                    ]"
                />
            </div>
        @endif
    </div>

    {{-- Composer --}}
    <footer class="shrink-0 px-4 py-4">
        <x-ui.ai.prompt
            placeholder="{{ __('Ask me anything') }}"
            :attachments="false"
            improve
        >
            <x-slot:actions>
                <button
                    type="button"
                    class="inline-flex size-8 items-center justify-center rounded-full text-muted-foreground transition-colors hover:bg-accent hover:text-foreground focus-visible:outline-none focus-visible:ring-[length:var(--ring-width)] focus-visible:ring-ring motion-reduce:transition-none"
                    title="{{ __('Add link') }}"
                >
                    <span class="sr-only">{{ __('Add link') }}</span>
                    <svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" class="size-4">
                        <path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" />
                        <path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" />
                    </svg>
                </button>
                <button
                    type="button"
                    class="inline-flex size-8 items-center justify-center rounded-full text-muted-foreground transition-colors hover:bg-accent hover:text-foreground focus-visible:outline-none focus-visible:ring-[length:var(--ring-width)] focus-visible:ring-ring motion-reduce:transition-none"
                    title="{{ __('Add image') }}"
                >
                    <span class="sr-only">{{ __('Add image') }}</span>
                    <svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" class="size-4">
                        <rect x="3" y="3" width="18" height="18" rx="2" />
                        <circle cx="9" cy="9" r="2" />
                        <path d="m21 15-5-5L5 21" />
                    </svg>
                </button>
            </x-slot:actions>

            <x-slot:footer>
                <span class="inline-flex min-w-0 items-center gap-2 truncate">
                    <svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" class="size-4 shrink-0"><rect x="3" y="3" width="7" height="7" rx="1.5" /><rect x="14" y="3" width="7" height="7" rx="1.5" /><rect x="3" y="14" width="7" height="7" rx="1.5" /><rect x="14" y="14" width="7" height="7" rx="1.5" /></svg>
                    {{ __('Connect apps') }}
                </span>
                <span class="flex shrink-0 items-center gap-2" aria-hidden="true">
                    @foreach ([
                        ['label' => __('Calendar'), 'path' => '<rect x="3" y="4" width="18" height="17" rx="2"/><path d="M8 2v4M16 2v4M3 10h18"/>'],
                        ['label' => __('Mail'), 'path' => '<rect x="3" y="5" width="18" height="14" rx="2"/><path d="m3 7 9 6 9-6"/>'],
                        ['label' => __('Files'), 'path' => '<path d="M4 4h6l2 3h8a1 1 0 0 1 1 1v10a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1Z"/>'],
                    ] as $app)
                        <span
                            class="grid size-6 place-items-center rounded-sm border border-border bg-muted text-muted-foreground"
                            title="{{ $app['label'] }}"
                        >
                            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="size-4">{!! $app['path'] !!}</svg>
                        </span>
                    @endforeach
                </span>
            </x-slot:footer>
        </x-ui.ai.prompt>
    </footer>
</section>

Ownership & lifecycle

Owner, release state, review evidence and adoption for this item.
Owner
Platform UI (@JoshJML)
Current version
1.0.1
Status
Stable
License
open
Deprecation
Not deprecated
Contract
v1
Foundation
≥ 1.0.0