# Prompts
> Crie confirmações reutilizáveis, páginas de resultado e instâncias isoladas que funcionam tanto em slash quanto em prefixo.

- Área: Componentes
- URL humana: https://ninenity.vercel.app/doc/prompts
- URL Markdown: https://ninenity.vercel.app/doc/markdown/prompts
- Pacotes e conceitos: Prompt, registerPrompt, promptRequest

## Definição e solicitação são separadas

- **Defina uma vez**: O módulo em `Discord/Interactions` monta o prompt e registra um ID estável.
- **Solicite quando precisar**: Um comando chama `interaction.promptRequest(id)` para criar a instância.
- **O Core isola**: Autor, guild e runtime são vinculados para impedir que outra pessoa controle o fluxo.
- **Resolva a escolha**: O botão atualiza para sua página de resultado e executa o callback associado.

> **Por que registrar antes?:** O auto-loader importa `Discord/Interactions` no boot. Quando o comando é executado, a definição já existe e pode ser instanciada sem remontar toda a configuração.

## Prompt simples com opções automáticas

`Prompt.create()` adiciona uma linha de botões com base nas opções informadas.

### Discord/Interactions/DeletePrompt.ts

```text

import {
  ButtonStyle,
  ContainerBuilder,
  MessageBuilder,
  Prompt,
  TextDisplayBuilder
} from '@ninenity/componentbuilder'
import Client from '../../Shared/Settings/Client'

const prompt = Prompt.create({
  id: 'delete-prompt',
  prompt: new MessageBuilder()
    .addComponents(
      new ContainerBuilder()
        .setAccentColor(0xff7582)
        .addTextDisplayComponents(
          new TextDisplayBuilder().setContent(
            '## Excluir projeto?
Esta ação não poderá ser desfeita.'
          )
        )
    )
    .template(),
  options: [
    { id: 'confirm', label: 'Excluir', style: ButtonStyle.Danger },
    { id: 'cancel', label: 'Cancelar', style: ButtonStyle.Secondary }
  ]
})

Client.registerPrompt('delete-prompt', prompt)
```


Sem páginas ou callbacks, uma opção apenas conclui o fluxo. Para executar uma regra e mostrar um resultado diferente, use páginas exportadas ou botões com callback como no próximo exemplo.

## Prompt com páginas de resultado

A primeira página contém os botões; páginas chamadas `yes` e `no` são escolhidas pelo sufixo do `customId`.

### Discord/Interactions/PublishPrompt.ts

```text

const promptPage = new MessageBuilder()
  .addComponents(
    new ContainerBuilder()
      .setAccentColor(0xffc14e)
      .addTextDisplayComponents(
        new TextDisplayBuilder().setContent('Deseja publicar esta versão?')
      )
      .addActionRowComponents(
        new ActionRowBuilder<MessageActionRowComponentBuilder>().addComponents(
          new ButtonBuilder()
            .setStyle(ButtonStyle.Success)
            .setLabel('Publicar')
            .setCustomId('publish-prompt:yes')
            .setCallback(async interaction => {
              await publishVersion(interaction.guildId)
              console.log('[Prompt] versão publicada')
            }),
          new ButtonBuilder()
            .setStyle(ButtonStyle.Secondary)
            .setLabel('Agora não')
            .setCustomId('publish-prompt:no')
        )
      )
  )
  .template()

const successPage = new MessageBuilder()
  .addComponents(new ContainerBuilder().addTextDisplayComponents(
    new TextDisplayBuilder().setContent('✓ Versão publicada com sucesso.')
  ))
  .template()

const canceledPage = new MessageBuilder()
  .addComponents(new ContainerBuilder().addTextDisplayComponents(
    new TextDisplayBuilder().setContent('Publicação cancelada.')
  ))
  .template()

Client.registerPrompt('publish-prompt', [
  { id: 'prompt', name: 'prompt', template: promptPage },
  { id: 'result-yes', name: 'yes', template: successPage },
  { id: 'result-no', name: 'no', template: canceledPage }
])
```


> **Integração com Builder Studio:** A exportação de múltiplas páginas já produz essa lista. Nomeie as páginas de destino com o mesmo sufixo dos botões, por exemplo `yes`, `no`, `approve` ou `cancel`.

## Solicitar no comando

O mesmo ID registrado funciona em qualquer interação normalizada pelo Core.

### Discord/Commands/Publish.ts

```text

Client.slash({
  data: new SlashCommandBuilder()
    .setName('publish')
    .setDescription('Confirma a publicação.'),
  execute: async interaction => {
    await interaction.promptRequest('publish-prompt', {
      ephemeral: true
    })
  }
})

Client.prefix({
  data: new PrefixCommandBuilder()
    .setName('publish')
    .setAliases(['publicar']),
  execute: async interaction => {
    await interaction.promptRequest('publish-prompt', {
      ephemeral: true
    })
  }
})
```


Cada chamada cria uma instância com o autor e a guild da interação. O erro `Prompt "id" is not registered` indica que o módulo não foi carregado, o ID diverge ou o arquivo está fora das pastas percorridas.

## Definição dinâmica por usuário

Registre uma factory quando o texto ou as páginas dependem da interação que abriu o prompt.

### prompt factory

```text

Client.registerPrompt('remove-member', async context => {
  const targetId = context.interaction.options?.getUser('usuario')?.id
  const target = targetId ? await loadMember(targetId) : null

  const page = new MessageBuilder()
    .addComponents(
      new ContainerBuilder().addTextDisplayComponents(
        new TextDisplayBuilder().setContent(
          'Remover **' + (target?.name ?? 'membro desconhecido') + '**?'
        )
      )
    )
    .template()

  return Prompt.create({
    id: 'remove-member',
    prompt: page,
    ownerId: context.ownerId,
    guildId: context.guildId
  })
})
```


> **Não compartilhe estado mutável:** Crie os builders dentro da factory quando dados mudam por usuário. Uma definição global com variáveis externas mutáveis pode vazar conteúdo entre duas solicitações simultâneas.
