@@ -72,11 +72,24 @@ export type TextWrap = 'wrap' | 'nowrap'
7272
7373/**
7474 * Text styling options for visual appearance.
75+ *
76+ * @example
77+ * ```typescript
78+ * // Named colors
79+ * Text({ children: 'Red text', color: 'red' })
80+ *
81+ * // Hex colors
82+ * Text({ children: 'Custom', color: '#FF5733' })
83+ *
84+ * // ANSI 256 colors
85+ * Text({ children: 'Orange', color: 'ansi:208' })
86+ * Text({ children: 'Pink', color: '213' }) // Bare number also works
87+ * ```
7588 */
7689export interface TextStyle {
7790 /** Apply bold styling to text */
7891 bold ?: boolean
79- /** Set text color (named colors or hex values ) */
92+ /** Set text color (named colors like 'red', hex like '#FF0000', or ANSI 256 codes like 'ansi:123' or '196' ) */
8093 color ?: string
8194 /** Apply dim/faded styling to text (maps to light weight) */
8295 dimColor ?: boolean
@@ -144,6 +157,95 @@ export interface BorderEdges {
144157 top ?: boolean
145158}
146159
160+ /**
161+ * Custom border characters for completely custom border rendering.
162+ *
163+ * @example
164+ * ```typescript
165+ * Box({
166+ * customBorderChars: {
167+ * topLeft: '╔',
168+ * topRight: '╗',
169+ * bottomLeft: '╚',
170+ * bottomRight: '╝',
171+ * top: '═',
172+ * bottom: '═',
173+ * left: '║',
174+ * right: '║'
175+ * }
176+ * })
177+ * ```
178+ */
179+ export interface CustomBorderChars {
180+ /** Bottom border character */
181+ bottom : string
182+ /** Bottom-left corner character */
183+ bottomLeft : string
184+ /** Bottom-right corner character */
185+ bottomRight : string
186+ /** Left border character */
187+ left : string
188+ /** Right border character */
189+ right : string
190+ /** Top border character */
191+ top : string
192+ /** Top-left corner character */
193+ topLeft : string
194+ /** Top-right corner character */
195+ topRight : string
196+ }
197+
198+ /**
199+ * Border style for Box/View components.
200+ *
201+ * @example
202+ * ```typescript
203+ * Box({ borderStyle: 'single' }) // ┌──┐
204+ * Box({ borderStyle: 'double' }) // ╔══╗
205+ * Box({ borderStyle: 'rounded' }) // ╭──╮
206+ * Box({ borderStyle: 'bold' }) // ┏━━┓
207+ * Box({ borderStyle: 'double-left-right' }) // ╓──╖
208+ * Box({ borderStyle: 'double-top-bottom' }) // ╒══╕
209+ * Box({ borderStyle: 'classic' }) // +--+
210+ * ```
211+ */
212+ export type BorderStyle =
213+ | 'none'
214+ | 'single'
215+ | 'double'
216+ | 'rounded'
217+ | 'bold'
218+ | 'double-left-right'
219+ | 'double-top-bottom'
220+ | 'classic'
221+
222+ /**
223+ * Mixed text content with individual styling per section.
224+ *
225+ * @example
226+ * ```typescript
227+ * {
228+ * text: 'Error:',
229+ * color: 'red',
230+ * weight: 'bold',
231+ * decoration: 'underline',
232+ * italic: false
233+ * }
234+ * ```
235+ */
236+ export interface MixedTextContentSection {
237+ /** Text color (named colors, hex, or ANSI codes) */
238+ color ?: string
239+ /** Text decoration (underline, strikethrough, or none) */
240+ decoration ?: 'underline' | 'strikethrough' | 'none'
241+ /** Apply italic styling */
242+ italic ?: boolean
243+ /** The text content for this section */
244+ text : string
245+ /** Text weight (normal, bold, or light) */
246+ weight ?: TextWeight
247+ }
248+
147249/**
148250 * Box/View layout properties (flexbox).
149251 *
@@ -186,14 +288,16 @@ export interface BoxProps {
186288 alignItems ?: 'flex-start' | 'flex-end' | 'center' | 'stretch'
187289 /** Background color (named colors or hex) */
188290 backgroundColor ?: string
189- /** Border color (named colors or hex ) */
291+ /** Border color (named colors, hex, or ANSI codes like 'ansi:123' or '196' ) */
190292 borderColor ?: string
191293 /** Configure which border edges to render */
192294 borderEdges ?: BorderEdges
193- /** Border style (none, single, double, rounded, bold ) */
194- borderStyle ?: 'single' | 'double' | 'rounded' | 'bold' | 'none'
295+ /** Border style (supports all variants including double-left-right, double-top-bottom, classic ) */
296+ borderStyle ?: BorderStyle
195297 /** Bottom inset for absolute positioning (can be negative) */
196298 bottom ?: number
299+ /** Custom border characters (when using custom border style) */
300+ customBorderChars ?: CustomBorderChars
197301 /** Child elements to render inside this box */
198302 children ?: Element | Element [ ]
199303 /** Gap between columns in flex layout */
@@ -312,6 +416,46 @@ export interface TextProps extends TextStyle {
312416 wrap ?: TextWrap
313417}
314418
419+ /**
420+ * Mixed text properties for rendering text with multiple styles.
421+ *
422+ * @example
423+ * ```typescript
424+ * MixedText({
425+ * contents: [
426+ * { text: 'Error: ', color: 'red', weight: 'bold' },
427+ * { text: 'File not found', color: 'white', italic: true }
428+ * ]
429+ * })
430+ * ```
431+ */
432+ export interface MixedTextProps {
433+ /** Horizontal text alignment */
434+ align ?: TextAlign
435+ /** Array of text sections with individual styling */
436+ contents : MixedTextContentSection [ ]
437+ /** Text wrapping behavior */
438+ wrap ?: TextWrap
439+ }
440+
441+ /**
442+ * Fragment properties for grouping elements without layout impact.
443+ *
444+ * @example
445+ * ```typescript
446+ * Fragment({
447+ * children: [
448+ * Text({ children: 'Line 1' }),
449+ * Text({ children: 'Line 2' })
450+ * ]
451+ * })
452+ * ```
453+ */
454+ export interface FragmentProps {
455+ /** Child elements to group */
456+ children : Element | Element [ ]
457+ }
458+
315459/**
316460 * Element type (iocraft element).
317461 */
@@ -530,6 +674,18 @@ export function Box(props: BoxProps): Element {
530674 if ( props . borderStyle ) {
531675 node . border_style = props . borderStyle
532676 }
677+ if ( props . customBorderChars ) {
678+ node . custom_border_chars = {
679+ top_left : props . customBorderChars . topLeft ,
680+ top_right : props . customBorderChars . topRight ,
681+ bottom_left : props . customBorderChars . bottomLeft ,
682+ bottom_right : props . customBorderChars . bottomRight ,
683+ top : props . customBorderChars . top ,
684+ bottom : props . customBorderChars . bottom ,
685+ left : props . customBorderChars . left ,
686+ right : props . customBorderChars . right ,
687+ }
688+ }
533689
534690 // Background
535691 if ( props . backgroundColor ) {
@@ -539,6 +695,70 @@ export function Box(props: BoxProps): Element {
539695 return node
540696}
541697
698+ /**
699+ * Create a mixed text element with multiple styled sections.
700+ *
701+ * @example
702+ * ```typescript
703+ * MixedText({
704+ * contents: [
705+ * { text: 'Success: ', color: 'green', weight: 'bold' },
706+ * { text: 'Operation completed', color: 'white' }
707+ * ],
708+ * align: 'center'
709+ * })
710+ * ```
711+ */
712+ export function MixedText ( props : MixedTextProps ) : Element {
713+ const node : Element = {
714+ type : 'MixedText' ,
715+ mixed_text_contents : props . contents . map ( ( section ) => ( {
716+ text : section . text ,
717+ color : section . color ,
718+ weight : section . weight ,
719+ decoration : section . decoration ,
720+ italic : section . italic ,
721+ } ) ) ,
722+ }
723+
724+ if ( props . align ) {
725+ node . align = props . align
726+ }
727+ if ( props . wrap ) {
728+ node . wrap = props . wrap
729+ }
730+
731+ return node
732+ }
733+
734+ /**
735+ * Create a fragment element that groups children without layout impact.
736+ *
737+ * Fragments are transparent wrappers that allow returning multiple elements
738+ * without affecting the layout hierarchy.
739+ *
740+ * @example
741+ * ```typescript
742+ * Fragment({
743+ * children: [
744+ * Text({ children: 'Line 1' }),
745+ * Text({ children: 'Line 2' }),
746+ * Text({ children: 'Line 3' })
747+ * ]
748+ * })
749+ * ```
750+ */
751+ export function Fragment ( props : FragmentProps ) : Element {
752+ const children = Array . isArray ( props . children )
753+ ? props . children
754+ : [ props . children ]
755+
756+ return {
757+ type : 'Fragment' ,
758+ children,
759+ }
760+ }
761+
542762/**
543763 * Render an element to a string.
544764 */
0 commit comments