From 39ba3d07727ba04349896df2538c569442e73bd5 Mon Sep 17 00:00:00 2001 From: Haitao Pan Date: Sun, 28 Jun 2026 10:55:29 +0800 Subject: [PATCH] fix(artifacts): prioritize PDF deliverables in sidebar --- .../desktop_thread_artifact_service.dart | 24 +++++++++++ .../desktop_thread_artifact_service_test.dart | 43 +++++++++++++++++++ 2 files changed, 67 insertions(+) diff --git a/lib/runtime/desktop_thread_artifact_service.dart b/lib/runtime/desktop_thread_artifact_service.dart index 0cbdadf0..9aa1ec69 100644 --- a/lib/runtime/desktop_thread_artifact_service.dart +++ b/lib/runtime/desktop_thread_artifact_service.dart @@ -270,6 +270,20 @@ class DesktopThreadArtifactService { } } entries.sort((a, b) { + final deliveryCompare = artifactDisplayPriorityInternal( + a.relativePath, + ).compareTo(artifactDisplayPriorityInternal(b.relativePath)); + if (deliveryCompare != 0) { + return deliveryCompare; + } + if (fileExtensionInternal(a.relativePath) == 'pdf') { + final depthCompare = artifactPathDepthInternal( + a.relativePath, + ).compareTo(artifactPathDepthInternal(b.relativePath)); + if (depthCompare != 0) { + return depthCompare; + } + } final updatedCompare = (b.updatedAtMs ?? 0).compareTo(a.updatedAtMs ?? 0); if (updatedCompare != 0) { return updatedCompare; @@ -279,6 +293,16 @@ class DesktopThreadArtifactService { return entries; } + static int artifactDisplayPriorityInternal(String relativePath) { + return fileExtensionInternal(relativePath) == 'pdf' ? 0 : 1; + } + + static int artifactPathDepthInternal(String relativePath) { + return normalizeArtifactPathInternal( + relativePath, + ).split('/').where((segment) => segment.isNotEmpty).length; + } + Future> buildResultEntriesInternal({ required List changes, required List fileEntries, diff --git a/test/runtime/desktop_thread_artifact_service_test.dart b/test/runtime/desktop_thread_artifact_service_test.dart index 15f23f3a..d28303cf 100644 --- a/test/runtime/desktop_thread_artifact_service_test.dart +++ b/test/runtime/desktop_thread_artifact_service_test.dart @@ -65,6 +65,49 @@ void main() { }, ); + test( + 'loadSnapshot shows the root PDF deliverable before supporting files', + () async { + final workspace = await Directory.systemTemp.createTemp( + 'xworkmate-pdf-artifact-order-', + ); + addTearDown(() async { + if (await workspace.exists()) { + await workspace.delete(recursive: true); + } + }); + await Directory( + '${workspace.path}/assets/diagrams', + ).create(recursive: true); + await File( + '${workspace.path}/assets/diagrams/chapter.png', + ).writeAsBytes([1, 2, 3]); + await File( + '${workspace.path}/assets/安全架构演进白皮书.pdf', + ).writeAsBytes([4, 5, 6]); + await File( + '${workspace.path}/安全架构演进白皮书.pdf', + ).writeAsBytes([4, 5, 6]); + + final snapshot = await DesktopThreadArtifactService().loadSnapshot( + workspacePath: workspace.path, + workspaceKind: WorkspaceRefKind.localPath, + artifactRelativePaths: const [ + 'assets/diagrams/chapter.png', + 'assets/安全架构演进白皮书.pdf', + '安全架构演进白皮书.pdf', + ], + ); + + expect(snapshot.fileEntries.map((entry) => entry.relativePath), [ + '安全架构演进白皮书.pdf', + 'assets/安全架构演进白皮书.pdf', + 'assets/diagrams/chapter.png', + ]); + expect(snapshot.resultEntries.first.mimeType, 'application/pdf'); + }, + ); + test( 'loadPreview rejects historical files outside current task artifacts', () async {