fix: reveal artifact files without blocking (#24)
Co-authored-by: Haitao Pan <haitao.pan@xworkmate.ai>
This commit is contained in:
parent
d048d7ec9c
commit
b1ec29fa36
@ -17,6 +17,7 @@ import '../../app/ui_feature_manifest.dart';
|
||||
import '../../i18n/app_language.dart';
|
||||
import '../../models/app_models.dart';
|
||||
import '../../runtime/gateway_acp_client.dart';
|
||||
import '../../runtime/local_file_revealer.dart';
|
||||
import '../../runtime/runtime_models.dart';
|
||||
import '../../theme/app_palette.dart';
|
||||
import '../../theme/app_theme.dart';
|
||||
@ -404,21 +405,7 @@ extension AssistantPageStateClosureInternal on AssistantPageStateInternal {
|
||||
entry.relativePath.contains(':\\')
|
||||
? entry.relativePath
|
||||
: '${workspacePath.replaceAll(RegExp(r'[\\/]+$'), '')}${Platform.pathSeparator}${entry.relativePath}';
|
||||
if (Platform.isMacOS) {
|
||||
await Process.run('open', <String>['-R', targetPath]);
|
||||
return;
|
||||
}
|
||||
if (Platform.isLinux) {
|
||||
await Process.run('xdg-open', <String>[
|
||||
File(targetPath).parent.path,
|
||||
]);
|
||||
return;
|
||||
}
|
||||
if (Platform.isWindows) {
|
||||
await Process.run('explorer.exe', <String>[
|
||||
'/select,$targetPath',
|
||||
]);
|
||||
}
|
||||
await revealLocalFile(targetPath);
|
||||
},
|
||||
loadSnapshot: () => controller.loadAssistantArtifactSnapshot(
|
||||
sessionKey: activeSessionKey,
|
||||
|
||||
39
lib/runtime/local_file_revealer.dart
Normal file
39
lib/runtime/local_file_revealer.dart
Normal file
@ -0,0 +1,39 @@
|
||||
import 'dart:io';
|
||||
|
||||
typedef DetachedProcessLauncher =
|
||||
Future<void> Function(
|
||||
String executable,
|
||||
List<String> arguments, {
|
||||
required ProcessStartMode mode,
|
||||
});
|
||||
|
||||
Future<void> revealLocalFile(
|
||||
String targetPath, {
|
||||
String? operatingSystem,
|
||||
DetachedProcessLauncher? launchDetached,
|
||||
}) async {
|
||||
final launcher = launchDetached ?? _launchDetached;
|
||||
switch (operatingSystem ?? Platform.operatingSystem) {
|
||||
case 'macos':
|
||||
await launcher('open', <String>[
|
||||
'-R',
|
||||
targetPath,
|
||||
], mode: ProcessStartMode.detached);
|
||||
case 'linux':
|
||||
await launcher('xdg-open', <String>[
|
||||
File(targetPath).parent.path,
|
||||
], mode: ProcessStartMode.detached);
|
||||
case 'windows':
|
||||
await launcher('explorer.exe', <String>[
|
||||
'/select,$targetPath',
|
||||
], mode: ProcessStartMode.detached);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _launchDetached(
|
||||
String executable,
|
||||
List<String> arguments, {
|
||||
required ProcessStartMode mode,
|
||||
}) async {
|
||||
await Process.start(executable, arguments, mode: mode);
|
||||
}
|
||||
64
test/runtime/local_file_revealer_test.dart
Normal file
64
test/runtime/local_file_revealer_test.dart
Normal file
@ -0,0 +1,64 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:xworkmate/runtime/local_file_revealer.dart';
|
||||
|
||||
void main() {
|
||||
group('revealLocalFile', () {
|
||||
test('reveals the file in Finder on macOS', () async {
|
||||
final invocation = await _captureInvocation(
|
||||
operatingSystem: 'macos',
|
||||
targetPath: '/tmp/thread/report.pdf',
|
||||
);
|
||||
|
||||
expect(invocation.executable, 'open');
|
||||
expect(invocation.arguments, <String>['-R', '/tmp/thread/report.pdf']);
|
||||
expect(invocation.mode, ProcessStartMode.detached);
|
||||
});
|
||||
|
||||
test('opens the parent directory on Linux', () async {
|
||||
final invocation = await _captureInvocation(
|
||||
operatingSystem: 'linux',
|
||||
targetPath: '/tmp/thread/reports/report.pdf',
|
||||
);
|
||||
|
||||
expect(invocation.executable, 'xdg-open');
|
||||
expect(invocation.arguments, <String>['/tmp/thread/reports']);
|
||||
expect(invocation.mode, ProcessStartMode.detached);
|
||||
});
|
||||
|
||||
test('selects the file in Explorer on Windows', () async {
|
||||
final invocation = await _captureInvocation(
|
||||
operatingSystem: 'windows',
|
||||
targetPath: r'C:\thread\report.pdf',
|
||||
);
|
||||
|
||||
expect(invocation.executable, 'explorer.exe');
|
||||
expect(invocation.arguments, <String>[r'/select,C:\thread\report.pdf']);
|
||||
expect(invocation.mode, ProcessStartMode.detached);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Future<_ProcessInvocation> _captureInvocation({
|
||||
required String operatingSystem,
|
||||
required String targetPath,
|
||||
}) async {
|
||||
late _ProcessInvocation invocation;
|
||||
await revealLocalFile(
|
||||
targetPath,
|
||||
operatingSystem: operatingSystem,
|
||||
launchDetached: (executable, arguments, {required mode}) async {
|
||||
invocation = _ProcessInvocation(executable, arguments, mode);
|
||||
},
|
||||
);
|
||||
return invocation;
|
||||
}
|
||||
|
||||
class _ProcessInvocation {
|
||||
const _ProcessInvocation(this.executable, this.arguments, this.mode);
|
||||
|
||||
final String executable;
|
||||
final List<String> arguments;
|
||||
final ProcessStartMode mode;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user