{"content":"# aux4/paginate\n\nPage through data one screen at a time and render each page however you like. paginate owns the navigation loop, never the data — you give it a command that returns one page and a command that renders it, so the same pager works for a local file and a paginated HTTP endpoint. Nothing is ever loaded in full, so a file larger than memory pages exactly as cheaply as a small one.\n\n## Installation\n\n``bash\naux4 aux4 pkger install aux4/paginate\n`\n\n## Quick Start\n\n`bash\n# A local file, three rows at a time, drawn as a table\naux4 paginate 'cat orders.json | aux4 json page --offset {offset} --limit {limit}' \\\n --render 'aux4 render table \"id,name,total\"' \\\n --limit 3\n\n# A paginated endpoint that wraps its rows in an envelope\naux4 paginate 'aux4 curl \"https://api.example.com/items?page={page}&limit={limit}\"' \\\n --data '$.data' \\\n --totalPages '$.pagination.totalPages' \\\n --render 'aux4 render table \"id,name\"'\n`\n\n## How it works\n\nThe pager keeps one piece of state: the current page index and the page size. Everything a fetch command might need is derived from those, so offset-style and page-style APIs are both served without a mode flag — your command names whichever it wants.\n\n| Placeholder | Value |\n|---|---|\n| {page} | current page, 1-based |\n| {pageIndex} | current page, 0-based |\n| {offset} | pageIndex * limit |\n| {limit}, {pageSize} | records per page |\n\nPlaceholders use **single braces**. ${page} would not survive: aux4 expands ${...} in parameter values before the command reaches the pager, so a simple ${page} arrives empty. Replacement matches whole tokens only, so a JSON body such as -d '{\"page\":1}' is left alone.\n\n## Keys\n\n| Key | Action |\n|---|---|\n| > . n space → | next page |\n| < , p ← | previous page |\n| [ g Home | first page |\n| ] G End | last page — only when the total is known |\n| q Ctrl-C | quit |\n\nBoundaries are clamped: < on the first page stays put rather than requesting a negative offset, and > will not move past the end.\n\n] works only when --total or --totalPages resolves. The pager never guesses a total, and it hides the ] last hint when there is none — so \"there is no last page\" stays distinguishable from \"the key did nothing\".\n\n## Responses with an envelope\n\nMost APIs wrap their rows:\n\n`json\n{\n \"data\": [],\n \"pagination\": {\n \"page\": 1,\n \"total\": 240,\n \"totalPages\": 24\n }\n}\n`\n\n--data points at the array, using the same path syntax as aux4 json get ($, $.data, $.results.0, $.page[0].items). The renderer only ever receives the extracted array, never the envelope — so aux4 render table gets exactly what it expects without knowing pagination happened.\n\n--total, --totalPages and --hasMore point at the metadata. With metadata the status line reads:\n\n`text\n page 3 of 24 · 240 items < prev > next [ first ] last q quit\n`\n\nand without it, simply page 3.\n\n--hasMore is worth setting even though it does not enable ]: without it the only way to detect the end is fetching an empty page.\n\nWith no --data, the fetch output is passed to the renderer untouched, so sources that are not JSON still work.\n\n## Options\n\n`bash\naux4 paginate <fetch> [--render <cmd>] [--data <path>] [--limit <n>]\n`\n\n- fetch — command that returns one page (required)\n- --render — command each page is piped through. Default: print the page as-is\n- --data — path to the array in the response. Default: $\n- --limit — records per page. Default: 10\n- --total — path to the total record count, e.g. $.pagination.total\n- --totalPages — path to the total page count, e.g. $.pagination.totalPages\n- --hasMore — path to a boolean saying whether more pages follow\n- --page — page to start on. Default: 1\n\n## Without a terminal\n\nPiped, redirected, or running in CI, paginate prints the starting page once and exits instead of waiting for keys. It never blocks a script.\n\n`bash\naux4 paginate 'cat orders.json | aux4 json page --offset {offset} --limit {limit}' --limit 2 > page1.json\n`\n\n## Renderers\n\nAny command that reads JSON on stdin works — the renderer never knows it is being paginated:\n\n`bash\n--render 'aux4 render table \"id,status,total\"'\n--render 'aux4 render list \"id\" --secondary \"customer.name\"'\n--render 'aux4 render kv'\n--render 'aux4 json select \"id,status\"'\n`\n\nEach renderer takes its own arguments — render table and render list both take the\nfield spec positionally, and render list` requires one.\n\n## License\n\nApache-2.0\n"}