diff --git a/CHANGELOG.md b/CHANGELOG.md
index a627a79..5ce2929 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,14 @@ with the pre-1.0 rules described in [docs/VERSIONING.md](docs/VERSIONING.md).
## [Unreleased]
+## [0.3.12] - 2026-10-17
+
+### Fixed
+
+- The log's time column held a full date in 72dp and wrapped on every line; both ends now
+ print the clock, one line, as the prototype does.
+- 同步's 策略 panel printed its summary twice.
+
## [0.3.11] - 2026-10-17
### Added
diff --git a/app/android/src/main/kotlin/io/github/cubsync/android/JobsScreen.kt b/app/android/src/main/kotlin/io/github/cubsync/android/JobsScreen.kt
index 7922c7b..90995ff 100644
--- a/app/android/src/main/kotlin/io/github/cubsync/android/JobsScreen.kt
+++ b/app/android/src/main/kotlin/io/github/cubsync/android/JobsScreen.kt
@@ -166,7 +166,7 @@ fun JobDetailScreen(state: AndroidAppState, jobId: String) {
}
for (entry in detail.logs.takeLast(40)) {
Row(Modifier.fillMaxWidth(), verticalAlignment = Alignment.Top) {
- Text(Format.stamp(entry.atMs), style = CubType.mono, color = colors.textFaint, modifier = Modifier.width(68.dp))
+ Text(Format.clockTime(entry.atMs), style = CubType.mono, color = colors.textFaint, maxLines = 1, modifier = Modifier.width(58.dp))
Text(entry.area, style = CubType.mono, color = levelColor(entry.level), modifier = Modifier.width(50.dp))
Text(entry.message, style = CubType.mono, color = colors.textDim, modifier = Modifier.weight(1f))
}
@@ -279,7 +279,7 @@ fun LogsBody(state: AndroidAppState, modifier: Modifier = Modifier) {
Column(Modifier.verticalScroll(rememberScrollState())) {
for (entry in shown) {
Row(Modifier.fillMaxWidth(), verticalAlignment = Alignment.Top) {
- Text(Format.stamp(entry.atMs), style = CubType.mono, color = colors.textFaint, modifier = Modifier.width(66.dp))
+ Text(Format.clockTime(entry.atMs), style = CubType.mono, color = colors.textFaint, maxLines = 1, modifier = Modifier.width(58.dp))
Text(Format.level(entry.level), style = CubType.mono, color = levelColor(entry.level), modifier = Modifier.width(40.dp))
Column(Modifier.weight(1f)) {
Text(entry.message, style = CubType.mono, color = colors.text)
diff --git a/app/desktop/src/main/kotlin/io/github/cubsync/desktop/ui/Components.kt b/app/desktop/src/main/kotlin/io/github/cubsync/desktop/ui/Components.kt
index 91c7491..712a573 100644
--- a/app/desktop/src/main/kotlin/io/github/cubsync/desktop/ui/Components.kt
+++ b/app/desktop/src/main/kotlin/io/github/cubsync/desktop/ui/Components.kt
@@ -286,6 +286,10 @@ fun KeyValueRow(key: String, value: String, dim: Boolean = false) {
value.ifEmpty { "—" },
style = CubType.body,
color = if (dim) colors.textDim else colors.text,
+ // A path in a 300dp panel is longer than the panel. One line and an ellipsis
+ // keeps the row a row; the full value is a copy button away.
+ maxLines = 1,
+ overflow = androidx.compose.ui.text.style.TextOverflow.Ellipsis,
modifier = Modifier.weight(1f),
)
}
diff --git a/app/desktop/src/main/kotlin/io/github/cubsync/desktop/ui/JobsScreen.kt b/app/desktop/src/main/kotlin/io/github/cubsync/desktop/ui/JobsScreen.kt
index 1e59f75..c6c538a 100644
--- a/app/desktop/src/main/kotlin/io/github/cubsync/desktop/ui/JobsScreen.kt
+++ b/app/desktop/src/main/kotlin/io/github/cubsync/desktop/ui/JobsScreen.kt
@@ -364,7 +364,16 @@ fun LogsBody(state: AppState, modifier: Modifier = Modifier) {
Column(Modifier.fillMaxSize().verticalScroll(rememberScrollState())) {
for (entry in shown) {
Row(Modifier.fillMaxWidth().padding(vertical = 1.dp), verticalAlignment = Alignment.Top) {
- Text(Format.stamp(entry.atMs), style = CubType.monoDim, color = colors.textFaint, modifier = Modifier.width(72.dp))
+ // The prototype prints the clock, not the date: a log page is read
+ // in one sitting, and a wrapped date column is what a 72dp box does
+ // to "10-17 13:25:33".
+ Text(
+ Format.clockTime(entry.atMs),
+ style = CubType.monoDim,
+ color = colors.textFaint,
+ maxLines = 1,
+ modifier = Modifier.width(58.dp),
+ )
Text(Format.level(entry.level), style = CubType.monoDim, color = levelColor(entry.level), modifier = Modifier.width(44.dp))
Text(entry.area, style = CubType.monoDim, color = colors.textDim, modifier = Modifier.width(56.dp))
Column(Modifier.weight(1f)) {
diff --git a/app/desktop/src/main/kotlin/io/github/cubsync/desktop/ui/SettingsScreen.kt b/app/desktop/src/main/kotlin/io/github/cubsync/desktop/ui/SettingsScreen.kt
index a8ae318..8e82f52 100644
--- a/app/desktop/src/main/kotlin/io/github/cubsync/desktop/ui/SettingsScreen.kt
+++ b/app/desktop/src/main/kotlin/io/github/cubsync/desktop/ui/SettingsScreen.kt
@@ -159,7 +159,7 @@ fun SettingsScreen(state: AppState) {
Spacer(Modifier.height(6.dp))
Text(
- "行密度(${Density.MIN}–${Density.MAX}%)两栏都生效,两栏同时生效;" +
+ "行密度(${Density.MIN}–${Density.MAX}%)两栏同时生效;" +
"排序 / 文件夹优先 / 作用范围在传输页每栏的 ⋯ 里。",
style = CubType.meta,
color = colors.textFaint,
@@ -253,7 +253,6 @@ private fun AboutPanel(state: AppState) {
)
Spacer(Modifier.height(6.dp))
Text("作用范围越具体越优先:${Scope.entries.joinToString(" > ") { it.label }} > 出厂默认。", style = CubType.meta, color = colors.textFaint)
- Text("两个目录的命名口径:${Words.LOCAL} / ${Words.REMOTE},${Words.TRASH}不叫回收站。", style = CubType.meta, color = colors.textFaint)
}
}
diff --git a/app/desktop/src/main/kotlin/io/github/cubsync/desktop/ui/SyncScreen.kt b/app/desktop/src/main/kotlin/io/github/cubsync/desktop/ui/SyncScreen.kt
index dbb2864..db50145 100644
--- a/app/desktop/src/main/kotlin/io/github/cubsync/desktop/ui/SyncScreen.kt
+++ b/app/desktop/src/main/kotlin/io/github/cubsync/desktop/ui/SyncScreen.kt
@@ -174,8 +174,7 @@ private fun ConfigDetail(state: AppState, config: SyncConfig) {
/** 策略: direction, comparison, conflict, deletion, parallelism, and the derived summary. */
@Composable
private fun PolicyPanel(state: AppState, draft: SyncConfig, onChange: (SyncConfig) -> Unit) {
- val colors = CubTheme.colors
- Panel(Modifier.fillMaxWidth(), title = "策略", subtitle = policySentence(draft)) {
+ Panel(Modifier.fillMaxWidth(), title = "策略") {
SettingRow("同步方向", Format.direction(draft.direction)) {
Segmented(
options = SyncDirection.entries.toList(),
@@ -216,8 +215,6 @@ private fun PolicyPanel(state: AppState, draft: SyncConfig, onChange: (SyncConfi
onSelect = { onChange(draft.copy(parallelism = it)) },
)
}
- Spacer(Modifier.height(6.dp))
- Text(policySentence(draft), style = CubType.meta, color = colors.textFaint)
}
}
diff --git a/docs/releases/v0.3.12.md b/docs/releases/v0.3.12.md
new file mode 100644
index 0000000..c8a12e2
--- /dev/null
+++ b/docs/releases/v0.3.12.md
@@ -0,0 +1,27 @@
+
+中文说明
+
+两个看得见的排版问题。
+
+**日志的时间列不再折行。** 之前那一列塞的是 `10-17 13:25:33`,宽 72dp 放不下,每条日志的时间都断成
+两行,整页跟着高低不齐。现在两端都只印时刻(`13:25:33`),单行——原型里也是这么印的。
+
+**同步页的「策略」不再把同一句话印两遍。** 面板标题旁的摘要和面板底下那行原本是同一句
+(「双向同步;删除会同步,被删掉的文件进回收站;冲突时停下来等人决定。」)。现在只留上面那处。
+
+
+
+
+English
+
+Two layout defects that were visible.
+
+**The log's time column no longer wraps.** It held `10-17 13:25:33` in 72dp, so every line's
+time broke in two and the page came out uneven. Both ends now print the clock alone
+(`13:25:33`), one line — which is what the prototype prints.
+
+**同步's 策略 panel no longer prints the same sentence twice.** The summary beside the
+panel title and the line under it were the same text (双向同步;删除会同步…;冲突时停下来
+等人决定。). Only the first one stays.
+
+
diff --git a/gradle.properties b/gradle.properties
index 91eecf4..4e360b8 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -3,7 +3,7 @@
# The project version lives here so that build scripts, packaging and CI all read
# a single value. See docs/VERSIONING.md for the versioning policy.
-cubsync.version=0.3.11
+cubsync.version=0.3.12
org.gradle.jvmargs=-Xmx3g -XX:MaxMetaspaceSize=768m -Dfile.encoding=UTF-8
org.gradle.parallel=true