@@ -4,7 +4,7 @@ import type { OutboundDeliveryResult } from "../infra/outbound/deliver.js";
44import { formatGatewaySummary , formatOutboundDeliverySummary } from "../infra/outbound/format.js" ;
55import type { MessageActionRunResult } from "../infra/outbound/message-action-runner.js" ;
66import { formatTargetDisplay } from "../infra/outbound/target-resolver.js" ;
7- import { normalizeOptionalString } from "../shared/string-coerce.js" ;
7+ import { hasNonEmptyString , normalizeOptionalString } from "../shared/string-coerce.js" ;
88import { normalizeStringEntries } from "../shared/string-normalization.js" ;
99import { getTerminalTableWidth , renderTable } from "../terminal/table.js" ;
1010import { isRich , theme } from "../terminal/theme.js" ;
@@ -35,8 +35,11 @@ function extractMessageId(payload: unknown): string | null {
3535
3636type FormatOpts = {
3737 width : number ;
38+ displayLimit ?: number ;
3839} ;
3940
41+ const DEFAULT_MESSAGE_LIST_LIMIT = 25 ;
42+
4043function renderObjectSummary ( payload : unknown , opts : FormatOpts ) : string [ ] {
4144 if ( ! payload || typeof payload !== "object" ) {
4245 return [ String ( payload ) ] ;
@@ -85,8 +88,22 @@ function renderObjectSummary(payload: unknown, opts: FormatOpts): string[] {
8588 ] ;
8689}
8790
88- function renderMessageList ( messages : unknown [ ] , opts : FormatOpts , emptyLabel : string ) : string [ ] {
89- const rows = messages . slice ( 0 , 25 ) . map ( ( m ) => {
91+ type ListRenderResult = {
92+ lines : string [ ] ;
93+ total : number ;
94+ displayed : number ;
95+ limit : number ;
96+ } ;
97+
98+ function renderMessageList (
99+ messages : unknown [ ] ,
100+ opts : FormatOpts ,
101+ emptyLabel : string ,
102+ ) : ListRenderResult {
103+ const limit = opts . displayLimit ?? DEFAULT_MESSAGE_LIST_LIMIT ;
104+ const total = messages . length ;
105+ const displayed = Math . min ( total , limit ) ;
106+ const rows = messages . slice ( 0 , displayed ) . map ( ( m ) => {
90107 const msg = m as Record < string , unknown > ;
91108 const id =
92109 ( typeof msg . id === "string" && msg . id ) ||
@@ -116,24 +133,29 @@ function renderMessageList(messages: unknown[], opts: FormatOpts, emptyLabel: st
116133 } ) ;
117134
118135 if ( rows . length === 0 ) {
119- return [ theme . muted ( emptyLabel ) ] ;
136+ return { lines : [ theme . muted ( emptyLabel ) ] , total , displayed , limit } ;
120137 }
121138
122- return [
123- renderTable ( {
124- width : opts . width ,
125- columns : [
126- { key : "Time" , header : "Time" , minWidth : 14 } ,
127- { key : "Author" , header : "Author" , minWidth : 10 } ,
128- { key : "Text" , header : "Text" , flex : true , minWidth : 24 } ,
129- { key : "Id" , header : "Id" , minWidth : 10 } ,
130- ] ,
131- rows,
132- } ) . trimEnd ( ) ,
133- ] ;
139+ return {
140+ lines : [
141+ renderTable ( {
142+ width : opts . width ,
143+ columns : [
144+ { key : "Time" , header : "Time" , minWidth : 14 } ,
145+ { key : "Author" , header : "Author" , minWidth : 10 } ,
146+ { key : "Text" , header : "Text" , flex : true , minWidth : 24 } ,
147+ { key : "Id" , header : "Id" , minWidth : 10 } ,
148+ ] ,
149+ rows,
150+ } ) . trimEnd ( ) ,
151+ ] ,
152+ total,
153+ displayed,
154+ limit,
155+ } ;
134156}
135157
136- function renderMessagesFromPayload ( payload : unknown , opts : FormatOpts ) : string [ ] | null {
158+ function renderMessagesFromPayload ( payload : unknown , opts : FormatOpts ) : ListRenderResult | null {
137159 if ( ! payload || typeof payload !== "object" ) {
138160 return null ;
139161 }
@@ -144,7 +166,7 @@ function renderMessagesFromPayload(payload: unknown, opts: FormatOpts): string[]
144166 return renderMessageList ( messages , opts , "No messages." ) ;
145167}
146168
147- function renderPinsFromPayload ( payload : unknown , opts : FormatOpts ) : string [ ] | null {
169+ function renderPinsFromPayload ( payload : unknown , opts : FormatOpts ) : ListRenderResult | null {
148170 if ( ! payload || typeof payload !== "object" ) {
149171 return null ;
150172 }
@@ -155,6 +177,28 @@ function renderPinsFromPayload(payload: unknown, opts: FormatOpts): string[] | n
155177 return renderMessageList ( pins , opts , "No pins." ) ;
156178}
157179
180+ function renderPaginationHint ( payload : unknown , rendered : ListRenderResult ) : string | null {
181+ const { displayed, total, limit } = rendered ;
182+ if ( total > displayed ) {
183+ return `Showing ${ displayed } of ${ total } ; raise --limit to see more` ;
184+ }
185+ if ( payload && typeof payload === "object" ) {
186+ const p = payload as Record < string , unknown > ;
187+ const hasCursor =
188+ p . hasMore === true ||
189+ hasNonEmptyString ( p . nextBatch ) ||
190+ hasNonEmptyString ( p [ "@odata.nextLink" ] ) ;
191+ if ( hasCursor ) {
192+ return "More results available beyond this page; use --json for the raw cursor" ;
193+ }
194+ }
195+ // Heuristic: a full page implies older history may exist beyond it
196+ if ( displayed > 0 && displayed === limit ) {
197+ return `Reached --limit (${ limit } ); raise it to fetch older history if any` ;
198+ }
199+ return null ;
200+ }
201+
158202function extractDiscordSearchResultsMessages ( results : unknown ) : unknown [ ] | null {
159203 if ( ! results || typeof results !== "object" ) {
160204 return null ;
@@ -237,14 +281,21 @@ function renderReactions(payload: unknown, opts: FormatOpts): string[] | null {
237281 ] ;
238282}
239283
240- export function formatMessageCliText ( result : MessageActionRunResult ) : string [ ] {
284+ export type FormatMessageCliTextOpts = {
285+ displayLimit ?: number ;
286+ } ;
287+
288+ export function formatMessageCliText (
289+ result : MessageActionRunResult ,
290+ textOpts : FormatMessageCliTextOpts = { } ,
291+ ) : string [ ] {
241292 const rich = isRich ( ) ;
242293 const ok = ( text : string ) => ( rich ? theme . success ( text ) : text ) ;
243294 const muted = ( text : string ) => ( rich ? theme . muted ( text ) : text ) ;
244295 const heading = ( text : string ) => ( rich ? theme . heading ( text ) : text ) ;
245296
246297 const width = getTerminalTableWidth ( ) ;
247- const opts : FormatOpts = { width } ;
298+ const opts : FormatOpts = { width, displayLimit : textOpts . displayLimit } ;
248299
249300 if ( result . handledBy === "dry-run" ) {
250301 return [ muted ( `[dry-run] would run ${ result . action } via ${ result . channel } ` ) ] ;
@@ -357,31 +408,35 @@ export function formatMessageCliText(result: MessageActionRunResult): string[] {
357408 return lines ;
358409 }
359410
411+ const pushSection = ( rendered : ListRenderResult , headingText : string ) : string [ ] => {
412+ lines . push ( heading ( headingText ) ) ;
413+ lines . push ( rendered . lines [ 0 ] ?? "" ) ;
414+ const hint = renderPaginationHint ( payload , rendered ) ;
415+ if ( hint ) {
416+ lines . push ( muted ( hint ) ) ;
417+ }
418+ return lines ;
419+ } ;
420+
360421 if ( result . action === "read" ) {
361- const messagesTable = renderMessagesFromPayload ( payload , opts ) ;
362- if ( messagesTable ) {
363- lines . push ( heading ( "Messages" ) ) ;
364- lines . push ( messagesTable [ 0 ] ?? "" ) ;
365- return lines ;
422+ const rendered = renderMessagesFromPayload ( payload , opts ) ;
423+ if ( rendered ) {
424+ return pushSection ( rendered , "Messages" ) ;
366425 }
367426 }
368427
369428 if ( result . action === "list-pins" ) {
370- const pinsTable = renderPinsFromPayload ( payload , opts ) ;
371- if ( pinsTable ) {
372- lines . push ( heading ( "Pinned messages" ) ) ;
373- lines . push ( pinsTable [ 0 ] ?? "" ) ;
374- return lines ;
429+ const rendered = renderPinsFromPayload ( payload , opts ) ;
430+ if ( rendered ) {
431+ return pushSection ( rendered , "Pinned messages" ) ;
375432 }
376433 }
377434
378435 if ( result . action === "search" ) {
379436 const results = ( payload as { results ?: unknown } ) . results ;
380437 const list = extractDiscordSearchResultsMessages ( results ) ;
381438 if ( list ) {
382- lines . push ( heading ( "Search results" ) ) ;
383- lines . push ( renderMessageList ( list , opts , "No results." ) [ 0 ] ?? "" ) ;
384- return lines ;
439+ return pushSection ( renderMessageList ( list , opts , "No results." ) , "Search results" ) ;
385440 }
386441 }
387442
0 commit comments