Skip to content

Commit 0c39c45

Browse files
committed
feat(主页): 新语法 - 随机文本块
1 parent 5f58e5b commit 0c39c45

3 files changed

Lines changed: 146 additions & 4 deletions

File tree

ZalithLauncher/src/main/assets/home_page/doc_page_en.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,27 @@ In addition to standard Markdown, you can also use the following extension compo
1515

1616
---
1717

18+
### Random Text Block
19+
Place multiple text segments within a block. When the homepage loads, one of the segments will be randomly displayed.
20+
21+
**Syntax**
22+
...random-start
23+
This is a piece of text, weight defaults to 1.0
24+
weight(2.0): This is the second piece of text, weight is set to 2.0
25+
This is the third piece of text
26+
...random-end
27+
28+
**Parameter description:**
29+
- Unlike other components, this component uses inline parameters, embedded directly at the beginning of the actual value line.
30+
- `weight`:
31+
- Specifies the weight value for this piece of text. Supports integers and decimals. Optional.
32+
- The homepage will randomly select a piece of text according to the weights.
33+
34+
> This component is only supported within standard Markdown.
35+
> This component will NOT take effect inside standard Markdown containers or other extension components.
36+
37+
---
38+
1839
### Card Component
1940
Used to wrap content inside a container with a background and rounded corners.
2041

ZalithLauncher/src/main/assets/home_page/doc_page_zh.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,27 @@
1515

1616
---
1717

18+
### 随机文本块
19+
在一个区块内填入多段文本,主页在加载时,将随机显示其中的一段文本
20+
21+
**语法**
22+
...random-start
23+
这是一段文本,权重默认为 1.0
24+
weight(2.0): 这是第二段文本,权重被配置为 2.0
25+
这是第三段文本
26+
...random-end
27+
28+
**参数说明:**
29+
- 与其他组件不同,该组件的参数为内嵌参数,直接嵌入到实际值行首
30+
- `weight`:
31+
- 可填写该段文本的具体的权重值,支持整数、小数,可选
32+
- 主页将按权重随机抽取文本
33+
34+
> 该组件仅支持在基础的 Markdown 内使用
35+
> 该组件不会生效于标准 Markdown 容器或其他扩展组件内
36+
37+
---
38+
1839
### 卡片组件
1940
用于将内容包裹在一个有背景和圆角的容器中
2041

ZalithLauncher/src/main/java/com/movtery/zalithlauncher/ui/screens/main/custom_home/CustomHome.kt

Lines changed: 104 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ import com.halilibo.richtext.ui.RichTextStyle
4040
import com.movtery.zalithlauncher.ui.components.MarkdownView
4141
import com.movtery.zalithlauncher.ui.components.defaultRichTextStyle
4242
import com.movtery.zalithlauncher.ui.theme.itemColor
43+
import kotlin.random.Random
4344

4445
fun LazyListScope.customHomePage(
4546
blocks: List<MarkdownBlock>,
@@ -399,17 +400,19 @@ private fun parseMarkdownBlocksInternal(
399400
if (match == null) {
400401
//没有更多匹配,添加剩余内容
401402
val remaining = cleared.substring(lastIndex).trim('\n')
402-
if (remaining.isNotEmpty()) {
403-
blocks.add(MarkdownBlock.Normal(astNode = parseMarkdown(remaining)))
403+
val processedRemaining = processRandomBlocksInText(remaining)
404+
if (processedRemaining.isNotEmpty()) {
405+
blocks.add(MarkdownBlock.Normal(astNode = parseMarkdown(processedRemaining)))
404406
}
405407
break
406408
}
407409

408410
//处理匹配项之前的普通文本
409411
if (match.range.first > lastIndex) {
410412
val text = cleared.substring(lastIndex, match.range.first).trim('\n')
411-
if (text.isNotEmpty()) {
412-
blocks.add(MarkdownBlock.Normal(astNode = parseMarkdown(text)))
413+
val processedText = processRandomBlocksInText(text)
414+
if (processedText.isNotEmpty()) {
415+
blocks.add(MarkdownBlock.Normal(astNode = parseMarkdown(processedText)))
413416
}
414417
}
415418

@@ -793,4 +796,101 @@ private fun parseWeight(params: String): Pair<Float, Boolean>? {
793796
val weight = match.groupValues[1].toFloatOrNull() ?: return null
794797
val fill = match.groupValues[2] != "noFill"
795798
return Pair(weight, fill)
799+
}
800+
801+
private const val randomStartTag = "...random-start"
802+
private const val randomEndTag = "...random-end"
803+
private val randomOptionRegex = Regex("""^weight\((\d+(?:\.\d+)?)\)\s*:\s*(.*)$""")
804+
805+
private data class RandomOption(
806+
val text: String,
807+
val weight: Double
808+
)
809+
810+
/**
811+
* 处理文本中的随机文本块,自动将其替换为随机选中的文本
812+
* 语法:
813+
* ```
814+
* ...random-start
815+
* weight(N): 文本内容
816+
* 默认权重的文本内容
817+
* ...random-end
818+
* ```
819+
* - 以 `weight(N): ` 开头可指定权重,否则权重默认为 1
820+
* - 随机抽取后的结果中不包含换行符
821+
*/
822+
private fun processRandomBlocksInText(text: String): String {
823+
if (!text.contains(randomStartTag)) return text
824+
825+
val lines = text.lines()
826+
val result = mutableListOf<String>()
827+
var inRandomBlock = false
828+
var randomStartLine: String? = null
829+
val randomLines = mutableListOf<String>()
830+
831+
for (line in lines) {
832+
val trimmed = line.trimStart()
833+
834+
if (inRandomBlock) {
835+
if (trimmed.startsWith(randomEndTag)) {
836+
val selected = pickRandomText(randomLines)
837+
result.add(selected)
838+
randomLines.clear()
839+
inRandomBlock = false
840+
randomStartLine = null
841+
} else {
842+
randomLines.add(line)
843+
}
844+
} else {
845+
if (trimmed.startsWith(randomStartTag)) {
846+
inRandomBlock = true
847+
randomStartLine = line
848+
} else {
849+
result.add(line)
850+
}
851+
}
852+
}
853+
854+
//如果文本结束但随机块未闭合,则忽略此随机文本块
855+
if (inRandomBlock) {
856+
randomStartLine?.let { result.add(it) }
857+
result.addAll(randomLines)
858+
}
859+
860+
return result.joinToString("\n")
861+
}
862+
863+
/**
864+
* 从随机文本候选项中根据权重抽取一个文本
865+
*/
866+
private fun pickRandomText(lines: List<String>): String {
867+
val options = lines.mapNotNull { line ->
868+
val trimmed = line.trim()
869+
if (trimmed.isEmpty()) return@mapNotNull null
870+
871+
//读取权重值
872+
val match = randomOptionRegex.find(trimmed)
873+
if (match != null) {
874+
val weight = match.groupValues[1].toDoubleOrNull() ?: 1.0
875+
val text = match.groupValues[2]
876+
RandomOption(text, weight)
877+
} else {
878+
//未配置权重时,默认为 1
879+
RandomOption(trimmed, 1.0)
880+
}
881+
}
882+
883+
if (options.isEmpty()) return ""
884+
885+
val totalWeight = options.sumOf { it.weight }
886+
var randomValue = Random.nextDouble() * totalWeight
887+
888+
for (option in options) {
889+
randomValue -= option.weight
890+
if (randomValue <= 0) {
891+
return option.text
892+
}
893+
}
894+
895+
return options.last().text
796896
}

0 commit comments

Comments
 (0)