@@ -25,37 +25,52 @@ interface SlackBlock {
2525 } >
2626}
2727
28+ /**
29+ * Split text into section blocks, respecting Slack's 3000 char per-section limit.
30+ * Splits at line boundaries where possible; hard-truncates individual lines that
31+ * are themselves longer than the limit.
32+ */
33+ function splitIntoSectionBlocks ( text : string ) : SlackBlock [ ] {
34+ const blocks : SlackBlock [ ] = [ ]
35+ const lines = text . split ( '\n' )
36+ let current = ''
37+
38+ for ( let line of lines ) {
39+ // Hard-truncate a single line that exceeds the limit on its own
40+ if ( line . length > MAX_SECTION_TEXT ) {
41+ line = `${ line . slice ( 0 , MAX_SECTION_TEXT - 1 ) } …`
42+ }
43+
44+ const candidate = current ? `${ current } \n${ line } ` : line
45+ if ( candidate . length > MAX_SECTION_TEXT && current ) {
46+ blocks . push ( { type : 'section' , text : { type : 'mrkdwn' , text : current } } )
47+ current = line
48+ } else {
49+ current = candidate
50+ }
51+ }
52+
53+ if ( current ) {
54+ blocks . push ( { type : 'section' , text : { type : 'mrkdwn' , text : current } } )
55+ }
56+
57+ return blocks
58+ }
59+
2860/**
2961 * Build content blocks from either a simple string or an array of sections.
3062 * Splits text that exceeds Slack's 3000 char per-section limit at line boundaries.
3163 */
3264function buildContentBlocks ( content : string | SlackMessageSection [ ] ) : SlackBlock [ ] {
3365 if ( typeof content === 'string' ) {
34- return [ { type : 'section' , text : { type : 'mrkdwn' , text : content } } ]
66+ return splitIntoSectionBlocks ( content )
3567 }
3668
3769 const blocks : SlackBlock [ ] = [ ]
3870 for ( const section of content ) {
3971 const fullText = `*${ section . title } *\n${ section . text } `
40- if ( fullText . length <= MAX_SECTION_TEXT ) {
41- blocks . push ( { type : 'section' , text : { type : 'mrkdwn' , text : fullText } } )
42- continue
43- }
44-
45- // Split at line boundaries into multiple blocks
46- const lines = fullText . split ( '\n' )
47- let current = ''
48- for ( const line of lines ) {
49- const candidate = current ? `${ current } \n${ line } ` : line
50- if ( candidate . length > MAX_SECTION_TEXT && current ) {
51- blocks . push ( { type : 'section' , text : { type : 'mrkdwn' , text : current } } )
52- current = line
53- } else {
54- current = candidate
55- }
56- }
57- if ( current ) {
58- blocks . push ( { type : 'section' , text : { type : 'mrkdwn' , text : current } } )
72+ for ( const block of splitIntoSectionBlocks ( fullText ) ) {
73+ blocks . push ( block )
5974 }
6075 }
6176 return blocks
0 commit comments