{"_id":"@amatiscorp/pagincord","_rev":"3-581bcbc389b8200a576115a7b36bb6c1","name":"@amatiscorp/pagincord","dist-tags":{"latest":"2.2.0"},"versions":{"2.0.0":{"name":"@amatiscorp/pagincord","version":"2.0.0","keywords":["discord","discordjs","pagination","paginator","embeds","embed","discord-pagination","buttons","select-menu","components","slash-commands","discord-v14","discord.js","typescript","i18n","amatiscorp"],"author":{"name":"Amatis Corp","email":"dymidless@gmail.com"},"license":"MIT","_id":"@amatiscorp/pagincord@2.0.0","maintainers":[{"name":"clasifed","email":"clasifed@proton.me"}],"homepage":"https://github.com/amatiscorp/pagincord#readme","bugs":{"url":"https://github.com/amatiscorp/pagincord/issues"},"dist":{"shasum":"c344cbdc84e65e9e6f90ba9e7a811a161ab06d82","tarball":"https://registry.npmjs.org/@amatiscorp/pagincord/-/pagincord-2.0.0.tgz","fileCount":9,"integrity":"sha512-ZzjsMdF4kremyGvi9GQnVSqsCgVxjKpy44Ae3zvaLfKnDGyJq7OSGCPREQ/857XtCQOcWhNtCwZ9IIQVUChcYw==","signatures":[{"sig":"MEUCIBsEjLvRXlicg9NMBDTtlPVI8Z8LiqI3UhYfpWbLK2SaAiEA+V+KDzunuLYEBjYkIZwcZbjnOejcHNZ73dlI2PMKxN0=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":293898},"main":"./dist/index.js","types":"./dist/index.d.ts","module":"./dist/index.mjs","engines":{"node":">=16.11.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.js"}},"gitHead":"ab9b1522e159976f29e8f0a2013f52fbe2c68f14","scripts":{"dev":"tsup --watch","build":"tsup","prepublishOnly":"npm run build"},"_npmUser":{"name":"clasifed","email":"clasifed@proton.me"},"repository":{"url":"git+https://github.com/amatiscorp/pagincord.git","type":"git"},"_npmVersion":"11.17.0","description":"Smart, fully configurable embed pagination for Discord.js v14+. Buttons, select menus, jump modal, EN/ES locales, events, and list helpers.","directories":{},"sideEffects":false,"_nodeVersion":"24.19.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.0.1","discord.js":"^14.14.1","typescript":"^5.3.3","@types/node":"^20.10.0"},"peerDependencies":{"discord.js":"^14.0.0"},"_npmOperationalInternal":{"tmp":"tmp/pagincord_2.0.0_1788090423461_0.7830621834641918","host":"s3://npm-registry-packages-npm-production"}},"2.1.0":{"name":"@amatiscorp/pagincord","version":"2.1.0","keywords":["discord","discordjs","pagination","paginator","embeds","embed","discord-pagination","buttons","select-menu","components","slash-commands","discord-v14","discord.js","typescript","i18n","amatiscorp"],"author":{"name":"Amatis Corp","email":"dymidless@gmail.com"},"license":"MIT","_id":"@amatiscorp/pagincord@2.1.0","maintainers":[{"name":"clasifed","email":"clasifed@proton.me"}],"homepage":"https://github.com/amatiscorp/pagincord#readme","bugs":{"url":"https://github.com/amatiscorp/pagincord/issues"},"dist":{"shasum":"f62053b27648b7d9bbcc496409bc238ae533dafd","tarball":"https://registry.npmjs.org/@amatiscorp/pagincord/-/pagincord-2.1.0.tgz","fileCount":9,"integrity":"sha512-zLaYW0RX7jedAQTAIM41nQ5+paN5chhVe4WfOq7CVA7X3oyLZUxCf4uJNy8fYMoYITsTNeMvlMpV26Df/siICQ==","signatures":[{"sig":"MEUCIQC0dtB8KSyVv3hcUc5vjX/+YR0VU9u+wTFTrl6T1zAJfwIgHkEuJX1S4xCeri8biEGV24EQNjiepqm6iDVAwvRUQcE=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":413638},"main":"./dist/index.js","types":"./dist/index.d.ts","module":"./dist/index.mjs","engines":{"node":">=16.11.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.js"}},"gitHead":"ab9b1522e159976f29e8f0a2013f52fbe2c68f14","scripts":{"dev":"tsup --watch","build":"tsup","prepublishOnly":"npm run build"},"_npmUser":{"name":"clasifed","email":"clasifed@proton.me"},"repository":{"url":"git+https://github.com/amatiscorp/pagincord.git","type":"git"},"_npmVersion":"11.17.0","description":"Smart, fully configurable embed pagination for Discord.js v14+. Themes, search, numbered buttons, EN/ES/PT/FR/DE locales, and list helpers.","directories":{},"sideEffects":false,"_nodeVersion":"24.19.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.0.1","discord.js":"^14.14.1","typescript":"^5.3.3","@types/node":"^20.10.0"},"peerDependencies":{"discord.js":"^14.0.0"},"_npmOperationalInternal":{"tmp":"tmp/pagincord_2.1.0_1788243014517_0.42503287032867254","host":"s3://npm-registry-packages-npm-production"}},"2.2.0":{"name":"@amatiscorp/pagincord","version":"2.2.0","description":"Smart, fully configurable embed pagination for Discord.js v14+. Lazy pages, progress bars, search, themes, and EN/ES/PT/FR/DE/IT locales.","main":"./dist/index.js","module":"./dist/index.mjs","types":"./dist/index.d.ts","sideEffects":false,"exports":{".":{"types":"./dist/index.d.ts","require":"./dist/index.js","import":"./dist/index.mjs"}},"scripts":{"build":"tsup","dev":"tsup --watch","prepublishOnly":"npm run build"},"keywords":["discord","discordjs","pagination","paginator","embeds","embed","discord-pagination","buttons","select-menu","components","slash-commands","discord-v14","discord.js","typescript","i18n","amatiscorp"],"author":{"name":"Amatis Corp","email":"dymidless@gmail.com"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/amatiscorp/pagincord.git"},"homepage":"https://github.com/amatiscorp/pagincord#readme","bugs":{"url":"https://github.com/amatiscorp/pagincord/issues"},"publishConfig":{"access":"public"},"peerDependencies":{"discord.js":"^14.0.0"},"devDependencies":{"@types/node":"^20.10.0","discord.js":"^14.14.1","tsup":"^8.0.1","typescript":"^5.3.3"},"engines":{"node":">=16.11.0"},"gitHead":"cff8d9a517d50603b2d129903715279dcaac435f","_id":"@amatiscorp/pagincord@2.2.0","_nodeVersion":"24.19.0","_npmVersion":"11.17.0","dist":{"integrity":"sha512-BZ7wPAxioJAfYGBYIVxiHHjqffzYSQMooKgmOUmmcnXd0mFjIhqGL3fB0ssmQz4ya4BKpbZLamvJeMXkCRLMmw==","shasum":"b706d08a2c7b90c228df72c0ab82055e829622e3","tarball":"https://registry.npmjs.org/@amatiscorp/pagincord/-/pagincord-2.2.0.tgz","fileCount":9,"unpackedSize":504813,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQDeqPQVEbHcbMo2PMPi8SPTcv/zwtGmyh6BW+HEL11xUwIhAOVlUqKTcrEFs19UBFi2mayAZpJGEu54b+cU41Xr/2oV"}]},"_npmUser":{"name":"clasifed","email":"clasifed@proton.me"},"directories":{},"maintainers":[{"name":"clasifed","email":"clasifed@proton.me"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/pagincord_2.2.0_1788434722287_0.6317271367212054"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-30T11:47:03.228Z","modified":"2026-09-03T11:25:22.639Z","2.0.0":"2026-08-30T11:47:03.603Z","2.1.0":"2026-09-01T06:10:14.733Z","2.2.0":"2026-09-03T11:25:22.413Z"},"bugs":{"url":"https://github.com/amatiscorp/pagincord/issues"},"author":{"name":"Amatis Corp","email":"dymidless@gmail.com"},"license":"MIT","homepage":"https://github.com/amatiscorp/pagincord#readme","keywords":["discord","discordjs","pagination","paginator","embeds","embed","discord-pagination","buttons","select-menu","components","slash-commands","discord-v14","discord.js","typescript","i18n","amatiscorp"],"repository":{"type":"git","url":"git+https://github.com/amatiscorp/pagincord.git"},"description":"Smart, fully configurable embed pagination for Discord.js v14+. Lazy pages, progress bars, search, themes, and EN/ES/PT/FR/DE/IT locales.","maintainers":[{"name":"clasifed","email":"clasifed@proton.me"}],"readme":"<div align=\"center\">\r\n\r\n# Pagincord\r\n\r\n**Smart, fully configurable embed pagination for Discord.js v14+**\r\n\r\nby [Amatis Corp](https://github.com/amatiscorp)\r\n\r\nButtons · Lazy pages · Progress bar · Search · Themes · EN / ES / PT / FR / DE / IT · TypeScript\r\n\r\n[![npm version](https://img.shields.io/npm/v/@amatiscorp/pagincord.svg?style=flat-square)](https://www.npmjs.com/package/@amatiscorp/pagincord)\r\n[![npm downloads](https://img.shields.io/npm/dm/@amatiscorp/pagincord.svg?style=flat-square)](https://www.npmjs.com/package/@amatiscorp/pagincord)\r\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square)](https://opensource.org/licenses/MIT)\r\n[![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue.svg?style=flat-square)](https://www.typescriptlang.org/)\r\n\r\n[Install](#installation) · [Quick start](#quick-start) · [Locales](#languages-en--es) · [Configure](#global-defaults) · [API](#api-reference) · [Español](#español)\r\n\r\n</div>\r\n\r\n---\r\n\r\nPagincord turns an array of embeds into an interactive Discord message. You control the language, buttons, labels, timeout, who can click, and what happens when it ends.\r\n\r\nWorks with slash commands, buttons, select menus, and regular messages.\r\n\r\n**Requires:** Node.js `>=16.11` and `discord.js` `^14`.\r\n\r\n**Package:** [`@amatiscorp/pagincord`](https://www.npmjs.com/package/@amatiscorp/pagincord)  \r\n**Repo:** [github.com/amatiscorp/pagincord](https://github.com/amatiscorp/pagincord)\r\n\r\n---\r\n\r\n## Installation\r\n\r\n```bash\r\nnpm install @amatiscorp/pagincord discord.js\r\n```\r\n\r\n```bash\r\nyarn add @amatiscorp/pagincord discord.js\r\n```\r\n\r\n```bash\r\npnpm add @amatiscorp/pagincord discord.js\r\n```\r\n\r\n`discord.js` is a peer dependency. Install it in your bot as well.\r\n\r\n```javascript\r\nconst { Paginator, paginate, configure } = require('@amatiscorp/pagincord');\r\n```\r\n\r\n```typescript\r\nimport { Paginator, paginate, configure } from '@amatiscorp/pagincord';\r\n```\r\n\r\n---\r\n\r\n## Quick start\r\n\r\n### Slash command\r\n\r\n```javascript\r\nconst { Paginator } = require('@amatiscorp/pagincord');\r\nconst { EmbedBuilder } = require('discord.js');\r\n\r\nclient.on('interactionCreate', async (interaction) => {\r\n  if (!interaction.isChatInputCommand()) return;\r\n  if (interaction.commandName !== 'help') return;\r\n\r\n  const paginator = new Paginator({\r\n    embeds: [\r\n      new EmbedBuilder().setTitle('Page 1').setDescription('Welcome').setColor(0x5865f2),\r\n      new EmbedBuilder().setTitle('Page 2').setDescription('Commands').setColor(0x57f287),\r\n      new EmbedBuilder().setTitle('Page 3').setDescription('Support').setColor(0xfee75c),\r\n    ],\r\n    authorId: interaction.user.id,\r\n    locale: 'en',\r\n  });\r\n\r\n  await paginator.start(interaction);\r\n});\r\n```\r\n\r\n### One-liner\r\n\r\n```javascript\r\nconst { paginate } = require('@amatiscorp/pagincord');\r\n\r\nawait paginate(interaction, {\r\n  embeds: pages,\r\n  authorId: interaction.user.id,\r\n  locale: 'en',\r\n});\r\n```\r\n\r\n### Prefix command\r\n\r\n```javascript\r\nconst { Paginator } = require('@amatiscorp/pagincord');\r\n\r\nclient.on('messageCreate', async (message) => {\r\n  if (message.content !== '!pages') return;\r\n\r\n  await new Paginator({\r\n    embeds: [\r\n      { title: 'Page 1', description: 'Hello', color: 0x00ff00 },\r\n      { title: 'Page 2', description: 'World', color: 0x0000ff },\r\n    ],\r\n    authorId: message.author.id,\r\n  }).start(message);\r\n});\r\n```\r\n\r\n---\r\n\r\n## Languages\r\n\r\nBuilt-in UI strings (errors, footer, select, jump modal, search, empty list, button labels) ship in:\r\n\r\n| Code | Language |\r\n|------|----------|\r\n| `en` | English (default) |\r\n| `es` | Español |\r\n| `pt` | Português (`pt-BR` also works) |\r\n| `fr` | Français |\r\n| `de` | Deutsch |\r\n| `it` | Italiano |\r\n\r\n```javascript\r\n// English (default)\r\nawait paginate(interaction, { embeds, locale: 'en' });\r\n\r\n// Español — modal, errores, footer y select en español\r\nawait paginate(interaction, {\r\n  embeds,\r\n  locale: 'es',\r\n  showButtonLabels: true, // First → Inicio, Back → Atrás, …\r\n});\r\n```\r\n\r\nSet it once for the whole bot:\r\n\r\n```javascript\r\nconst { configure } = require('@amatiscorp/pagincord');\r\n\r\nconfigure({\r\n  locale: 'es',\r\n  showButtonLabels: true,\r\n  timeout: 120_000,\r\n});\r\n```\r\n\r\nOverride a single string:\r\n\r\n```javascript\r\nawait paginate(interaction, {\r\n  embeds,\r\n  locale: 'es',\r\n  texts: {\r\n    unauthorized: 'Solo el autor del comando puede usar estos botones.',\r\n  },\r\n});\r\n```\r\n\r\nAdd another language:\r\n\r\n```javascript\r\nconst { defineLocale } = require('@amatiscorp/pagincord');\r\n\r\ndefineLocale('fr', {\r\n  unauthorized: \"Vous ne pouvez pas contrôler cette pagination.\",\r\n  pageLabel: 'Page {page} sur {total}',\r\n  selectPlaceholder: 'Page {page} sur {total}',\r\n  selectEnded: 'Pagination terminée',\r\n  selectOption: '{page}. {title}',\r\n  fallbackTitle: 'Page {page}',\r\n  jumpModalTitle: 'Aller à la page',\r\n  jumpModalLabel: 'Numéro de page (1-{total})',\r\n  jumpModalPlaceholder: 'Actuellement {page} sur {total}',\r\n  jumpModalInvalid: 'Entrez un nombre entre 1 et {total}.',\r\n  emptyList: 'Aucun élément à afficher.',\r\n  indicator: '{page} / {total}',\r\n  buttons: {\r\n    first: 'Début',\r\n    previous: 'Retour',\r\n    next: 'Suivant',\r\n    last: 'Fin',\r\n    stop: 'Fermer',\r\n  },\r\n});\r\n```\r\n\r\nTokens you can use in templates: `{page}` `{total}` `{title}`.\r\n\r\n---\r\n\r\n## Global defaults\r\n\r\n```javascript\r\nconst { configure, setLocale, Paginator } = require('@amatiscorp/pagincord');\r\n\r\nconfigure({\r\n  locale: 'en',\r\n  timeout: 90_000,\r\n  loop: false,\r\n  useSelectMenu: false,\r\n  endBehavior: 'disable',\r\n  autoFooter: false,\r\n  showButtonLabels: false,\r\n  hideEmojis: false,\r\n  preset: 'full',\r\n  ephemeral: false,\r\n});\r\n\r\n// aliases\r\nsetLocale('es');\r\nPaginator.configure({ locale: 'en' });\r\n```\r\n\r\nInstance options always win over `configure()`.\r\n\r\n---\r\n\r\n## Presets\r\n\r\n| Preset | Buttons | Select menu |\r\n|--------|---------|-------------|\r\n| `full` (default) | First · Previous · Stop · Next · Last | no |\r\n| `compact` | Previous · `1 / 5` · Next · Stop | no |\r\n| `minimal` | Previous · Next | no |\r\n| `select` | Stop only | yes |\r\n\r\n```javascript\r\nawait paginate(interaction, {\r\n  embeds,\r\n  authorId: interaction.user.id,\r\n  preset: 'compact',\r\n  jumpModal: true,\r\n});\r\n```\r\n\r\nYou can still override `buttons`, `buttonOrder`, and `useSelectMenu` after picking a preset.\r\n\r\n---\r\n\r\n## Features\r\n\r\n- **EmbedBuilder or plain objects** — mix both; hex colors like `'#5865F2'` are allowed\r\n- **Lazy pages** — `fetchPage` + `totalPages` for huge datasets\r\n- **`embedsPerPage`** — show up to 10 embeds at once\r\n- **Progress bar**, **link buttons**, **home / random / history**\r\n- **`allowedRoles`**, **`notifyPageChange`**, **`clone()`**, **`shufflePages()`**\r\n- **EN / ES / PT / FR / DE / IT locales** plus `defineLocale()` for any other language\r\n- **Themes** — `classic`, `arrows`, `round`, `discord`\r\n- **Search** — find a page by title or description\r\n- **Numbered buttons** — jump with `1 2 3 4 5`\r\n- **`paginateList()`** — list → pages → start in one call\r\n- **Tables, images, code** — `createTablePages`, `createImagePages`, `createCodePages`\r\n- **Pause / resume**, confirm-close, `autoTitle`, `transform`, `beforePageChange`\r\n- **User lock** — `authorId`, `allowedUsers`, or a custom `filter`\r\n- **Smart buttons** — auto-disable at the edges; custom order, labels, styles, emojis\r\n- **Jump-to-page modal** — click `1 / 5` and type a number\r\n- **Select menu** — works with more than 25 pages (sliding window)\r\n- **Helpers** — `createPages`, `createTextPages`, `createFieldPages`, `chunk`, `splitText`\r\n- **Events** — `start`, `pageChange`, `collect`, `unauthorized`, `end`, `error`\r\n- **Loop**, ephemeral replies, auto footer, extra action rows\r\n- **Unique custom IDs** — several paginators can live in the same channel\r\n- **Zero runtime dependencies**\r\n\r\n---\r\n\r\n## Examples\r\n\r\n### 1. Paginate a list (`createPages`)\r\n\r\n```javascript\r\nconst { paginate, createPages } = require('@amatiscorp/pagincord');\r\n\r\nconst pages = createPages({\r\n  items: users,\r\n  itemsPerPage: 5,\r\n  mapItem: (user, i) => `**${i + 1}.** ${user.name} — **${user.score}** pts`,\r\n  embed: { title: '🏆 Leaderboard', color: 0xffd700 },\r\n  locale: 'en',\r\n});\r\n\r\nawait paginate(interaction, {\r\n  embeds: pages,\r\n  authorId: interaction.user.id,\r\n  useSelectMenu: true,\r\n});\r\n```\r\n\r\nCustom page body:\r\n\r\n```javascript\r\ncreatePages({\r\n  items: products,\r\n  itemsPerPage: 3,\r\n  mapPage: (items) => items.map((p) => `**${p.name}**\\n${p.price} coins`).join('\\n\\n'),\r\n  embed: { title: 'Shop', color: 0x3498db },\r\n});\r\n```\r\n\r\n### 2. Long text (`createTextPages` / `splitText`)\r\n\r\n```javascript\r\nconst { createTextPages, splitText } = require('@amatiscorp/pagincord');\r\n\r\nconst pages = createTextPages(rulesMarkdown, {\r\n  title: (page, total) => `Rules (${page}/${total})`,\r\n  color: 0xe74c3c,\r\n  maxLength: 3500,\r\n  locale: 'en',\r\n});\r\n```\r\n\r\n`splitText(text, 4096)` only splits a string. Discord embed descriptions max out at 4096 characters.\r\n\r\n### 3. Embed fields (`createFieldPages`)\r\n\r\n```javascript\r\nconst { createFieldPages, paginate } = require('@amatiscorp/pagincord');\r\n\r\nconst pages = createFieldPages(\r\n  [\r\n    { name: 'Sword', value: '120 coins', inline: true },\r\n    { name: 'Shield', value: '90 coins', inline: true },\r\n    { name: 'Potion', value: '25 coins', inline: true },\r\n  ],\r\n  { fieldsPerPage: 6, embed: { title: 'Shop', color: 0x3498db } }\r\n);\r\n\r\nawait paginate(interaction, { embeds: pages, authorId: interaction.user.id });\r\n```\r\n\r\n### 4. Labels, order, and emojis\r\n\r\n```javascript\r\nawait paginate(interaction, {\r\n  embeds,\r\n  locale: 'en',\r\n  showButtonLabels: true,\r\n  hideEmojis: false,\r\n  buttonOrder: ['previous', 'pageIndicator', 'next', 'stop'],\r\n  buttons: { first: false, last: false, pageIndicator: true, stop: true },\r\n  buttonLabels: { stop: 'Dismiss' },\r\n  buttonEmojis: { previous: '⬅️', next: '➡️', stop: '❌' },\r\n  jumpModal: true,\r\n});\r\n```\r\n\r\n### 5. Who can click\r\n\r\n```javascript\r\n// anyone\r\nawait paginate(interaction, { embeds });\r\n\r\n// author + extra users\r\nawait paginate(interaction, {\r\n  embeds,\r\n  authorId: interaction.user.id,\r\n  allowedUsers: ['123456789012345678'],\r\n});\r\n\r\n// role filter\r\nawait paginate(interaction, {\r\n  embeds,\r\n  filter: (i) => i.member?.roles.cache.has(modRoleId),\r\n  unauthorizedMessage: (user) => `${user}, this menu is for moderators.`,\r\n});\r\n```\r\n\r\n### 6. Footer, content, ephemeral, end behavior\r\n\r\n```javascript\r\nawait paginate(interaction, {\r\n  embeds,\r\n  authorId: interaction.user.id,\r\n  locale: 'en',\r\n  ephemeral: true,\r\n  autoFooter: { format: 'Page {page} / {total}', append: true },\r\n  content: ({ page, total }) => `Viewing **${page + 1}** of **${total}**`,\r\n  endBehavior: 'clear', // 'disable' | 'delete' | 'clear'\r\n  timeout: 120_000,     // idle; 0 = never\r\n  maxDuration: 600_000, // hard cap, even if people keep clicking\r\n});\r\n```\r\n\r\n`page` in callbacks is **0-based**. Show it as `page + 1`.\r\n\r\n### 7. Events and live updates\r\n\r\n```javascript\r\nconst paginator = new Paginator({\r\n  embeds,\r\n  authorId: interaction.user.id,\r\n  onStart: (message) => console.log('sent', message.id),\r\n  onPageChange: ({ page, total }) => console.log(`${page + 1}/${total}`),\r\n  onEnd: (reason) => console.log(reason),\r\n});\r\n\r\npaginator.on('collect', (i) => console.log(i.user.tag));\r\npaginator.on('unauthorized', (i) => console.log('blocked', i.user.id));\r\npaginator.on('error', (err) => console.error(err));\r\n\r\nawait paginator.start(interaction);\r\n\r\nawait paginator.goToPage(2);\r\nawait paginator.next();\r\nawait paginator.previous();\r\nawait paginator.first();\r\nawait paginator.last();\r\nawait paginator.addEmbeds([{ title: 'Extra', description: 'Appended' }]);\r\nawait paginator.insertEmbeds(0, [{ title: 'Intro', description: 'New first page' }]);\r\nawait paginator.removePage(3);\r\npaginator.setAllowedUsers([interaction.user.id]);\r\npaginator.setLocale('es');\r\nawait paginator.refresh();\r\nawait paginator.stop();\r\n```\r\n\r\n### 8. Custom select options + extra rows\r\n\r\n```javascript\r\nconst { ActionRowBuilder, ButtonBuilder, ButtonStyle } = require('discord.js');\r\n\r\nawait paginate(interaction, {\r\n  embeds,\r\n  useSelectMenu: true,\r\n  selectOption: (embed, index) => ({\r\n    label: `Go to ${embed.data.title}`,\r\n    description: `Page ${index + 1}`,\r\n    emoji: '📄',\r\n  }),\r\n  extraRows: ({ page }) => [\r\n    new ActionRowBuilder().addComponents(\r\n      new ButtonBuilder()\r\n        .setCustomId(`claim-${page}`)\r\n        .setLabel('Claim this page')\r\n        .setStyle(ButtonStyle.Success)\r\n    ),\r\n  ],\r\n});\r\n```\r\n\r\nDiscord allows **5 rows** total. Pagincord fills buttons + select first, then appends `extraRows`.\r\n\r\n### 9. Reply mode\r\n\r\n```javascript\r\nawait interaction.deferReply();\r\nawait paginate(interaction, { embeds }); // editReply\r\n\r\nawait paginate(buttonInteraction, { embeds, replyAs: 'update' });\r\nawait paginate(interaction, { embeds, replyAs: 'followUp' });\r\n```\r\n\r\n### 10. Deferred command with slow I/O\r\n\r\n```javascript\r\nawait interaction.deferReply();\r\nconst tracks = await fetchQueue();\r\n\r\nconst pages = createPages({\r\n  items: tracks,\r\n  itemsPerPage: 8,\r\n  mapItem: (t, i) => `\\`${i + 1}.\\` ${t.title} — ${t.duration}`,\r\n  embed: { title: 'Music queue', color: 0x1db954 },\r\n});\r\n\r\nawait paginate(interaction, { embeds: pages, authorId: interaction.user.id, locale: 'en' });\r\n```\r\n\r\n### 11. One-shot lists (`paginateList`)\r\n\r\n```javascript\r\nconst { paginateList } = require('@amatiscorp/pagincord');\r\n\r\nawait paginateList(interaction, users, {\r\n  itemsPerPage: 5,\r\n  mapItem: (user, i) => `**${i + 1}.** ${user.name} — ${user.score}`,\r\n  embed: { title: '🏆 Leaderboard', color: 0xffd700 },\r\n  authorId: interaction.user.id,\r\n  locale: 'en',\r\n  searchable: true,\r\n  numberedButtons: true,\r\n});\r\n```\r\n\r\n### 12. Themes, search, numbered buttons, auto title\r\n\r\n```javascript\r\nawait paginate(interaction, {\r\n  embeds,\r\n  authorId: interaction.user.id,\r\n  theme: 'arrows',          // ⏪ ⬅️ ➡️ ⏩ ❌\r\n  searchable: true,         // 🔍 opens a search modal\r\n  numberedButtons: true,    // row of 1–5\r\n  autoTitle: true,          // \"Welcome (1/3)\"\r\n  confirmStop: true,        // Close must be clicked twice\r\n  autoDefer: true,\r\n});\r\n```\r\n\r\nOther themes: `classic`, `arrows`, `round`, `discord`. Import `themes` if you want to copy an emoji pack.\r\n\r\n### 13. Tables, images, code\r\n\r\n```javascript\r\nconst {\r\n  createTablePages,\r\n  createImagePages,\r\n  createCodePages,\r\n  formatList,\r\n} = require('@amatiscorp/pagincord');\r\n\r\ncreateTablePages(\r\n  [\r\n    ['Alex', 1200],\r\n    ['Sam', 980],\r\n  ],\r\n  { headers: ['Name', 'Score'], rowsPerPage: 10, embed: { title: 'Ranking' } }\r\n);\r\n\r\ncreateImagePages(['https://example.com/1.png', 'https://example.com/2.png'], {\r\n  color: 0x5865f2,\r\n});\r\n\r\ncreateCodePages(source, { language: 'js', title: 'index.js' });\r\n\r\nformatList(['alpha', 'beta'], { style: 'bulleted' });\r\n// • alpha\r\n// • beta\r\n```\r\n\r\n### 14. Transform, cancel navigation, pause\r\n\r\n```javascript\r\nconst paginator = new Paginator({\r\n  embeds,\r\n  authorId: interaction.user.id,\r\n  transform: (embed, { page, total }) =>\r\n    embed.setFooter({ text: `Custom ${page + 1}/${total}` }),\r\n  beforePageChange: (from, to) => to !== 2, // skip page 3\r\n});\r\n\r\nawait paginator.start(interaction);\r\nawait paginator.pause();\r\nawait paginator.resume();\r\n\r\n// Edit an existing bot message instead of sending a new one:\r\nawait paginator.attach(existingMessage);\r\n```\r\n\r\n### 15. Lazy pages, progress bar, extra buttons, roles\r\n\r\n```javascript\r\nawait paginate(interaction, {\r\n  fetchPage: async (index) => {\r\n    const item = await db.getPage(index);\r\n    return { title: item.name, description: item.body, color: 0x5865f2 };\r\n  },\r\n  totalPages: 200,\r\n  authorId: interaction.user.id,\r\n  progressBar: true,\r\n  buttons: { home: true, random: true, back: true },\r\n  allowedRoles: [modRoleId],\r\n  notifyPageChange: true,\r\n  linkButtons: [{ label: 'Docs', url: 'https://github.com/amatiscorp/pagincord' }],\r\n  embedsPerPage: 1,\r\n});\r\n```\r\n\r\n```javascript\r\nconst { createLeaderboardPages, createMentionPages, createProgressBar } = require('@amatiscorp/pagincord');\r\n\r\ncreateLeaderboardPages(users, {\r\n  mapItem: (u) => `${u.name} — ${u.score}`,\r\n  itemsPerPage: 10,\r\n});\r\n\r\ncreateMentionPages(userIds, { kind: 'user', itemsPerPage: 15 });\r\n\r\ncreateProgressBar(3, 10); // ██████░░░░\r\n```\r\n\r\n---\r\n\r\n## API reference\r\n\r\n### `configure(defaults)` / `setLocale(code)`\r\n\r\n| Key | Type | Default |\r\n|-----|------|---------|\r\n| `locale` | `'en' \\| 'es' \\| 'pt' \\| 'fr' \\| 'de' \\| string` | `'en'` |\r\n| `timeout` | `number` | `60000` |\r\n| `loop` | `boolean` | `false` |\r\n| `useSelectMenu` | `boolean` | `false` |\r\n| `endBehavior` | `'disable' \\| 'delete' \\| 'clear'` | `'disable'` |\r\n| `autoFooter` | `boolean` | `false` |\r\n| `showButtonLabels` | `boolean` | `false` |\r\n| `hideEmojis` | `boolean` | `false` |\r\n| `preset` | `'full' \\| 'compact' \\| 'minimal' \\| 'select'` | — |\r\n| `theme` | `'classic' \\| 'arrows' \\| 'round' \\| 'discord'` | `'classic'` |\r\n| `autoTitle` | `boolean \\| string` | `false` |\r\n| `numberedButtons` | `boolean \\| number` | `false` |\r\n| `searchable` | `boolean` | `false` |\r\n| `confirmStop` | `boolean` | `false` |\r\n| `silentUnauthorized` | `boolean` | `false` |\r\n| `autoDefer` | `boolean` | `false` |\r\n| `embedsPerPage` | `number` | `1` |\r\n| `progressBar` | `boolean` | `false` |\r\n| `ephemeral` | `boolean` | `false` |\r\n| `buttons` / `buttonEmojis` | objects | built-in |\r\n\r\n`getConfig()`, `resetConfig()`, `getLocale()` read the current defaults.\r\n\r\n### `new Paginator(options)`\r\n\r\n| Option | Type | Default | Description |\r\n|--------|------|---------|-------------|\r\n| `embeds` | `(EmbedBuilder \\| EmbedData)[]` | **required** | Pages |\r\n| `locale` | `'en' \\| 'es' \\| 'pt' \\| 'fr' \\| 'de' \\| string` | config | UI language |\r\n| `texts` | `object` | — | Per-instance string overrides |\r\n| `preset` | `'full' \\| 'compact' \\| 'minimal' \\| 'select'` | `full` | Button layout shortcut |\r\n| `theme` | `'classic' \\| 'arrows' \\| 'round' \\| 'discord'` | `classic` | Emoji pack |\r\n| `autoTitle` | `boolean \\| string` | `false` | Append `(1/5)` to the title |\r\n| `transform` | `(embed, ctx) => …` | — | Mutate embed before send |\r\n| `beforePageChange` | `(from, to) => boolean` | — | Return `false` to cancel |\r\n| `numberedButtons` | `boolean \\| number` | `false` | Number row (max 5) |\r\n| `searchable` | `boolean` | `false` | Search modal + button |\r\n| `confirmStop` | `boolean` | `false` | Double-click Close |\r\n| `silentUnauthorized` | `boolean` | `false` | Block without a reply |\r\n| `editMessage` | `boolean` | `false` | Edit the target `Message` |\r\n| `autoDefer` | `boolean` | `false` | `deferReply` if needed |\r\n| `fetchPage` | `(index, total) => embed(s)` | — | Load pages on demand |\r\n| `totalPages` | `number` | — | Required with `fetchPage` if no embeds |\r\n| `embedsPerPage` | `number` | `1` | 1–10 embeds visible at once |\r\n| `progressBar` | `boolean \\| { size, format }` | `false` | Footer bar `{bar}` `{percent}` |\r\n| `linkButtons` | `{ label, url, emoji? }[]` | — | URL buttons |\r\n| `allowedRoles` | `string[]` | `[]` | Role IDs that may click |\r\n| `notifyPageChange` | `boolean \\| fn` | `false` | Ephemeral “now on page X” |\r\n| `homePage` | `number` | `0` | Target of the Home button |\r\n| `buttons.home` / `.random` / `.back` | `boolean` | `false` | Extra nav buttons |\r\n| `authorId` | `string` | — | Only this user can click |\r\n| `allowedUsers` | `string[]` | `[]` | Extra allowed IDs |\r\n| `filter` | `(i) => boolean` | — | Return `false` to reject |\r\n| `useSelectMenu` | `boolean` | `false` | Page dropdown |\r\n| `selectPlaceholder` | `string \\| fn` | locale | Select placeholder |\r\n| `selectOption` | `(embed, index, ctx) => …` | locale | Custom option label / emoji |\r\n| `timeout` | `number` | `60000` | Idle ms (`0` = never) |\r\n| `maxDuration` | `number` | — | Absolute collector lifetime |\r\n| `buttonEmojis` | `object` | `⏮️ ◀️ ▶️ ⏭️ 🗑️` | Custom emojis |\r\n| `buttonLabels` | `object` | — | Custom labels |\r\n| `buttonStyles` | `object` | Primary / Danger | discord.js styles |\r\n| `buttons` | `object` | all nav + stop | Toggle each button |\r\n| `buttonOrder` | `ButtonKey[]` | default order | Render order |\r\n| `showButtonLabels` | `boolean` | `false` | Use locale labels |\r\n| `hideEmojis` | `boolean` | `false` | Labels only |\r\n| `jumpModal` | `boolean \\| object` | `false` | Type a page number |\r\n| `deleteOnStop` | `boolean` | `false` | Alias of `endBehavior: 'delete'` |\r\n| `endBehavior` | `'disable' \\| 'delete' \\| 'clear'` | `'disable'` | After stop / timeout |\r\n| `startPage` | `number` | `0` | 0-based |\r\n| `loop` | `boolean` | `false` | Wrap around |\r\n| `ephemeral` | `boolean` | `false` | Interaction-only |\r\n| `content` | `string \\| fn` | — | Text above the embed |\r\n| `autoFooter` | `boolean \\| { format, append }` | `false` | Page footer |\r\n| `unauthorizedMessage` | `string \\| fn` | locale | Reject message |\r\n| `hideButtonsIfSinglePage` | `boolean` | `false` | Hide controls if 1 page |\r\n| `replyAs` | `'reply' \\| 'editReply' \\| 'followUp' \\| 'update'` | auto | How to send |\r\n| `indicatorFormat` | `string` | locale | `1 / 5` button text |\r\n| `customIdPrefix` | `string` | `'pgc'` | Component ID prefix |\r\n| `extraRows` | `rows \\| (ctx) => rows` | — | Extra action rows |\r\n| `allowedMentions` | `object` | — | Passed to Discord |\r\n| `onPageChange` / `onEnd` / `onCollect` / `onStart` / `onUnauthorized` | fn | — | Callbacks |\r\n\r\n`jumpModal`: `{ title?, label?, placeholder?, invalid? }`.\r\n\r\n### Methods\r\n\r\n```ts\r\nawait paginator.start(target)\r\nawait paginator.attach(message)    // edit an existing message\r\nawait paginator.stop()\r\nawait paginator.pause()\r\nawait paginator.resume()\r\nawait paginator.goToPage(index)    // 0-based\r\nawait paginator.next()\r\nawait paginator.previous()\r\nawait paginator.first()\r\nawait paginator.last()\r\nawait paginator.home()\r\nawait paginator.random()\r\nawait paginator.back()\r\nawait paginator.shufflePages()\r\npaginator.clone()\r\nawait paginator.setEmbeds(embeds)\r\nawait paginator.addEmbeds(embeds)\r\nawait paginator.insertEmbeds(index, embeds)\r\nawait paginator.removePage(index)\r\nawait paginator.refresh()\r\n\r\npaginator.setAllowedUsers(ids)\r\npaginator.setLocale('es')\r\npaginator.getLocale()\r\npaginator.getEmbeds()\r\npaginator.getTotalPages()\r\npaginator.getCurrentPage()         // 0-based\r\npaginator.getMessage()\r\npaginator.isActive()\r\npaginator.isPaused()\r\npaginator.getState()               // includes locale, active, paused\r\n```\r\n\r\n### Helpers\r\n\r\n| Function | Purpose |\r\n|----------|---------|\r\n| `paginate(target, options)` | `new Paginator` + `start` |\r\n| `paginateList(target, items, opts)` | List → embeds → start |\r\n| `createPages({ items, … })` | Split a list into embeds |\r\n| `createTextPages(text, opts)` | Split a long string into embeds |\r\n| `createFieldPages(fields, opts)` | Split embed fields (max 25 / page) |\r\n| `createTablePages(rows, opts)` | Markdown/code table pages |\r\n| `createImagePages(urls, opts)` | One image per page |\r\n| `createLeaderboardPages(items, opts)` | Ranked list with 🥇🥈🥉 |\r\n| `createMentionPages(ids, opts)` | `<@user>` / `<@&role>` / `<#channel>` |\r\n| `createProgressBar(value, max, size)` | `██████░░░░` |\r\n| `isPagincordCustomId(id)` | Detect Pagincord component IDs |\r\n| `VERSION` | `'2.2.0'` |\r\n| `formatList(items, opts)` | Numbered / bulleted / dashed |\r\n| `chunk(array, size)` | Raw `T[][]` |\r\n| `splitText(text, maxLength)` | Split a string on paragraphs / spaces |\r\n| `toEmbedBuilders(embeds)` | Normalize mixed pages to `EmbedBuilder[]` |\r\n| `interpolate(template, vars)` | Replace `{page}` `{total}` `{title}` |\r\n| `defineLocale(code, strings)` | Register a language |\r\n| `listLocales()` | Registered locale codes |\r\n| `locales` / `themes` | Built-in dictionaries and emoji packs |\r\n\r\n### Events\r\n\r\n```ts\r\npaginator.on('start', (message) => {});\r\npaginator.on('pageChange', ({ page, total, embed, interaction }) => {});\r\npaginator.on('end', (reason) => {});          // timeout | stop | manual | idle | messageDelete\r\npaginator.on('collect', (interaction) => {});\r\npaginator.on('unauthorized', (interaction) => {});\r\npaginator.on('pause', () => {});\r\npaginator.on('resume', () => {});\r\npaginator.on('error', (error) => {});\r\n```\r\n\r\nListen to `error` if you want failed Discord API calls (deleted message, etc.) instead of silent ignore.\r\n\r\n### `EmbedData`\r\n\r\n```ts\r\ninterface EmbedData {\r\n  title?: string;\r\n  description?: string;\r\n  color?: number | string; // 0x5865f2 or '#5865F2'\r\n  fields?: Array<{ name: string; value: string; inline?: boolean }>;\r\n  footer?: { text: string; iconURL?: string };\r\n  thumbnail?: string;\r\n  image?: string;\r\n  author?: { name: string; iconURL?: string; url?: string };\r\n  timestamp?: boolean | Date;\r\n  url?: string;\r\n}\r\n```\r\n\r\n---\r\n\r\n## Button layout\r\n\r\nDefault (`full`):\r\n\r\n`⏮️` First · `◀️` Previous · `🗑️` Stop · `▶️` Next · `⏭️` Last\r\n\r\n- First / Previous disable on page 1 (unless `loop`)\r\n- Next / Last disable on the last page (unless `loop`)\r\n- Enable `pageIndicator` or `jumpModal` to insert `1 / 5`\r\n- Discord allows 5 buttons per row; extras wrap\r\n- Custom IDs look like `pgc:<id>:next` so two paginators never clash\r\n\r\n---\r\n\r\n## Notes\r\n\r\n- Idle `timeout` resets on every authorized click. `maxDuration` is a hard stop.\r\n- Select menus are limited to 25 options. Pagincord windows around the current page.\r\n- Call `start()` **once** per instance. Create a new `Paginator` for each command.\r\n- `ephemeral` only applies to interactions, not `message.channel.send`.\r\n- Incoming `EmbedBuilder`s are cloned. Pagincord does not mutate yours.\r\n\r\n---\r\n\r\n## License\r\n\r\nMIT © [Amatis Corp](https://github.com/amatiscorp)\r\n\r\n---\r\n---\r\n\r\n# Español\r\n\r\nPagincord convierte un array de embeds en un mensaje interactivo. El paquete en npm es **`@amatiscorp/pagincord`**.\r\n\r\n**Requiere:** Node.js 16.11+ y `discord.js` v14+.\r\n\r\n## Instalación\r\n\r\n```bash\r\nnpm install @amatiscorp/pagincord discord.js\r\n```\r\n\r\n```javascript\r\nconst { Paginator, paginate, configure } = require('@amatiscorp/pagincord');\r\n```\r\n\r\n## Idioma del bot\r\n\r\nTodos los textos internos vienen en **inglés, español, portugués, francés, alemán e italiano**: error de permisos, footer, placeholder del select, modal “ir a página”, búsqueda, lista vacía y etiquetas de botones.\r\n\r\n```javascript\r\nconfigure({ locale: 'es', showButtonLabels: true, theme: 'arrows' });\r\n```\r\n\r\n```javascript\r\n// Una vez al arrancar el bot\r\nconfigure({\r\n  locale: 'es',\r\n  showButtonLabels: true, // Inicio, Atrás, Siguiente, Final, Cerrar\r\n  timeout: 120_000,\r\n});\r\n\r\n// O solo en un comando\r\nawait paginate(interaction, {\r\n  embeds: paginas,\r\n  authorId: interaction.user.id,\r\n  locale: 'es',\r\n  showButtonLabels: true,\r\n  jumpModal: true,\r\n});\r\n```\r\n\r\nTextos en inglés por defecto (`locale: 'en'`).\r\n\r\nSobrescribe una frase:\r\n\r\n```javascript\r\nawait paginate(interaction, {\r\n  embeds: paginas,\r\n  locale: 'es',\r\n  texts: {\r\n    unauthorized: 'Solo el autor del comando puede usar estos botones.',\r\n    jumpModalTitle: 'Saltar a página',\r\n  },\r\n});\r\n```\r\n\r\n## Inicio rápido\r\n\r\n```javascript\r\nconst { Paginator } = require('@amatiscorp/pagincord');\r\nconst { EmbedBuilder } = require('discord.js');\r\n\r\nclient.on('interactionCreate', async (interaction) => {\r\n  if (!interaction.isChatInputCommand()) return;\r\n  if (interaction.commandName !== 'ayuda') return;\r\n\r\n  const paginator = new Paginator({\r\n    embeds: [\r\n      new EmbedBuilder().setTitle('Página 1').setDescription('Bienvenida').setColor(0x5865f2),\r\n      new EmbedBuilder().setTitle('Página 2').setDescription('Comandos').setColor(0x57f287),\r\n      new EmbedBuilder().setTitle('Página 3').setDescription('Soporte').setColor(0xfee75c),\r\n    ],\r\n    authorId: interaction.user.id,\r\n    locale: 'es',\r\n    showButtonLabels: true,\r\n  });\r\n\r\n  await paginator.start(interaction);\r\n});\r\n```\r\n\r\nAtajo:\r\n\r\n```javascript\r\nconst { paginate } = require('@amatiscorp/pagincord');\r\n\r\nawait paginate(interaction, {\r\n  embeds: paginas,\r\n  authorId: interaction.user.id,\r\n  locale: 'es',\r\n});\r\n```\r\n\r\n## Paginar una lista\r\n\r\n```javascript\r\nconst { paginate, createPages } = require('@amatiscorp/pagincord');\r\n\r\nconst paginas = createPages({\r\n  items: usuarios,\r\n  itemsPerPage: 5,\r\n  mapItem: (user, i) => `**${i + 1}.** ${user.name} — **${user.score}** pts`,\r\n  embed: { title: '🏆 Ranking', color: 0xffd700 },\r\n  locale: 'es', // footer \"Página X de Y\"\r\n});\r\n\r\nawait paginate(interaction, {\r\n  embeds: paginas,\r\n  authorId: interaction.user.id,\r\n  locale: 'es',\r\n  useSelectMenu: true,\r\n});\r\n```\r\n\r\nAtajo de lista:\r\n\r\n```javascript\r\nconst { paginateList } = require('@amatiscorp/pagincord');\r\n\r\nawait paginateList(interaction, usuarios, {\r\n  itemsPerPage: 5,\r\n  mapItem: (u, i) => `**${i + 1}.** ${u.name}`,\r\n  embed: { title: 'Ranking' },\r\n  authorId: interaction.user.id,\r\n  locale: 'es',\r\n  searchable: true,\r\n  numberedButtons: true,\r\n  theme: 'arrows',\r\n});\r\n```\r\n\r\nTablas, imágenes y código:\r\n\r\n```javascript\r\nconst { createTablePages, createImagePages, createCodePages } = require('@amatiscorp/pagincord');\r\n\r\ncreateTablePages(filas, { headers: ['Nombre', 'Puntos'], embed: { title: 'Ranking' } });\r\ncreateImagePages(urls, { color: 0x5865f2 });\r\ncreateCodePages(fuente, { language: 'js', title: 'bot.js' });\r\n```\r\n\r\nPáginas bajo demanda, barra de progreso y botones extra:\r\n\r\n```javascript\r\nawait paginate(interaction, {\r\n  fetchPage: async (i) => db.getPage(i),\r\n  totalPages: 200,\r\n  locale: 'es',\r\n  progressBar: true,\r\n  buttons: { home: true, random: true, back: true },\r\n  allowedRoles: [idRolMod],\r\n  linkButtons: [{ label: 'Web', url: 'https://example.com' }],\r\n});\r\n```\r\n\r\nTexto largo (reglas, changelog):\r\n\r\n```javascript\r\nconst { createTextPages } = require('@amatiscorp/pagincord');\r\n\r\nconst paginas = createTextPages(reglamento, {\r\n  title: (page, total) => `Reglas (${page}/${total})`,\r\n  color: 0xe74c3c,\r\n  locale: 'es',\r\n});\r\n```\r\n\r\nCampos de embed:\r\n\r\n```javascript\r\nconst { createFieldPages } = require('@amatiscorp/pagincord');\r\n\r\nconst paginas = createFieldPages(campos, {\r\n  fieldsPerPage: 6,\r\n  embed: { title: 'Tienda' },\r\n  locale: 'es',\r\n});\r\n```\r\n\r\n## Presets\r\n\r\n```javascript\r\nawait paginate(interaction, {\r\n  embeds: paginas,\r\n  locale: 'es',\r\n  preset: 'compact', // Atrás · 1/5 · Siguiente · Cerrar\r\n  jumpModal: true,   // pulsar 1/5 abre el modal\r\n});\r\n```\r\n\r\n- `full` — todos los botones\r\n- `compact` — atrás / indicador / siguiente / cerrar\r\n- `minimal` — solo atrás y siguiente\r\n- `select` — menú desplegable + cerrar\r\n\r\n## Opciones frecuentes\r\n\r\n```javascript\r\nconst paginator = new Paginator({\r\n  embeds: misEmbeds,\r\n  locale: 'es',\r\n  showButtonLabels: true,\r\n  authorId: interaction.user.id,\r\n  allowedUsers: ['id1', 'id2'],\r\n  useSelectMenu: true,\r\n  jumpModal: true,\r\n  timeout: 120_000,          // inactividad (0 = nunca)\r\n  maxDuration: 10 * 60_000,  // tope absoluto\r\n  loop: true,\r\n  ephemeral: true,\r\n  autoFooter: true,          // \"Página X de Y\"\r\n  endBehavior: 'disable',    // 'disable' | 'delete' | 'clear'\r\n  preset: 'compact',\r\n  theme: 'arrows',\r\n  searchable: true,\r\n  numberedButtons: true,\r\n  autoTitle: true,\r\n  confirmStop: true,\r\n  buttonOrder: ['previous', 'pageIndicator', 'next', 'stop', 'search'],\r\n});\r\n\r\nawait paginator.start(interaction);\r\n```\r\n\r\n## Eventos y control\r\n\r\n```javascript\r\npaginator.on('pageChange', ({ page, total }) => {\r\n  console.log(`Página ${page + 1}/${total}`);\r\n});\r\n\r\npaginator.on('end', (reason) => {\r\n  console.log('Terminó:', reason);\r\n});\r\n\r\nawait paginator.goToPage(2);\r\nawait paginator.next();\r\nawait paginator.first();\r\nawait paginator.addEmbeds([{ title: 'Extra', description: 'Añadida después' }]);\r\npaginator.setLocale('en');\r\nawait paginator.stop();\r\n```\r\n\r\n`page` es **desde 0**. Para mostrarlo usa `page + 1`.\r\n\r\n## Métodos\r\n\r\n| Método | Qué hace |\r\n|--------|----------|\r\n| `start(target)` / `attach(message)` | Envía o edita un mensaje existente |\r\n| `stop()` | Termina y aplica `endBehavior` |\r\n| `pause()` / `resume()` | Congela o reactiva los botones |\r\n| `home()` / `random()` / `back()` | Inicio, aleatorio, historial |\r\n| `shufflePages()` / `clone()` | Mezcla páginas o duplica el paginador |\r\n| `goToPage(n)` / `first()` / `last()` | Salta de página (desde 0) |\r\n| `next()` / `previous()` | Avanza o retrocede |\r\n| `setEmbeds` / `addEmbeds` / `insertEmbeds` / `removePage` | Cambia páginas en caliente |\r\n| `refresh()` | Vuelve a pintar el mensaje |\r\n| `setAllowedUsers(ids)` | Cambia quién puede pulsar |\r\n| `setLocale('es')` | Cambia el idioma sobre la marcha |\r\n| `getState()` | `{ currentPage, totalPages, locale, active, paused, … }` |\r\n\r\n## Notas\r\n\r\n- El timeout es de **inactividad** y se reinicia con cada clic válido.\r\n- El select de Discord admite 25 opciones; si hay más páginas se muestra una ventana.\r\n- Un `Paginator` = un `start()`. Crea uno nuevo en cada comando.\r\n- `ephemeral` solo vale en interacciones, no en mensajes de texto.\r\n- Publicación npm: **`@amatiscorp/pagincord`** (acceso público, organización Amatis Corp).\r\n\r\n## Licencia\r\n\r\nMIT © Amatis Corp\r\n","readmeFilename":"README.md"}