@@ -50,98 +50,6 @@ internal fun MarkdownComposer(
5050 markdown : String ,
5151 debug : Boolean = false,
5252 animate : Boolean = true,
53- segmentation : LineSegmentation = LineSegmentation .None ,
54- onCompleted : () -> Unit = {}
55- ) {
56- val commonmarkAstNodeParser: CommonmarkAstNodeParser = remember {
57- CommonmarkAstNodeParser ()
58- }
59-
60- val astRootNode by produceState<AstNode ?>(
61- initialValue = null ,
62- key1 = commonmarkAstNodeParser,
63- key2 = markdown
64- ) {
65- value = commonmarkAstNodeParser.parse(markdown)
66- }
67-
68-
69- val tableBlockNodeComposer: AstBlockNodeComposer = remember {
70- object : AstBlockNodeComposer {
71-
72- override fun predicate (astBlockNodeType : AstBlockNodeType ): Boolean {
73- // Intercept tables
74- val isTable = astBlockNodeType == AstTableRoot
75- // Intercept Text
76- val isText = astBlockNodeType == AstParagraph
77- // println(
78- // "isTable: $isTable, " +
79- // "isText: $isText," +
80- // " astBlockNodeType: $astBlockNodeType"
81- // )
82- return isTable || isText
83- }
84-
85- @Composable
86- override fun RichTextScope.Compose (
87- astNode : AstNode ,
88- visitChildren : @Composable ((AstNode ) -> Unit )
89- ) {
90- if (astNode.type is AstTableRoot ) {
91- CustomTable (tableRoot = astNode)
92- } else if (astNode.type is AstParagraph ) {
93-
94- /* *
95- * Persist startIndex OUTSIDE the node composable so it won't reset to 0 when:
96- * - the markdown becomes valid (e.g. ** closes),
97- * - AST is rebuilt,
98- * - paragraph Text() composable gets recreated.
99- */
100- val startIndexByNodeKey = remember(markdown) {
101- mutableStateMapOf<String , Int >()
102- }
103-
104- val nodeKey = remember(astNode) { astNode.stablePathKey() }
105-
106- val startIndexForNode = startIndexByNodeKey[nodeKey] ? : - 1
107-
108- // println("✅ nodeKey: $nodeKey, startIndex: $startIndexForNode")
109-
110- MarkdownFadeInRichText (
111- modifier = Modifier .border(
112- 2 .dp,
113- if (animate) Color .Cyan else Color .Magenta
114- ),
115- astNode = astNode,
116- segmentation = segmentation,
117- debug = debug,
118- startIndex = startIndexForNode,
119- onStartIndexChange = { newStart ->
120- // monotonic to avoid regressions
121- val old = startIndexByNodeKey[nodeKey] ? : 0
122- startIndexByNodeKey[nodeKey] = maxOf(old, newStart)
123- },
124- onCompleted = {
125- onCompleted()
126- },
127- animate = animate
128- )
129- }
130- }
131- }
132- }
133-
134- astRootNode?.let { astNode ->
135- RichTextScope .BasicMarkdown (astNode, tableBlockNodeComposer)
136- }
137- }
138-
139- @Composable
140- internal fun MarkdownComposer (
141- markdown : String ,
142- debug : Boolean = false,
143- revealStore : RevealStore ? = null,
144- animate : Boolean = true,
14553 messageKey : String? = null,
14654 segmentation : LineSegmentation = LineSegmentation .None ,
14755 onCompleted : () -> Unit = {}
@@ -162,9 +70,7 @@ internal fun MarkdownComposer(
16270 object : AstBlockNodeComposer {
16371
16472 override fun predicate (astBlockNodeType : AstBlockNodeType ): Boolean {
165- // Intercept tables
16673 val isTable = astBlockNodeType == AstTableRoot
167- // Intercept Text
16874 val isText = astBlockNodeType == AstParagraph
16975 return isTable || isText
17076 }
@@ -176,30 +82,29 @@ internal fun MarkdownComposer(
17682 ) {
17783
17884 if (animate) {
85+ val revealStore = LocalRevealStore .current
86+
17987 val localFallbackStarts = remember { mutableStateMapOf<String , Int >() }
18088 val localFallbackCompleted = remember { mutableStateMapOf<String , Boolean >() }
18189
18290 val startIndexByNodeKey =
183- revealStore? .startIndexByNodeKey ? : localFallbackStarts
91+ revealStore.startIndexByNodeKey.ifEmpty { localFallbackStarts }
18492 val completedByNodeKey =
185- revealStore? .completedByNodeKey ? : localFallbackCompleted
93+ revealStore.completedByNodeKey.ifEmpty { localFallbackCompleted }
18694
18795 val rawNodeKey = remember(astNode) { astNode.stablePathKey() }
18896
189- // ✅ CRITICAL: prefix by messageKey so different messages don't collide
190- // If messageKey is null, fall back to rawNodeKey (preview/non-lazy uses)
19197 val nodeKey = if (messageKey != null ) {
19298 " $messageKey |$rawNodeKey "
19399 } else {
194100 rawNodeKey
195101 }
196102
197- val startIndexForNode = startIndexByNodeKey[nodeKey] ? : 0
198-
199- // println("✅ nodeKey: $nodeKey, startIndex: $startIndexForNode")
103+ println (" Composer $messageKey , rawNodeKey: $rawNodeKey " )
200104
105+ val startIndexForNode = startIndexByNodeKey[nodeKey] ? : 0
201106 val alreadyCompleted = completedByNodeKey[nodeKey] == true
202- val shouldAnimate = animate && ! alreadyCompleted
107+ val shouldAnimate = ! alreadyCompleted
203108
204109 if (astNode.type is AstTableRoot ) {
205110 CustomTable (tableRoot = astNode)
@@ -214,7 +119,6 @@ internal fun MarkdownComposer(
214119 debug = debug,
215120 startIndex = startIndexForNode,
216121 onStartIndexChange = { newStart ->
217- // monotonic to avoid regressions
218122 val old = startIndexByNodeKey[nodeKey] ? : 0
219123 startIndexByNodeKey[nodeKey] = maxOf(old, newStart)
220124 },
@@ -226,6 +130,7 @@ internal fun MarkdownComposer(
226130 )
227131 }
228132 } else {
133+ // ✅ untouched non-animated path
229134 if (astNode.type is AstTableRoot ) {
230135 CustomTable (tableRoot = astNode)
231136 } else if (astNode.type is AstParagraph ) {
@@ -241,4 +146,4 @@ internal fun MarkdownComposer(
241146 astRootNode?.let { astNode ->
242147 RichTextScope .BasicMarkdown (astNode, tableBlockNodeComposer)
243148 }
244- }
149+ }
0 commit comments