@@ -553,13 +553,29 @@ export default function convertParsedDSLtoMermaid(parsedDSLOriginal) {
553553 'set' , 'set_multiple' , 'set_matrix' , 'set_matrix_multiple' ,
554554 'add' , 'insert' , 'remove' , 'remove_at' , 'remove_subtree' ,
555555 'add_child' , 'set_child' , 'add_matrix_row' , 'add_matrix_column' ,
556- 'remove_matrix_row' , 'remove_matrix_column' , 'add_matrix_border' , 'insert_matrix_row' , 'insert_matrix_column'
556+ 'remove_matrix_row' , 'remove_matrix_column' , 'add_matrix_border' , 'insert_matrix_row' , 'insert_matrix_column' , 'set_text' , 'set_chained'
557557 ] . includes ( command . type ) ) {
558558 const targetObject = pages . length > 0 ? pages [ pages . length - 1 ] ?. find ( comp => comp . name === command . name ) : null ;
559559 if ( targetObject ) {
560560 const methodName = getMethodNameFromCommand ( command ) ;
561- if ( methodName && ! isMethodSupported ( targetObject . type , methodName ) ) {
562- causeCompileError ( `Method not supported\n\nMethod: ${ methodName } \nComponent type: ${ targetObject . type } \nComponent: ${ command . name } ` , command ) ;
561+ if ( methodName ) {
562+ // For chained commands, validate against text object methods instead
563+ if ( command . type === 'set_chained' ) {
564+ if ( ! isMethodSupported ( 'text' , methodName ) ) {
565+ causeCompileError ( `Method not supported on linked text object\n\nMethod: ${ methodName } \nComponent type: text\nParent component: ${ command . name } ` , command ) ;
566+ }
567+ } else if ( ! isMethodSupported ( targetObject . type , methodName ) ) {
568+ causeCompileError ( `Method not supported\n\nMethod: ${ methodName } \nComponent type: ${ targetObject . type } \nComponent: ${ command . name } ` , command ) ;
569+ } else if ( targetObject . type === 'text' && command . type === 'set_multiple' ) {
570+ // Special check for multi-line methods on single-line text objects
571+ const body = targetObject . body ;
572+ const isMultiLineMethod = methodName && ( methodName . endsWith ( 's' ) || methodName . includes ( 'Multiple' ) ) ;
573+ const hasArrayValue = Array . isArray ( body . value ) ;
574+
575+ if ( isMultiLineMethod && ! hasArrayValue ) {
576+ causeCompileError ( `Cannot use multi-line method on single-line text\n\nMethod: ${ methodName } \nText type: Single-line\nSuggestion: Use ${ methodName . replace ( / s $ / , '' ) } instead, or convert to multi-line text with setValue([...])` , command ) ;
577+ }
578+ }
563579 }
564580 }
565581 }
@@ -740,8 +756,11 @@ export default function convertParsedDSLtoMermaid(parsedDSLOriginal) {
740756 break ;
741757 }
742758 } else {
743- // For non-node components, ensure index is a number
744- if ( typeof indexOrNodeName !== 'number' ) {
759+ // For non-node components, ensure index is a number or undefined (for text objects)
760+ if ( targetObject . type === "text" && indexOrNodeName === undefined ) {
761+ // Allow undefined index for text objects (means replace entire value)
762+ index = undefined ;
763+ } else if ( typeof indexOrNodeName !== 'number' ) {
745764 causeCompileError ( `Invalid index type\n\nExpected number, got: ${ typeof indexOrNodeName } \nIndex: ${ indexOrNodeName } \nProperty: ${ property } \nComponent: ${ name } ` , command ) ;
746765 break ;
747766 }
@@ -753,7 +772,12 @@ export default function convertParsedDSLtoMermaid(parsedDSLOriginal) {
753772 if ( targetObject . type === "text" ) {
754773 if ( property === "value" ) {
755774 // For text components, check if we're setting an array element or the whole value
756- if ( isValidIndex ) {
775+ if ( index === undefined ) {
776+ // No index provided - replace entire value
777+ // If current value is array and new value is string, replace with string
778+ // If current value is string and new value is string, replace with string
779+ body [ property ] = newValue ;
780+ } else if ( isValidIndex ) {
757781 // Setting a specific line in a multi-line text
758782 if ( ! Array . isArray ( body [ property ] ) ) {
759783 // Convert single string to array if needed
@@ -770,7 +794,10 @@ export default function convertParsedDSLtoMermaid(parsedDSLOriginal) {
770794 }
771795 } else {
772796 // For other text properties (fontSize, color, etc.)
773- if ( isValidIndex ) {
797+ if ( index === undefined ) {
798+ // No index provided - replace entire property value
799+ body [ property ] = newValue ;
800+ } else if ( isValidIndex ) {
774801 // Setting array-based property
775802 if ( ! Array . isArray ( body [ property ] ) ) {
776803 // Convert single value to array, preserving existing value
@@ -1812,6 +1839,175 @@ export default function convertParsedDSLtoMermaid(parsedDSLOriginal) {
18121839 if ( ! body2 . nodes . includes ( parent2 ) ) body2 . nodes . push ( parent2 ) ;
18131840 break ;
18141841
1842+ case "set_text" : {
1843+ const name = command . name ;
1844+ const text = command . args . index ; // text value comes from index field
1845+ const position = command . args . value . value ; // position comes from token's value field
1846+ const targetObject = pages [ pages . length - 1 ] . find ( comp => comp . name === name ) ;
1847+
1848+ if ( ! targetObject ) {
1849+ causeCompileError ( `Component not on page\n\nName: ${ name } ` , command ) ;
1850+ break ;
1851+ }
1852+
1853+ // setText is not allowed on text objects themselves
1854+ if ( targetObject . type === "text" ) {
1855+ causeCompileError ( `setText cannot be used on text objects\n\nUse setValue, setFontSize, etc. instead.\nComponent: ${ name } ` , command ) ;
1856+ break ;
1857+ }
1858+
1859+ // Find the placement text component for this position
1860+ const textComponentName = `${ name } _${ position } ` ;
1861+ let textComponent = pages [ pages . length - 1 ] . find ( comp => comp . name === textComponentName ) ;
1862+
1863+ if ( text === null || text === undefined ) {
1864+ // Remove the text object if it exists
1865+ if ( textComponent ) {
1866+ const index = pages [ pages . length - 1 ] . indexOf ( textComponent ) ;
1867+ if ( index > - 1 ) {
1868+ pages [ pages . length - 1 ] . splice ( index , 1 ) ;
1869+ }
1870+ }
1871+ // Also remove from the component definition if it was set as a property
1872+ if ( targetObject . body [ position ] ) {
1873+ delete targetObject . body [ position ] ;
1874+ }
1875+ } else {
1876+ // Set or update the text
1877+ if ( textComponent ) {
1878+ // Update existing text component
1879+ if ( Array . isArray ( text ) ) {
1880+ // Convert array to single line string
1881+ textComponent . body . value = text . join ( ' ' ) ;
1882+ } else {
1883+ textComponent . body . value = text ;
1884+ }
1885+ } else {
1886+ // Check if there's a reference to another text object in the definition
1887+ const referencedTextName = targetObject . body [ position ] ;
1888+ if ( referencedTextName && typeof referencedTextName === 'string' ) {
1889+ // Find if this references an existing text component
1890+ const referencedComponent = findComponentDefinitionByName ( definitions , referencedTextName ) ;
1891+ if ( referencedComponent && referencedComponent . type === 'text' ) {
1892+ // Update the referenced text object directly
1893+ if ( Array . isArray ( text ) ) {
1894+ referencedComponent . body . value = text . join ( ' ' ) ;
1895+ } else {
1896+ referencedComponent . body . value = text ;
1897+ }
1898+ // Find the text component on the current page and update it
1899+ textComponent = pages [ pages . length - 1 ] . find ( comp => comp . name === referencedTextName ) ;
1900+ if ( textComponent ) {
1901+ if ( Array . isArray ( text ) ) {
1902+ textComponent . body . value = text . join ( ' ' ) ;
1903+ } else {
1904+ textComponent . body . value = text ;
1905+ }
1906+ }
1907+ break ;
1908+ }
1909+ }
1910+
1911+ // Create new text component
1912+ const newTextComponent = {
1913+ type : 'text' ,
1914+ name : textComponentName ,
1915+ body : { value : Array . isArray ( text ) ? text . join ( ' ' ) : text } ,
1916+ position : 'previous' ,
1917+ placement : position
1918+ } ;
1919+ pages [ pages . length - 1 ] . push ( newTextComponent ) ;
1920+
1921+ // Also update the main component's definition to reference this text
1922+ targetObject . body [ position ] = textComponentName ;
1923+ }
1924+ }
1925+ break ;
1926+ }
1927+
1928+ case "set_chained" : {
1929+ const name = command . name ;
1930+ const placement = Array . isArray ( command . placement ) ? command . placement [ 0 ] . value : command . placement ;
1931+ const property = command . target ;
1932+ const args = command . args ;
1933+ const targetObject = pages [ pages . length - 1 ] . find ( comp => comp . name === name ) ;
1934+
1935+ if ( ! targetObject ) {
1936+ causeCompileError ( `Component not on page\n\nName: ${ name } ` , command ) ;
1937+ break ;
1938+ }
1939+
1940+ // Find the text component for this placement
1941+ const textComponentName = `${ name } _${ placement } ` ;
1942+ let textComponent = pages [ pages . length - 1 ] . find ( comp => comp . name === textComponentName ) ;
1943+
1944+ // Check if the placement refers to a referenced text object
1945+ if ( ! textComponent && targetObject . body [ placement ] ) {
1946+ const referencedTextName = targetObject . body [ placement ] ;
1947+ if ( typeof referencedTextName === 'string' ) {
1948+ textComponent = pages [ pages . length - 1 ] . find ( comp => comp . name === referencedTextName ) ;
1949+ }
1950+ }
1951+
1952+ if ( ! textComponent ) {
1953+ // Create a new text component if it doesn't exist
1954+ textComponent = {
1955+ type : 'text' ,
1956+ name : textComponentName ,
1957+ body : { value : "" } ,
1958+ position : 'previous' ,
1959+ placement : placement
1960+ } ;
1961+ pages [ pages . length - 1 ] . push ( textComponent ) ;
1962+ targetObject . body [ placement ] = textComponentName ;
1963+ }
1964+
1965+ if ( textComponent . type !== "text" ) {
1966+ causeCompileError ( `Chained method can only be used on text objects\n\nComponent: ${ textComponent . name } is not a text object` , command ) ;
1967+ break ;
1968+ }
1969+
1970+ // Apply the method to the text component using the same logic as regular text commands
1971+ if ( property === "value" ) {
1972+ // Handle setValue for text - check if args has index or is direct value
1973+ if ( typeof args === 'object' && args . index !== undefined ) {
1974+ // Array-style operation with index: setValue(index, value)
1975+ if ( ! Array . isArray ( textComponent . body [ property ] ) ) {
1976+ textComponent . body [ property ] = [ textComponent . body [ property ] || "" ] ;
1977+ }
1978+ // Ensure array is long enough
1979+ while ( textComponent . body [ property ] . length <= args . index ) {
1980+ textComponent . body [ property ] . push ( "" ) ;
1981+ }
1982+ textComponent . body [ property ] [ args . index ] = args . value ;
1983+ } else {
1984+ // Direct value setting: setValue("text") or setValue(["line1", "line2"])
1985+ textComponent . body [ property ] = args ;
1986+ }
1987+ } else {
1988+ // Handle other properties (fontSize, color, etc.)
1989+ if ( typeof args === 'object' && args . index !== undefined ) {
1990+ // Array-style operation with index
1991+ if ( ! Array . isArray ( textComponent . body [ property ] ) ) {
1992+ const existingValue = textComponent . body [ property ] ;
1993+ textComponent . body [ property ] = [ ] ;
1994+ if ( existingValue !== undefined && existingValue !== null ) {
1995+ textComponent . body [ property ] [ 0 ] = existingValue ;
1996+ }
1997+ }
1998+ // Ensure array is long enough
1999+ while ( textComponent . body [ property ] . length <= args . index ) {
2000+ textComponent . body [ property ] . push ( null ) ;
2001+ }
2002+ textComponent . body [ property ] [ args . index ] = args . value ;
2003+ } else {
2004+ // Direct property setting
2005+ textComponent . body [ property ] = args ;
2006+ }
2007+ }
2008+ break ;
2009+ }
2010+
18152011 }
18162012 } ) ;
18172013
@@ -1907,7 +2103,7 @@ function preCheck(parsedDSL) {
19072103 if ( ! [
19082104 "page" , "show" , "hide" , "set" , "set_multiple" , "set_matrix" , "set_matrix_multiple" ,
19092105 "add" , "insert" , "remove" , "remove_subtree" , "remove_at" , "comment" ,
1910- "add_matrix_row" , "add_matrix_column" , "remove_matrix_row" , "remove_matrix_column" , "insert_matrix_row" , "insert_matrix_column" , "add_matrix_border" , "add_child" , "set_child"
2106+ "add_matrix_row" , "add_matrix_column" , "remove_matrix_row" , "remove_matrix_column" , "insert_matrix_row" , "insert_matrix_column" , "add_matrix_border" , "add_child" , "set_child" , "set_text" , "set_chained"
19112107 ] . includes ( cmd . type ) ) {
19122108 throw createPreCheckError (
19132109 `Unknown command\n\nType: ${ cmd . type } ` ,
@@ -1918,7 +2114,7 @@ function preCheck(parsedDSL) {
19182114
19192115
19202116 // Check if the first command is a page (only if there are commands)
1921- if ( parsedDSL . cmds . length > 0 && parsedDSL . cmds [ 0 ] . type !== "page" ) {
2117+ if ( parsedDSL . cmds . length > 0 && parsedDSL . cmds [ 0 ] . type !== "page" && parsedDSL . cmds [ 0 ] . type !== "comment" ) {
19222118 throw createPreCheckError ( "Command before page\n\nPlease start a page using 'page'.\nThen use any other commands." , parsedDSL . cmds [ 0 ] ) ;
19232119 }
19242120}
0 commit comments