Skip to content

Navigators#

A common usecase for buttons is creating paginated button-menus. In miru, these are called navigators, and such functionality is provided by the extension miru.ext.nav.

Note

miru.ext.nav is included with your installation, but is not imported implicitly, thus you have to explicitly import the module.

The base of any navigator is the NavigatorView, a specialized view designed for creating navigators. To get started, it's as easy as creating a new instance of it, turning it into a builder, and sending it to a channel or interaction.

import hikari
import miru
# Import the navigation module
from miru.ext import nav

bot = hikari.GatewayBot("...")

client = miru.Client(bot)

@bot.listen()
async def navigator(event: hikari.MessageCreateEvent) -> None:

    # Do not process messages from bots or webhooks
    if not event.is_human:
        return

    me = bot.get_me()

    # If the bot is mentioned
    if me.id in event.message.user_mentions_ids:
        embed = hikari.Embed(
            title="I'm the second page!",
            description="Also an embed!"
        )

        # A Page object can be used to further customize the page payload
        page = nav.Page(
            content="I'm the last page!",
            embed=hikari.Embed(title="I also have an embed!")
        )

        # The list of pages this navigator should paginate through
        # This should be a list that contains
        # 'str', 'hikari.Embed', or 'nav.Page' objects.
        pages = ["I'm the first page!", embed, page]

        # Define our navigator and pass in our list of pages
        navigator = nav.NavigatorView(pages=pages)

        builder = await navigator.build_response_async(client)
        await builder.send_to_channel(event.channel_id)
        client.start_view(navigator)


bot.run()
async def handle_command(interaction: hikari.CommandInteraction):
    embed = hikari.Embed(
        title="I'm the second page!",
        description="Also an embed!"
    )

    # A Page object can be used to further customize the page payload
    page = nav.Page(
        content="I'm the last page!",
        embed=hikari.Embed(title="I also have an embed!")
    )

    # The list of pages this navigator should paginate through
    # This should be a list that contains
    # 'str', 'hikari.Embed', or 'nav.Page' objects.
    pages = ["I'm the first page!", embed, page]

    # Define our navigator and pass in our list of pages
    navigator = nav.NavigatorView(pages=pages)

    builder = await navigator.build_response_async(client)
    yield builder
    client.start_view(navigator)


async def create_commands(bot: hikari.RESTBot) -> None:
    application = await bot.rest.fetch_application()

    await bot.rest.set_application_commands(
        application=application.id,
        commands=[
            bot.rest.slash_command_builder("test", "My first test command!"),
        ],
    )

bot.add_startup_callback(create_commands)
bot.set_listener(hikari.CommandInteraction, handle_command)

bot.run()
import arc
import hikari
import miru
# Import the navigation module
from miru.ext import nav

bot = hikari.GatewayBot("TOKEN")

arc_client = arc.GatewayClient(bot)
client = miru.Client.from_arc(arc_client)

@arc_client.include
@arc.slash_command("name", "description")
async def my_command(ctx: arc.GatewayContext) -> None:
    embed = hikari.Embed(
        title="I'm the second page!",
        description="Also an embed!"
    )
    # A Page object can be used to further customize the page payload
    page = nav.Page(
        content="I'm the last page!",
        embed=hikari.Embed(title="I also have an embed!")
    )

    # The list of pages this navigator should paginate through
    # This should be a list that contains
    # 'str', 'hikari.Embed', or 'nav.Page' objects.
    pages = ["I'm the first page!", embed, page]

    # Define our navigator and pass in our list of pages
    navigator = nav.NavigatorView(pages=pages)

    builder = await navigator.build_response_async(client)
    await ctx.respond_with_builder(builder)
    client.start_view(navigator)


bot.run()
import arc
import hikari
import miru
# Import the navigation module
from miru.ext import nav

bot = hikari.RESTBot("TOKEN")

arc_client = arc.RESTClient(bot)
client = miru.Client.from_arc(arc_client)

@arc_client.include
@arc.slash_command("name", "description")
async def my_command(ctx: arc.RESTContext) -> None:
    embed = hikari.Embed(
        title="I'm the second page!",
        description="Also an embed!"
    )
    # A Page object can be used to further customize the page payload
    page = nav.Page(
        content="I'm the last page!",
        embed=hikari.Embed(title="I also have an embed!")
    )

    # The list of pages this navigator should paginate through
    # This should be a list that contains
    # 'str', 'hikari.Embed', or 'nav.Page' objects.
    pages = ["I'm the first page!", embed, page]

    # Define our navigator and pass in our list of pages
    navigator = nav.NavigatorView(pages=pages)

    builder = await navigator.build_response_async(client)
    await ctx.respond_with_builder(builder)
    client.start_view(navigator)


bot.run()
import crescent
import hikari
import miru

bot = hikari.GatewayBot("TOKEN")

client = miru.Client(bot)
crescent_client = crescent.Client(bot)

@crescent_client.include
@crescent.command("name", "description")
class SomeSlashCommand:
    async def callback(self, ctx: crescent.Context) -> None:
        embed = hikari.Embed(
            title="I'm the second page!",
            description="Also an embed!"
        )
        # A Page object can be used to further customize the page payload
        page = nav.Page(
            content="I'm the last page!",
            embed=hikari.Embed(title="I also have an embed!")
        )

        # The list of pages this navigator should paginate through
        # This should be a list that contains
        # 'str', 'hikari.Embed', or 'nav.Page' objects.
        pages = ["I'm the first page!", embed, page]

        # Define our navigator and pass in our list of pages
        navigator = nav.NavigatorView(pages=pages)

        builder = await navigator.build_response_async(client)
        await ctx.respond_with_builder(builder)
        client.start_view(navigator)

bot.run()
import crescent
import hikari
import miru

bot = hikari.RESTBot("TOKEN")

client = miru.Client(bot)
crescent_client = crescent.Client(bot)

@crescent_client.include
@crescent.command("name", "description")
class SomeSlashCommand:
    async def callback(self, ctx: crescent.Context) -> None:
        embed = hikari.Embed(
            title="I'm the second page!",
            description="Also an embed!"
        )
        # A Page object can be used to further customize the page payload
        page = nav.Page(
            content="I'm the last page!",
            embed=hikari.Embed(title="I also have an embed!")
        )

        # The list of pages this navigator should paginate through
        # This should be a list that contains
        # 'str', 'hikari.Embed', or 'nav.Page' objects.
        pages = ["I'm the first page!", embed, page]

        # Define our navigator and pass in our list of pages
        navigator = nav.NavigatorView(pages=pages)

        builder = await navigator.build_response_async(client)
        await ctx.respond_with_builder(builder)
        client.start_view(navigator)

bot.run()
import hikari
import lightbulb
import miru
# Import the navigation module
from miru.ext import nav

bot = lightbulb.BotApp("TOKEN")

client = miru.Client(bot)

@bot.command
@lightbulb.command("name", "description", auto_defer=False)
@lightbulb.implements(lightbulb.SlashCommand)
async def my_command(ctx: lightbulb.SlashContext) -> None:
    embed = hikari.Embed(
        title="I'm the second page!",
        description="Also an embed!"
    )
    # A Page object can be used to further customize the page payload
    page = nav.Page(
        content="I'm the last page!",
        embed=hikari.Embed(title="I also have an embed!")
    )

    # The list of pages this navigator should paginate through
    # This should be a list that contains
    # 'str', 'hikari.Embed', or 'nav.Page' objects.
    pages = ["I'm the first page!", embed, page]

    # Define our navigator and pass in our list of pages
    navigator = nav.NavigatorView(pages=pages)

    builder = await navigator.build_response_async(client)
    await builder.create_initial_response(ctx.interaction)
    # Or in a prefix command:
    # await builder.send_to_channel(ctx.channel_id)

    client.start_view(navigator)


bot.run()
import hikari
import miru
import tanjun
# Import the navigation module
from miru.ext import nav

bot = hikari.GatewayBot("TOKEN")

tanjun_client = tanjun.Client.from_gateway_bot(bot)
client = miru.Client.from_tanjun(tanjun_client)

@tanjun.as_slash_command("name", "description")
async def some_slash_command(ctx: tanjun.abc.SlashContext) -> None:
    embed = hikari.Embed(
        title="I'm the second page!",
        description="Also an embed!"
    )
    # A Page object can be used to further customize the page payload
    page = nav.Page(
        content="I'm the last page!",
        embed=hikari.Embed(title="I also have an embed!")
    )

    # The list of pages this navigator should paginate through
    # This should be a list that contains
    # 'str', 'hikari.Embed', or 'nav.Page' objects.
    pages = ["I'm the first page!", embed, page]

    # Define our navigator and pass in our list of pages
    navigator = nav.NavigatorView(pages=pages)

    builder = await navigator.build_response_async(client)
    # the builder has specific adapters for tanjun
    await builder.respond_with_tanjun(ctx)
    client.start_view(navigator)


bot.run()
import hikari
import miru
import tanjun
# Import the navigation module
from miru.ext import nav

bot = hikari.RESTBot("TOKEN")

tanjun_client = tanjun.Client.from_rest_bot(bot)
client = miru.Client.from_tanjun(tanjun_client)

@tanjun.as_slash_command("name", "description")
async def some_slash_command(ctx: tanjun.abc.SlashContext) -> None:
    embed = hikari.Embed(
        title="I'm the second page!",
        description="Also an embed!"
    )
    # A Page object can be used to further customize the page payload
    page = nav.Page(
        content="I'm the last page!",
        embed=hikari.Embed(title="I also have an embed!")
    )

    # The list of pages this navigator should paginate through
    # This should be a list that contains
    # 'str', 'hikari.Embed', or 'nav.Page' objects.
    pages = ["I'm the first page!", embed, page]

    # Define our navigator and pass in our list of pages
    navigator = nav.NavigatorView(pages=pages)

    builder = await navigator.build_response_async(client)
    # the builder has specific adapters for tanjun
    await builder.respond_with_tanjun(ctx)
    client.start_view(navigator)


bot.run()

Tip

If you want to send a navigator in response to a miru item being interacted with, you may use Context.respond_with_builder().

Customizing Navigation Buttons#

If you would like to customize the items used by the navigator, you can pass buttons & selects via the items= keyword-only argument. This should be a sequence of NavItem (for a full list, see here).

There are also some built-in navigation buttons, these are:

You may use any mix of the built-in and custom navigation buttons in your navigator views.

Let's define a custom navigation button:

class MyIndicatorButton(nav.NavButton):
    def __init__(self):
        super().__init__(label="Page: 1", row=1)

    async def callback(self, ctx: miru.ViewContext) -> None:
        await ctx.respond("You clicked me!", flags=hikari.MessageFlag.EPHEMERAL)

    async def before_page_change(self) -> None:
        # This function is called before the new page is sent by the navigator
        self.label = f"Page: {self.view.current_page+1}"

Then we can add it to our Navigator before sending:

embed = hikari.Embed(title="I'm the second page!", description="Also an embed!")
pages = ["I'm a customized navigator!", embed, "I'm the last page!"]

# Define our custom buttons for this navigator, keep in mind the order
# All navigator items MUST subclass nav.NavItem
items: list[nav.NavItem] = [
    nav.PrevButton(),
    nav.StopButton(),
    nav.NextButton(),
    MyNavButton()
]

# Pass our list of NavButton to the navigator
navigator = nav.NavigatorView(pages=pages, items=items)

# ... Send the navigator