mirror of
https://github.com/slhaf/Partner.git
synced 2026-05-12 16:53:04 +08:00
refactor(context): make block activation/rendering exposure-aware and use rendered projections in aggregation
This commit is contained in:
@@ -51,7 +51,12 @@ class ContextWorkspace {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
val activationScore = block.activate()
|
val exposure = if (primaryDomain in matchedDomains) {
|
||||||
|
ContextBlock.Exposure.PRIMARY
|
||||||
|
} else {
|
||||||
|
ContextBlock.Exposure.SECONDARY
|
||||||
|
}
|
||||||
|
val activationScore = block.activate(exposure)
|
||||||
if (activationScore <= 0.0) {
|
if (activationScore <= 0.0) {
|
||||||
iterator.remove()
|
iterator.remove()
|
||||||
continue
|
continue
|
||||||
@@ -61,7 +66,7 @@ class ContextWorkspace {
|
|||||||
block = block,
|
block = block,
|
||||||
domainWeight = matchedDomains.sumOf { domainWeights.getValue(it) },
|
domainWeight = matchedDomains.sumOf { domainWeights.getValue(it) },
|
||||||
activationScore = activationScore,
|
activationScore = activationScore,
|
||||||
forceFullRender = primaryDomain in matchedDomains
|
renderedBlock = block.render(exposure)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,11 +91,7 @@ class ContextWorkspace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun renderResolvedBlock(resolved: ResolvedContextBlock): BlockContent {
|
private fun renderResolvedBlock(resolved: ResolvedContextBlock): BlockContent {
|
||||||
return if (resolved.forceFullRender) {
|
return resolved.renderedBlock
|
||||||
resolved.block.blockContent
|
|
||||||
} else {
|
|
||||||
resolved.block.render()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -128,7 +129,7 @@ private data class ResolvedContextBlock(
|
|||||||
val block: ContextBlock,
|
val block: ContextBlock,
|
||||||
val domainWeight: Int,
|
val domainWeight: Int,
|
||||||
val activationScore: Double,
|
val activationScore: Double,
|
||||||
val forceFullRender: Boolean
|
val renderedBlock: BlockContent
|
||||||
)
|
)
|
||||||
|
|
||||||
data class ContextBlock @JvmOverloads constructor(
|
data class ContextBlock @JvmOverloads constructor(
|
||||||
@@ -155,6 +156,16 @@ data class ContextBlock @JvmOverloads constructor(
|
|||||||
*/
|
*/
|
||||||
private val activateFactor: Double
|
private val activateFactor: Double
|
||||||
) {
|
) {
|
||||||
|
internal enum class Exposure {
|
||||||
|
PRIMARY,
|
||||||
|
SECONDARY
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum class ProjectionLevel {
|
||||||
|
ABSTRACT,
|
||||||
|
COMPACT,
|
||||||
|
FULL
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 默认活跃分数,降低至0时将在 [ContextWorkspace] 中移除该 block
|
* 默认活跃分数,降低至0时将在 [ContextWorkspace] 中移除该 block
|
||||||
@@ -185,9 +196,23 @@ data class ContextBlock @JvmOverloads constructor(
|
|||||||
return activationScore
|
return activationScore
|
||||||
}
|
}
|
||||||
|
|
||||||
fun activate(): Double {
|
internal fun activate(exposure: Exposure): Double {
|
||||||
refreshByElapsedTime()
|
refreshByElapsedTime()
|
||||||
activationScore = min(100.0, activationScore + activateFactor)
|
val currentLevel = currentProjectionLevel()
|
||||||
|
val increasedScore = when (exposure) {
|
||||||
|
Exposure.PRIMARY -> activationScore + when (currentLevel) {
|
||||||
|
ProjectionLevel.FULL -> activateFactor
|
||||||
|
ProjectionLevel.COMPACT -> activateFactor * 0.6
|
||||||
|
ProjectionLevel.ABSTRACT -> activateFactor * 0.6
|
||||||
|
}
|
||||||
|
|
||||||
|
Exposure.SECONDARY -> activationScore + when (currentLevel) {
|
||||||
|
ProjectionLevel.COMPACT -> activateFactor * 0.2
|
||||||
|
ProjectionLevel.ABSTRACT -> activateFactor * 0.1
|
||||||
|
ProjectionLevel.FULL -> 0.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
activationScore = min(activationCeiling(exposure, currentLevel), increasedScore)
|
||||||
return activationScore
|
return activationScore
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -202,11 +227,48 @@ data class ContextBlock @JvmOverloads constructor(
|
|||||||
return this.sourceKey == contextBlock.sourceKey
|
return this.sourceKey == contextBlock.sourceKey
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun render(exposure: Exposure): BlockContent {
|
||||||
|
return when (exposure) {
|
||||||
|
Exposure.PRIMARY -> when (currentProjectionLevel()) {
|
||||||
|
ProjectionLevel.FULL -> blockContent
|
||||||
|
ProjectionLevel.COMPACT, ProjectionLevel.ABSTRACT -> compactBlock
|
||||||
|
}
|
||||||
|
|
||||||
|
Exposure.SECONDARY -> when (currentProjectionLevel()) {
|
||||||
|
ProjectionLevel.ABSTRACT -> abstractBlock
|
||||||
|
ProjectionLevel.COMPACT, ProjectionLevel.FULL -> compactBlock
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun render(): BlockContent {
|
fun render(): BlockContent {
|
||||||
|
return when (currentProjectionLevel()) {
|
||||||
|
ProjectionLevel.ABSTRACT -> abstractBlock
|
||||||
|
ProjectionLevel.COMPACT -> compactBlock
|
||||||
|
ProjectionLevel.FULL -> blockContent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun currentProjectionLevel(): ProjectionLevel {
|
||||||
return when {
|
return when {
|
||||||
activationScore < 30 -> abstractBlock
|
activationScore < ABSTRACT_TO_COMPACT_THRESHOLD -> ProjectionLevel.ABSTRACT
|
||||||
activationScore < 70 -> compactBlock
|
activationScore < COMPACT_TO_FULL_THRESHOLD -> ProjectionLevel.COMPACT
|
||||||
else -> blockContent
|
else -> ProjectionLevel.FULL
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun activationCeiling(exposure: Exposure, currentLevel: ProjectionLevel): Double {
|
||||||
|
return when (exposure) {
|
||||||
|
Exposure.PRIMARY -> when (currentLevel) {
|
||||||
|
ProjectionLevel.ABSTRACT -> COMPACT_TO_FULL_THRESHOLD - PROJECTION_EPSILON
|
||||||
|
ProjectionLevel.COMPACT, ProjectionLevel.FULL -> MAX_ACTIVATION_SCORE
|
||||||
|
}
|
||||||
|
|
||||||
|
Exposure.SECONDARY -> when (currentLevel) {
|
||||||
|
ProjectionLevel.ABSTRACT -> ABSTRACT_TO_COMPACT_THRESHOLD - PROJECTION_EPSILON
|
||||||
|
ProjectionLevel.COMPACT -> COMPACT_TO_FULL_THRESHOLD - PROJECTION_EPSILON
|
||||||
|
ProjectionLevel.FULL -> MAX_ACTIVATION_SCORE
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -214,6 +276,13 @@ data class ContextBlock @JvmOverloads constructor(
|
|||||||
val blockName: String,
|
val blockName: String,
|
||||||
val source: String
|
val source: String
|
||||||
)
|
)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val MAX_ACTIVATION_SCORE = 100.0
|
||||||
|
private const val ABSTRACT_TO_COMPACT_THRESHOLD = 30.0
|
||||||
|
private const val COMPACT_TO_FULL_THRESHOLD = 70.0
|
||||||
|
private const val PROJECTION_EPSILON = 0.000001
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class AggregatedBlockContent(
|
private class AggregatedBlockContent(
|
||||||
@@ -221,11 +290,7 @@ private class AggregatedBlockContent(
|
|||||||
) : BlockContent(
|
) : BlockContent(
|
||||||
groupedBlocks.first().block.sourceKey.blockName,
|
groupedBlocks.first().block.sourceKey.blockName,
|
||||||
groupedBlocks.first().block.sourceKey.source,
|
groupedBlocks.first().block.sourceKey.source,
|
||||||
groupedBlocks.maxByOrNull {
|
groupedBlocks.maxByOrNull { it.renderedBlock.urgency.ordinal }?.renderedBlock?.urgency ?: Urgency.NORMAL
|
||||||
if (it.forceFullRender) it.block.blockContent.urgency.ordinal else it.block.render().urgency.ordinal
|
|
||||||
}?.let {
|
|
||||||
if (it.forceFullRender) it.block.blockContent.urgency else it.block.render().urgency
|
|
||||||
} ?: Urgency.NORMAL
|
|
||||||
) {
|
) {
|
||||||
|
|
||||||
override fun fillXml(document: Document, root: Element) {
|
override fun fillXml(document: Document, root: Element) {
|
||||||
@@ -238,11 +303,7 @@ private class AggregatedBlockContent(
|
|||||||
groupedBlocks.forEachIndexed { index, groupedBlock ->
|
groupedBlocks.forEachIndexed { index, groupedBlock ->
|
||||||
val tagName = if (index == snapshotIndex) "snapshot" else "history_snapshot"
|
val tagName = if (index == snapshotIndex) "snapshot" else "history_snapshot"
|
||||||
val wrapper = document.createElement(tagName)
|
val wrapper = document.createElement(tagName)
|
||||||
val renderedBlock = if (groupedBlock.forceFullRender) {
|
val renderedBlock = groupedBlock.renderedBlock
|
||||||
groupedBlock.block.blockContent
|
|
||||||
} else {
|
|
||||||
groupedBlock.block.render()
|
|
||||||
}
|
|
||||||
wrapper.setAttribute("source", renderedBlock.source)
|
wrapper.setAttribute("source", renderedBlock.source)
|
||||||
wrapper.setAttribute("urgency", renderedBlock.urgency.name.lowercase(Locale.ROOT))
|
wrapper.setAttribute("urgency", renderedBlock.urgency.name.lowercase(Locale.ROOT))
|
||||||
root.appendChild(wrapper)
|
root.appendChild(wrapper)
|
||||||
|
|||||||
@@ -64,13 +64,13 @@ class ContextWorkspaceTest {
|
|||||||
)
|
)
|
||||||
|
|
||||||
assertEquals(
|
assertEquals(
|
||||||
listOf("low", "mid", "high"),
|
listOf("low-compact", "mid-compact", "high"),
|
||||||
resolved.map { (it as TestBlockContent).content }
|
resolved.blocks.map { (it as TestBlockContent).content }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `resolve uses activation score only within same source`() {
|
fun `resolve aggregates same source blocks while preserving activation ordering within the group`() {
|
||||||
val manager = ContextWorkspace()
|
val manager = ContextWorkspace()
|
||||||
val older = contextBlock(
|
val older = contextBlock(
|
||||||
blockName = "memory",
|
blockName = "memory",
|
||||||
@@ -99,10 +99,14 @@ class ContextWorkspaceTest {
|
|||||||
|
|
||||||
val resolved = manager.resolve(listOf(ContextBlock.VisibleDomain.MEMORY))
|
val resolved = manager.resolve(listOf(ContextBlock.VisibleDomain.MEMORY))
|
||||||
|
|
||||||
assertEquals(
|
assertEquals(2, resolved.blocks.size)
|
||||||
listOf("older", "newer", "other-source"),
|
assertEquals("other-source", (resolved.blocks[1] as TestBlockContent).content)
|
||||||
resolved.map { (it as TestBlockContent).content }
|
|
||||||
)
|
val aggregatedXml = resolved.blocks[0].encodeToXmlString()
|
||||||
|
assertTrue(aggregatedXml.contains("<snapshot"))
|
||||||
|
assertTrue(aggregatedXml.contains("<content>newer</content>"))
|
||||||
|
assertTrue(aggregatedXml.contains("<history_snapshot"))
|
||||||
|
assertTrue(aggregatedXml.contains("<content>older</content>"))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -128,7 +132,7 @@ class ContextWorkspaceTest {
|
|||||||
|
|
||||||
val resolved = manager.resolve(listOf(ContextBlock.VisibleDomain.MEMORY))
|
val resolved = manager.resolve(listOf(ContextBlock.VisibleDomain.MEMORY))
|
||||||
|
|
||||||
assertEquals(listOf("replacement"), resolved.map { (it as TestBlockContent).content })
|
assertEquals(listOf("replacement"), resolved.blocks.map { (it as TestBlockContent).content })
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -158,7 +162,7 @@ class ContextWorkspaceTest {
|
|||||||
|
|
||||||
val resolved = manager.resolve(listOf(ContextBlock.VisibleDomain.MEMORY))
|
val resolved = manager.resolve(listOf(ContextBlock.VisibleDomain.MEMORY))
|
||||||
|
|
||||||
assertEquals(listOf("survivor"), resolved.map { (it as TestBlockContent).content })
|
assertEquals(listOf("survivor"), resolved.blocks.map { (it as TestBlockContent).content })
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -181,7 +185,7 @@ class ContextWorkspaceTest {
|
|||||||
|
|
||||||
val resolved = manager.resolve(listOf(ContextBlock.VisibleDomain.MEMORY))
|
val resolved = manager.resolve(listOf(ContextBlock.VisibleDomain.MEMORY))
|
||||||
|
|
||||||
assertEquals(listOf("restored"), resolved.map { (it as TestBlockContent).content })
|
assertEquals(listOf("restored"), resolved.blocks.map { (it as TestBlockContent).content })
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -215,6 +219,185 @@ class ContextWorkspaceTest {
|
|||||||
assertSame(summary, summaryBlock.render())
|
assertSame(summary, summaryBlock.render())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `primary exposure renders at least compact and can render full`() {
|
||||||
|
val lowActivationBlock = contextBlock(
|
||||||
|
blockName = "memory",
|
||||||
|
source = "main",
|
||||||
|
content = "full",
|
||||||
|
compactContent = "compact",
|
||||||
|
abstractContent = "abstract",
|
||||||
|
replaceFadeFactor = 80.0
|
||||||
|
)
|
||||||
|
lowActivationBlock.applyReplaceFade()
|
||||||
|
|
||||||
|
val highActivationBlock = contextBlock(
|
||||||
|
blockName = "memory",
|
||||||
|
source = "main",
|
||||||
|
content = "full",
|
||||||
|
compactContent = "compact",
|
||||||
|
abstractContent = "abstract"
|
||||||
|
)
|
||||||
|
|
||||||
|
assertContent("compact", lowActivationBlock.render(ContextBlock.Exposure.PRIMARY))
|
||||||
|
assertContent("full", highActivationBlock.render(ContextBlock.Exposure.PRIMARY))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `secondary exposure never renders full`() {
|
||||||
|
val block = contextBlock(
|
||||||
|
blockName = "memory",
|
||||||
|
source = "main",
|
||||||
|
content = "full",
|
||||||
|
compactContent = "compact",
|
||||||
|
abstractContent = "abstract"
|
||||||
|
)
|
||||||
|
|
||||||
|
assertContent("compact", block.render(ContextBlock.Exposure.SECONDARY))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `primary exposure promotes abstract only to compact in one activation`() {
|
||||||
|
val block = contextBlock(
|
||||||
|
blockName = "memory",
|
||||||
|
source = "main",
|
||||||
|
content = "full",
|
||||||
|
compactContent = "compact",
|
||||||
|
abstractContent = "abstract",
|
||||||
|
replaceFadeFactor = 80.0,
|
||||||
|
activateFactor = 50.0
|
||||||
|
)
|
||||||
|
block.applyReplaceFade()
|
||||||
|
|
||||||
|
block.activate(ContextBlock.Exposure.PRIMARY)
|
||||||
|
|
||||||
|
assertContent("compact", block.render())
|
||||||
|
assertContent("compact", block.render(ContextBlock.Exposure.PRIMARY))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `primary exposure promotes compact to full`() {
|
||||||
|
val block = contextBlock(
|
||||||
|
blockName = "memory",
|
||||||
|
source = "main",
|
||||||
|
content = "full",
|
||||||
|
compactContent = "compact",
|
||||||
|
abstractContent = "abstract",
|
||||||
|
replaceFadeFactor = 40.0,
|
||||||
|
activateFactor = 20.0
|
||||||
|
)
|
||||||
|
block.applyReplaceFade()
|
||||||
|
|
||||||
|
block.activate(ContextBlock.Exposure.PRIMARY)
|
||||||
|
|
||||||
|
assertContent("full", block.render())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `secondary exposure keeps abstract within abstract tier`() {
|
||||||
|
val block = contextBlock(
|
||||||
|
blockName = "memory",
|
||||||
|
source = "main",
|
||||||
|
content = "full",
|
||||||
|
compactContent = "compact",
|
||||||
|
abstractContent = "abstract",
|
||||||
|
replaceFadeFactor = 80.0,
|
||||||
|
activateFactor = 200.0
|
||||||
|
)
|
||||||
|
block.applyReplaceFade()
|
||||||
|
|
||||||
|
block.activate(ContextBlock.Exposure.SECONDARY)
|
||||||
|
|
||||||
|
assertContent("abstract", block.render())
|
||||||
|
assertContent("abstract", block.render(ContextBlock.Exposure.SECONDARY))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `secondary exposure keeps compact within compact tier`() {
|
||||||
|
val block = contextBlock(
|
||||||
|
blockName = "memory",
|
||||||
|
source = "main",
|
||||||
|
content = "full",
|
||||||
|
compactContent = "compact",
|
||||||
|
abstractContent = "abstract",
|
||||||
|
replaceFadeFactor = 40.0,
|
||||||
|
activateFactor = 200.0
|
||||||
|
)
|
||||||
|
block.applyReplaceFade()
|
||||||
|
|
||||||
|
block.activate(ContextBlock.Exposure.SECONDARY)
|
||||||
|
|
||||||
|
assertContent("compact", block.render())
|
||||||
|
assertContent("compact", block.render(ContextBlock.Exposure.SECONDARY))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `resolve uses exposure-specific rendering for primary and secondary domains`() {
|
||||||
|
val manager = ContextWorkspace()
|
||||||
|
val block = contextBlock(
|
||||||
|
blockName = "memory",
|
||||||
|
source = "main",
|
||||||
|
content = "full",
|
||||||
|
compactContent = "compact",
|
||||||
|
abstractContent = "abstract",
|
||||||
|
visibleTo = setOf(ContextBlock.VisibleDomain.MEMORY, ContextBlock.VisibleDomain.ACTION),
|
||||||
|
replaceFadeFactor = 80.0
|
||||||
|
)
|
||||||
|
block.applyReplaceFade()
|
||||||
|
manager.register(block)
|
||||||
|
|
||||||
|
val primaryResolved = manager.resolve(
|
||||||
|
listOf(ContextBlock.VisibleDomain.MEMORY, ContextBlock.VisibleDomain.ACTION)
|
||||||
|
)
|
||||||
|
val secondaryResolved = manager.resolve(
|
||||||
|
listOf(ContextBlock.VisibleDomain.PERCEIVE, ContextBlock.VisibleDomain.ACTION)
|
||||||
|
)
|
||||||
|
|
||||||
|
assertEquals(listOf("compact"), primaryResolved.blocks.map { (it as TestBlockContent).content })
|
||||||
|
assertEquals(listOf("abstract"), secondaryResolved.blocks.map { (it as TestBlockContent).content })
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `aggregated blocks use rendered projection for urgency and snapshot`() {
|
||||||
|
val manager = ContextWorkspace()
|
||||||
|
manager.register(
|
||||||
|
ContextBlock(
|
||||||
|
blockContent = TestBlockContent("memory", "main", "full-critical", BlockContent.Urgency.CRITICAL),
|
||||||
|
compactBlock = TestBlockContent("memory", "main", "compact-low", BlockContent.Urgency.LOW),
|
||||||
|
abstractBlock = TestBlockContent("memory", "main", "abstract-low", BlockContent.Urgency.LOW),
|
||||||
|
visibleTo = setOf(ContextBlock.VisibleDomain.ACTION),
|
||||||
|
replaceFadeFactor = 20.0,
|
||||||
|
timeFadeFactor = 0.0,
|
||||||
|
activateFactor = 0.0
|
||||||
|
)
|
||||||
|
)
|
||||||
|
manager.register(
|
||||||
|
ContextBlock(
|
||||||
|
blockContent = TestBlockContent("memory", "main", "full-normal", BlockContent.Urgency.NORMAL),
|
||||||
|
compactBlock = TestBlockContent("memory", "main", "compact-normal", BlockContent.Urgency.NORMAL),
|
||||||
|
abstractBlock = TestBlockContent("memory", "main", "abstract-normal", BlockContent.Urgency.NORMAL),
|
||||||
|
visibleTo = setOf(ContextBlock.VisibleDomain.ACTION),
|
||||||
|
replaceFadeFactor = 80.0,
|
||||||
|
timeFadeFactor = 0.0,
|
||||||
|
activateFactor = 0.0
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
val resolved = manager.resolve(
|
||||||
|
listOf(ContextBlock.VisibleDomain.PERCEIVE, ContextBlock.VisibleDomain.ACTION)
|
||||||
|
)
|
||||||
|
|
||||||
|
val aggregated = resolved.blocks.single()
|
||||||
|
val xml = aggregated.encodeToXmlString()
|
||||||
|
assertEquals(BlockContent.Urgency.NORMAL, aggregated.urgency)
|
||||||
|
assertTrue(xml.contains("<content>compact-low</content>"))
|
||||||
|
assertFalse(xml.contains("<content>full-critical</content>"))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun assertContent(expected: String, rendered: BlockContent) {
|
||||||
|
assertEquals(expected, (rendered as TestBlockContent).content)
|
||||||
|
}
|
||||||
|
|
||||||
private fun contextBlock(
|
private fun contextBlock(
|
||||||
blockName: String,
|
blockName: String,
|
||||||
source: String,
|
source: String,
|
||||||
@@ -240,8 +423,9 @@ class ContextWorkspaceTest {
|
|||||||
private class TestBlockContent(
|
private class TestBlockContent(
|
||||||
blockName: String,
|
blockName: String,
|
||||||
source: String,
|
source: String,
|
||||||
val content: String
|
val content: String,
|
||||||
) : BlockContent(blockName, source) {
|
urgency: Urgency = Urgency.NORMAL
|
||||||
|
) : BlockContent(blockName, source, urgency) {
|
||||||
override fun fillXml(document: org.w3c.dom.Document, root: org.w3c.dom.Element) {
|
override fun fillXml(document: org.w3c.dom.Document, root: org.w3c.dom.Element) {
|
||||||
appendTextElement(document, root, "content", content)
|
appendTextElement(document, root, "content", content)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user